Preserve partial creation replies on stream failure
Some checks failed
CI / verify (push) Has been cancelled

This commit is contained in:
kdletters
2026-05-05 11:31:50 +08:00
parent 100fee7e7a
commit 995661e7cc
299 changed files with 13805 additions and 1429 deletions

View File

@@ -0,0 +1,53 @@
# 创作 Agent 流式失败保留可见回复修复 2026-05-05
## 1. 问题
方洞挑战等轻量玩法复用 `usePlatformCreationAgentFlowController``creationAgentSse.ts` 消费 `reply_delta / session / error`。当上游 LLM 已经返回部分 `replyText`但后续因为超时、上游断流、SSE 解析或最终 JSON 解析失败而发送 `error` 事件时,前端会在 `finally` 里退出流式态。
旧 UI 只在 `isStreamingReply=true` 时展示临时 assistant 气泡,因此用户会先看到一段回答,然后回答突然消失,只剩错误提示。
## 2. 目标
1. 已经展示给用户的流式回复不能因为最终失败从聊天区消失。
2. SSE `error` 仍然终止本轮提交,并保留错误提示。
3. 后端错误不能只压成 `上游服务请求失败`,应优先把 LLM 流错误原因放到业务 `message`
4. 不修改 SpacetimeDB schema、消息表结构或玩法运行规则。
## 3. 前端契约
`readCreationAgentSessionFromSse()` 在收到 `reply_delta` 后再收到 `error` 时,必须先触发 `onUpdate(text)`,再抛出错误。调用方可以从最近一次可见文本中恢复 UI。
`usePlatformCreationAgentFlowController.submitMessage()` 的失败收尾规则:
1. 提交时仍先追加 optimistic user message。
2. 每次 `onUpdate` 同步更新 `streamingReplyText` 与最近可见回复引用。
3. 如果 `streamMessage()` 抛错且最近可见回复非空,把该文本追加为本地 assistant `warning` 消息。
4. 再设置 `error`,最后关闭 `isStreamingReply`
5. 成功拿到最终 session 时,以后端 session snapshot 为准,并清空最近可见回复。
这条本地 `warning` 消息只用于失败态 UI 保留,不代表该 assistant 消息已经写入 SpacetimeDB。
## 4. 后端契约
`creation_agent_llm_turn``LlmClient::stream_text()` 失败时,返回:
```text
<玩法 generation_failed 文案><LlmError Display>
```
同时写 `warn` 日志,便于结合 `logs/llm-raw` 定位上游原始输入输出。
方洞挑战 SSE 错误提取优先级:
1. `error.details.message`
2. `error.message`
3. 其它嵌套 JSON message
4. 原始 body 文本
5. 状态码兜底
## 5. 验收
1. `reply_delta` 后收到 `error` 时,测试应断言 `onUpdate` 已经收到可见文本。
2. 控制器测试应断言失败后本地消息列表包含 user 消息和 assistant warning 消息。
3. `cargo check -p api-server` 通过。
4. `npm run typecheck` 与编码检查通过。