fix: delay creation agent reply until final session
This commit is contained in:
@@ -0,0 +1,23 @@
|
||||
# 世界共创聊天最终回复时机调整(2026-04-24)
|
||||
|
||||
## 背景
|
||||
|
||||
创作聊天页顶部进度条来自后端会话快照 `progressPercent`,而助手文本此前通过 SSE `reply_delta` 在模型生成过程中提前展示。这样会导致玩家先看到完整或接近完整的文本回复,但进度、锚点、阶段与推荐操作仍停留在上一轮状态。
|
||||
|
||||
## 本次约束
|
||||
|
||||
- 玩家可继续看到自己的输入被乐观插入聊天线程。
|
||||
- 助手回复不再通过中途 `reply_delta` 展示。
|
||||
- 本轮模型输出解析、SpacetimeDB finalize、最终 session 快照读取全部完成后,助手回复才随最终 session 一次性显示。
|
||||
- 进度条、阶段、锚点内容、推荐动作和助手回复在同一次 session 替换中同步刷新。
|
||||
|
||||
## 落地方案
|
||||
|
||||
1. `server-rs/crates/api-server/src/custom_world.rs` 的 `stream_custom_world_agent_message` 保留 SSE 响应格式,但不再发送 `reply_delta` 事件。
|
||||
2. 同一接口仍等待 `run_custom_world_agent_turn` 完成,再调用 `finalize_custom_world_agent_message` 写入 SpacetimeDB。
|
||||
3. finalize 成功后读取最终 session,并通过 `session` 事件一次性返回给前端。
|
||||
4. `src/components/creation-agent/CreationAgentWorkspace.tsx` 仅在确实存在流式文本时显示临时助手气泡,避免无 `reply_delta` 时出现空回复。
|
||||
|
||||
## 预期体验
|
||||
|
||||
发送消息后,玩家会先看到自己的消息和忙碌状态;助手文本会在进度、阶段与会话数据全部更新后一次性出现,避免“回复已经到了但进度还没动”的错位感。
|
||||
@@ -730,42 +730,18 @@ pub async fn stream_custom_world_agent_message(
|
||||
let owner_user_id_for_stream = owner_user_id.clone();
|
||||
let operation_id = operation.operation_id.clone();
|
||||
let stream = async_stream::stream! {
|
||||
let (reply_tx, mut reply_rx) = tokio::sync::mpsc::unbounded_channel::<String>();
|
||||
let turn_result = {
|
||||
let run_turn = run_custom_world_agent_turn(
|
||||
CustomWorldAgentTurnRequest {
|
||||
llm_client: state.llm_client(),
|
||||
session: &session,
|
||||
quick_fill_requested,
|
||||
focus_card_id,
|
||||
},
|
||||
move |text| {
|
||||
let _ = reply_tx.send(text.to_string());
|
||||
},
|
||||
);
|
||||
tokio::pin!(run_turn);
|
||||
|
||||
loop {
|
||||
tokio::select! {
|
||||
result = &mut run_turn => break result,
|
||||
maybe_text = reply_rx.recv() => {
|
||||
if let Some(text) = maybe_text {
|
||||
yield Ok::<Event, Infallible>(custom_world_sse_json_event_or_error(
|
||||
"reply_delta",
|
||||
json!({ "text": text }),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
while let Some(text) = reply_rx.recv().await {
|
||||
yield Ok::<Event, Infallible>(custom_world_sse_json_event_or_error(
|
||||
"reply_delta",
|
||||
json!({ "text": text }),
|
||||
));
|
||||
}
|
||||
// 聊天回复必须等本轮模型解析、进度与会话快照全部落库后,
|
||||
// 再随最终 session 一次性返回,避免玩家先看到回复而进度仍停在旧状态。
|
||||
let turn_result = run_custom_world_agent_turn(
|
||||
CustomWorldAgentTurnRequest {
|
||||
llm_client: state.llm_client(),
|
||||
session: &session,
|
||||
quick_fill_requested,
|
||||
focus_card_id,
|
||||
},
|
||||
|_| {},
|
||||
)
|
||||
.await;
|
||||
|
||||
let finalize_input = match turn_result {
|
||||
Ok(turn_result) => build_finalize_record_input(
|
||||
|
||||
@@ -317,7 +317,8 @@ export function CreationAgentWorkspace({
|
||||
shouldShowQuickAction(action, session, progress),
|
||||
);
|
||||
const streamingMessageId = `streaming-assistant-${session.sessionId}`;
|
||||
const displayedMessages = isStreamingReply
|
||||
const shouldShowStreamingReply = isStreamingReply && streamingReplyText.trim();
|
||||
const displayedMessages = shouldShowStreamingReply
|
||||
? [
|
||||
...session.messages,
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user