1
This commit is contained in:
@@ -41,6 +41,7 @@ use module_combat::{
|
||||
use module_custom_world::{
|
||||
CustomWorldAgentActionExecuteInput, CustomWorldAgentActionExecuteResult,
|
||||
CustomWorldAgentCardDetailGetInput, CustomWorldAgentMessageSnapshot,
|
||||
CustomWorldAgentMessageFinalizeInput,
|
||||
CustomWorldAgentMessageSubmitInput, CustomWorldAgentOperationGetInput,
|
||||
CustomWorldAgentOperationProcedureResult, CustomWorldAgentOperationSnapshot,
|
||||
CustomWorldAgentSessionCreateInput, CustomWorldAgentSessionGetInput,
|
||||
@@ -61,7 +62,9 @@ use module_custom_world::{
|
||||
build_custom_world_published_profile_compile_snapshot,
|
||||
validate_custom_world_agent_action_execute_input,
|
||||
validate_custom_world_agent_card_detail_get_input,
|
||||
validate_custom_world_agent_message_finalize_input,
|
||||
validate_custom_world_agent_message_submit_input,
|
||||
validate_custom_world_agent_operation_fields,
|
||||
validate_custom_world_agent_operation_get_input,
|
||||
validate_custom_world_agent_session_create_input,
|
||||
validate_custom_world_agent_session_get_input, validate_custom_world_gallery_detail_input,
|
||||
@@ -878,6 +881,7 @@ pub struct CustomWorldAgentMessage {
|
||||
created_at: Timestamp,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
#[spacetimedb::table(
|
||||
accessor = custom_world_agent_operation,
|
||||
index(accessor = by_custom_world_agent_operation_session_id, btree(columns = [session_id]))
|
||||
@@ -1809,6 +1813,25 @@ pub fn submit_custom_world_agent_message(
|
||||
}
|
||||
}
|
||||
|
||||
#[spacetimedb::procedure]
|
||||
pub fn finalize_custom_world_agent_message_turn(
|
||||
ctx: &mut ProcedureContext,
|
||||
input: CustomWorldAgentMessageFinalizeInput,
|
||||
) -> CustomWorldAgentOperationProcedureResult {
|
||||
match ctx.try_with_tx(|tx| finalize_custom_world_agent_message_turn_tx(tx, input.clone())) {
|
||||
Ok(operation) => CustomWorldAgentOperationProcedureResult {
|
||||
ok: true,
|
||||
operation: Some(operation),
|
||||
error_message: None,
|
||||
},
|
||||
Err(message) => CustomWorldAgentOperationProcedureResult {
|
||||
ok: false,
|
||||
operation: None,
|
||||
error_message: Some(message),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
#[spacetimedb::procedure]
|
||||
pub fn get_custom_world_agent_operation(
|
||||
ctx: &mut ProcedureContext,
|
||||
@@ -2514,16 +2537,6 @@ fn submit_custom_world_agent_message_tx(
|
||||
|
||||
let submitted_at = Timestamp::from_micros_since_unix_epoch(input.submitted_at_micros);
|
||||
let user_message_text = input.user_message_text.trim().to_string();
|
||||
let assistant_message_id = format!("assistant-{}", input.operation_id);
|
||||
if ctx
|
||||
.db
|
||||
.custom_world_agent_message()
|
||||
.message_id()
|
||||
.find(&assistant_message_id)
|
||||
.is_some()
|
||||
{
|
||||
return Err("custom_world_agent_message.assistant_message_id 已存在".to_string());
|
||||
}
|
||||
|
||||
ctx.db
|
||||
.custom_world_agent_message()
|
||||
@@ -2537,96 +2550,21 @@ fn submit_custom_world_agent_message_tx(
|
||||
created_at: submitted_at,
|
||||
});
|
||||
|
||||
let user_message_count = ctx
|
||||
.db
|
||||
.custom_world_agent_message()
|
||||
.iter()
|
||||
.filter(|row| {
|
||||
row.session_id == input.session_id && matches!(row.role, RpgAgentMessageRole::User)
|
||||
})
|
||||
.count() as u32;
|
||||
let next_turn = session.current_turn.saturating_add(1);
|
||||
let (next_stage, next_progress_percent, next_readiness_json, next_pending_clarifications_json) =
|
||||
if user_message_count >= 2 {
|
||||
(
|
||||
RpgAgentStage::FoundationReview,
|
||||
100,
|
||||
r#"{"isReady":true,"completedKeys":["seed_input"],"missingKeys":[]}"#.to_string(),
|
||||
"[]".to_string(),
|
||||
)
|
||||
} else {
|
||||
(
|
||||
RpgAgentStage::Clarifying,
|
||||
session.progress_percent.max(20),
|
||||
session.creator_intent_readiness_json.clone(),
|
||||
format!(
|
||||
r#"[{{"id":"clarify-{next_turn}","label":"补充核心设定","question":"请继续补充这个世界的玩家身份、主题氛围或核心冲突。","targetKey":"core_conflict","priority":1}}]"#
|
||||
),
|
||||
)
|
||||
};
|
||||
let assistant_reply = "已记录这条设定。我会先把它当作新的世界线索收进当前草稿,你可以继续补充玩家身份、主题氛围、核心冲突、关键关系或标志性元素。".to_string();
|
||||
|
||||
ctx.db
|
||||
.custom_world_agent_operation()
|
||||
.insert(CustomWorldAgentOperation {
|
||||
operation_id: input.operation_id.clone(),
|
||||
session_id: input.session_id.clone(),
|
||||
operation_type: RpgAgentOperationType::ProcessMessage,
|
||||
status: RpgAgentOperationStatus::Completed,
|
||||
phase_label: "消息已处理".to_string(),
|
||||
phase_detail: if next_stage == RpgAgentStage::FoundationReview {
|
||||
"当前上下文已达到最小 foundation_review 门槛。".to_string()
|
||||
} else {
|
||||
"当前上下文已记录,继续收集世界关键锚点。".to_string()
|
||||
},
|
||||
progress: 100,
|
||||
status: RpgAgentOperationStatus::Running,
|
||||
phase_label: "消息处理中".to_string(),
|
||||
phase_detail: "已记录用户消息,等待大模型生成本轮回复。".to_string(),
|
||||
progress: 10,
|
||||
error_message: None,
|
||||
created_at: submitted_at,
|
||||
updated_at: submitted_at,
|
||||
});
|
||||
|
||||
ctx.db
|
||||
.custom_world_agent_message()
|
||||
.insert(CustomWorldAgentMessage {
|
||||
message_id: assistant_message_id,
|
||||
session_id: input.session_id.clone(),
|
||||
role: RpgAgentMessageRole::Assistant,
|
||||
kind: RpgAgentMessageKind::Chat,
|
||||
text: assistant_reply.clone(),
|
||||
related_operation_id: Some(input.operation_id.clone()),
|
||||
created_at: submitted_at,
|
||||
});
|
||||
|
||||
ctx.db
|
||||
.custom_world_agent_session()
|
||||
.session_id()
|
||||
.update(CustomWorldAgentSession {
|
||||
session_id: session.session_id.clone(),
|
||||
owner_user_id: session.owner_user_id.clone(),
|
||||
seed_text: session.seed_text.clone(),
|
||||
current_turn: next_turn,
|
||||
progress_percent: next_progress_percent,
|
||||
stage: next_stage,
|
||||
focus_card_id: session.focus_card_id.clone(),
|
||||
anchor_content_json: session.anchor_content_json.clone(),
|
||||
creator_intent_json: session.creator_intent_json.clone(),
|
||||
creator_intent_readiness_json: next_readiness_json,
|
||||
anchor_pack_json: session.anchor_pack_json.clone(),
|
||||
lock_state_json: session.lock_state_json.clone(),
|
||||
draft_profile_json: session.draft_profile_json.clone(),
|
||||
last_assistant_reply: Some(assistant_reply),
|
||||
publish_gate_json: session.publish_gate_json.clone(),
|
||||
result_preview_json: session.result_preview_json.clone(),
|
||||
pending_clarifications_json: next_pending_clarifications_json,
|
||||
quality_findings_json: session.quality_findings_json.clone(),
|
||||
suggested_actions_json: session.suggested_actions_json.clone(),
|
||||
recommended_replies_json: session.recommended_replies_json.clone(),
|
||||
asset_coverage_json: session.asset_coverage_json.clone(),
|
||||
checkpoints_json: session.checkpoints_json.clone(),
|
||||
created_at: session.created_at,
|
||||
updated_at: submitted_at,
|
||||
});
|
||||
|
||||
get_custom_world_agent_operation_tx(
|
||||
ctx,
|
||||
CustomWorldAgentOperationGetInput {
|
||||
@@ -2661,6 +2599,93 @@ fn get_custom_world_agent_operation_tx(
|
||||
Ok(build_custom_world_agent_operation_snapshot(&operation))
|
||||
}
|
||||
|
||||
fn finalize_custom_world_agent_message_turn_tx(
|
||||
ctx: &ReducerContext,
|
||||
input: CustomWorldAgentMessageFinalizeInput,
|
||||
) -> Result<CustomWorldAgentOperationSnapshot, String> {
|
||||
validate_custom_world_agent_message_finalize_input(&input)
|
||||
.map_err(|error| error.to_string())?;
|
||||
|
||||
let session = ctx
|
||||
.db
|
||||
.custom_world_agent_session()
|
||||
.session_id()
|
||||
.find(&input.session_id)
|
||||
.filter(|row| row.owner_user_id == input.owner_user_id)
|
||||
.ok_or_else(|| "custom_world_agent_session 不存在".to_string())?;
|
||||
|
||||
let operation = ctx
|
||||
.db
|
||||
.custom_world_agent_operation()
|
||||
.operation_id()
|
||||
.find(&input.operation_id)
|
||||
.filter(|row| row.session_id == input.session_id)
|
||||
.ok_or_else(|| "custom_world_agent_operation 不存在".to_string())?;
|
||||
|
||||
if ctx
|
||||
.db
|
||||
.custom_world_agent_message()
|
||||
.message_id()
|
||||
.find(&input.assistant_message_id)
|
||||
.is_some()
|
||||
{
|
||||
return Err("custom_world_agent_message.assistant_message_id 已存在".to_string());
|
||||
}
|
||||
|
||||
let updated_at = Timestamp::from_micros_since_unix_epoch(input.updated_at_micros);
|
||||
|
||||
ctx.db
|
||||
.custom_world_agent_message()
|
||||
.insert(CustomWorldAgentMessage {
|
||||
message_id: input.assistant_message_id.clone(),
|
||||
session_id: input.session_id.clone(),
|
||||
role: RpgAgentMessageRole::Assistant,
|
||||
kind: RpgAgentMessageKind::Chat,
|
||||
text: input.assistant_reply_text.clone(),
|
||||
related_operation_id: Some(input.operation_id.clone()),
|
||||
created_at: updated_at,
|
||||
});
|
||||
|
||||
let next_session = rebuild_custom_world_agent_session_row(
|
||||
&session,
|
||||
CustomWorldAgentSessionPatch {
|
||||
current_turn: Some(session.current_turn.saturating_add(1)),
|
||||
progress_percent: Some(input.progress_percent),
|
||||
stage: Some(input.stage),
|
||||
focus_card_id: Some(input.focus_card_id.clone()),
|
||||
anchor_content_json: Some(input.anchor_content_json.clone()),
|
||||
creator_intent_json: Some(input.creator_intent_json.clone()),
|
||||
creator_intent_readiness_json: Some(input.creator_intent_readiness_json.clone()),
|
||||
anchor_pack_json: Some(input.anchor_pack_json.clone()),
|
||||
draft_profile_json: Some(input.draft_profile_json.clone()),
|
||||
last_assistant_reply: Some(Some(input.assistant_reply_text.clone())),
|
||||
pending_clarifications_json: Some(input.pending_clarifications_json.clone()),
|
||||
quality_findings_json: Some(input.quality_findings_json.clone()),
|
||||
suggested_actions_json: Some(input.suggested_actions_json.clone()),
|
||||
recommended_replies_json: Some(input.recommended_replies_json.clone()),
|
||||
asset_coverage_json: Some(input.asset_coverage_json.clone()),
|
||||
updated_at_micros: Some(input.updated_at_micros),
|
||||
..CustomWorldAgentSessionPatch::default()
|
||||
},
|
||||
)?;
|
||||
replace_custom_world_agent_session(ctx, &session, next_session);
|
||||
|
||||
let next_operation = rebuild_custom_world_agent_operation_row(
|
||||
&operation,
|
||||
CustomWorldAgentOperationPatch {
|
||||
status: Some(input.operation_status),
|
||||
phase_label: Some(input.phase_label.clone()),
|
||||
phase_detail: Some(input.phase_detail.clone()),
|
||||
progress: Some(input.operation_progress),
|
||||
error_message: Some(input.error_message.clone()),
|
||||
updated_at_micros: Some(input.updated_at_micros),
|
||||
},
|
||||
)?;
|
||||
replace_custom_world_agent_operation(ctx, &operation, next_operation.clone());
|
||||
|
||||
Ok(build_custom_world_agent_operation_snapshot(&next_operation))
|
||||
}
|
||||
|
||||
// AI 任务当前先固定成 private 真相表,后续由 Axum / platform-llm 再往外包一层 HTTP 与 SSE 协议。
|
||||
#[spacetimedb::reducer]
|
||||
pub fn create_ai_task(ctx: &ReducerContext, input: AiTaskCreateInput) -> Result<(), String> {
|
||||
@@ -5027,6 +5052,7 @@ fn execute_placeholder_custom_world_action(
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
struct CustomWorldAgentSessionPatch {
|
||||
current_turn: Option<u32>,
|
||||
progress_percent: Option<u32>,
|
||||
stage: Option<RpgAgentStage>,
|
||||
focus_card_id: Option<Option<String>>,
|
||||
@@ -5048,6 +5074,16 @@ struct CustomWorldAgentSessionPatch {
|
||||
updated_at_micros: Option<i64>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
struct CustomWorldAgentOperationPatch {
|
||||
status: Option<RpgAgentOperationStatus>,
|
||||
phase_label: Option<String>,
|
||||
phase_detail: Option<String>,
|
||||
progress: Option<u32>,
|
||||
error_message: Option<Option<String>>,
|
||||
updated_at_micros: Option<i64>,
|
||||
}
|
||||
|
||||
fn build_custom_world_publish_gate_from_session(
|
||||
session: &CustomWorldAgentSession,
|
||||
) -> CustomWorldPublishGateSnapshot {
|
||||
@@ -5467,7 +5503,7 @@ fn rebuild_custom_world_agent_session_row(
|
||||
session_id: current.session_id.clone(),
|
||||
owner_user_id: current.owner_user_id.clone(),
|
||||
seed_text: current.seed_text.clone(),
|
||||
current_turn: current.current_turn,
|
||||
current_turn: patch.current_turn.unwrap_or(current.current_turn),
|
||||
progress_percent: patch.progress_percent.unwrap_or(current.progress_percent),
|
||||
stage: patch.stage.unwrap_or(current.stage),
|
||||
focus_card_id: patch
|
||||
@@ -5527,6 +5563,44 @@ fn rebuild_custom_world_agent_session_row(
|
||||
})
|
||||
}
|
||||
|
||||
fn rebuild_custom_world_agent_operation_row(
|
||||
current: &CustomWorldAgentOperation,
|
||||
patch: CustomWorldAgentOperationPatch,
|
||||
) -> Result<CustomWorldAgentOperation, String> {
|
||||
let phase_label = patch
|
||||
.phase_label
|
||||
.unwrap_or_else(|| current.phase_label.clone());
|
||||
let progress = patch.progress.unwrap_or(current.progress);
|
||||
validate_custom_world_agent_operation_fields(
|
||||
¤t.operation_id,
|
||||
¤t.session_id,
|
||||
&phase_label,
|
||||
progress,
|
||||
)
|
||||
.map_err(|error| error.to_string())?;
|
||||
|
||||
Ok(CustomWorldAgentOperation {
|
||||
operation_id: current.operation_id.clone(),
|
||||
session_id: current.session_id.clone(),
|
||||
operation_type: current.operation_type,
|
||||
status: patch.status.unwrap_or(current.status),
|
||||
phase_label,
|
||||
phase_detail: patch
|
||||
.phase_detail
|
||||
.unwrap_or_else(|| current.phase_detail.clone()),
|
||||
progress,
|
||||
error_message: patch
|
||||
.error_message
|
||||
.unwrap_or_else(|| current.error_message.clone()),
|
||||
created_at: current.created_at,
|
||||
updated_at: Timestamp::from_micros_since_unix_epoch(
|
||||
patch
|
||||
.updated_at_micros
|
||||
.unwrap_or_else(|| current.updated_at.to_micros_since_unix_epoch()),
|
||||
),
|
||||
})
|
||||
}
|
||||
|
||||
fn replace_custom_world_agent_session(
|
||||
ctx: &ReducerContext,
|
||||
current: &CustomWorldAgentSession,
|
||||
@@ -5539,6 +5613,18 @@ fn replace_custom_world_agent_session(
|
||||
ctx.db.custom_world_agent_session().insert(next);
|
||||
}
|
||||
|
||||
fn replace_custom_world_agent_operation(
|
||||
ctx: &ReducerContext,
|
||||
current: &CustomWorldAgentOperation,
|
||||
next: CustomWorldAgentOperation,
|
||||
) {
|
||||
ctx.db
|
||||
.custom_world_agent_operation()
|
||||
.operation_id()
|
||||
.delete(¤t.operation_id);
|
||||
ctx.db.custom_world_agent_operation().insert(next);
|
||||
}
|
||||
|
||||
fn replace_custom_world_draft_card(
|
||||
ctx: &ReducerContext,
|
||||
current: &CustomWorldDraftCard,
|
||||
|
||||
Reference in New Issue
Block a user