This commit is contained in:
2026-04-24 22:27:45 +08:00
35 changed files with 1862 additions and 237 deletions

View File

@@ -0,0 +1,31 @@
# 创作 Agent 结果页 SSE 断开修复
日期:`2026-04-24`
## 1. 问题
RPG 世界共创草稿进入生成结果页后,前端仍可能保留上一条聊天消息的 `/messages/stream` 连接。该连接继续接收 `reply_delta` 时,会让结果页阶段仍表现为“聊天还在连着 SSE”。
## 2. 原因
当前聊天流式请求由 `streamRpgCreationMessage` 发起,底层使用 `fetch` 读取 SSE `ReadableStream`。旧实现只在请求自然结束后清理 `isStreamingAgentReply`,没有在以下 UI 生命周期主动中止网络流:
1.`agent-workspace` 跳到 `custom-world-result`
2. 清空或切换 `activeAgentSessionId`
3. 当前入口组件卸载。
因此,结果页虽然不再展示聊天工作区,但浏览器侧仍可能持有未完成的流读取器。
## 3. 修复设计
1. `TextStreamOptions` 增加 `signal?: AbortSignal`,让所有创作 Agent 流式读取都具备统一取消入口。
2. RPG 共创 `/messages/stream``fetch` 透传该 `signal`
3. `useRpgCreationSessionController` 持有当前聊天流的 `AbortController`
4.`selectionStage` 离开 `agent-workspace` / `custom-world-generating`,立即 `abort()` 当前聊天 SSE并清空临时流式文本。
5. session 切换、未登录清理、组件卸载时同样中止旧 SSE避免慢响应回写旧工作区状态。
## 4. 验收
1. 聊天中触发草稿生成并进入结果页后,浏览器 Network 中旧 `/messages/stream` 请求应变为 canceled/aborted 或结束。
2. 结果页不再继续追加聊天 `reply_delta`
3. 回到 Agent 工作区后,新的聊天消息会创建新的 SSE不复用已中止连接。

View File

@@ -83,3 +83,27 @@ cargo test -p api-server custom_world_foundation_draft -- --nocapture
```
结果:后端检查通过;`custom_world_foundation_draft` 相关测试 `3 passed`
## 2026-04-24 `spacetime-client` facade 补齐
合并 `draft_foundation` 进度链路后,`spacetime-module` 和生成绑定中已经存在 `upsert_custom_world_agent_operation_progress` procedure但手写 `spacetime-client` facade 尚未导出对应 record input 与调用方法,导致 `api-server` 编译时报:
1. `CustomWorldAgentOperationProgressRecordInput` 未导出。
2. `SpacetimeClient::upsert_custom_world_agent_operation_progress` 不存在。
本次补齐边界:
1. `spacetime-client::mapper` 新增 `CustomWorldAgentOperationProgressRecordInput`
2. `spacetime-client::custom_world` 新增 `upsert_custom_world_agent_operation_progress(...)`,负责把字符串形式的 operation type/status 翻译为 SpacetimeDB 生成枚举后调用 procedure。
3. `spacetime-client::module_bindings::mod` 补入已生成的 progress input/procedure 索引,避免 procedure 文件存在但 `RemoteProcedures` 扩展 trait 未进入作用域。
4. `api-server` 只依赖 facade不直接碰生成绑定保持 HTTP 层与 SpacetimeDB 生成类型隔离。
补充验证:
```bash
cargo fmt -p spacetime-client -p api-server
cargo check -p api-server --bin api-server
npm run check:encoding
```
结果:`api-server` 编译通过,编码检查通过;剩余 warning 为既有 dead code。

View File

@@ -196,10 +196,10 @@ Rust DTO 只承载对前端公开的 HTTP contract不直接泄露 `module-puz
## 6. 结果页图片生成策略
本轮不引入新的真实图像模型编排,而是复用 `api-server` 里已有的占位资产写盘模式:
本轮后续已经接入 `api-server` 统一资产链路:拼图候选图由 `api-server` 调用图像服务生成,再以 OSS 对象作为持久化真值SpacetimeDB 只保存候选图 URL、assetId 与 prompt snapshot。
1. 每次生成 2 张候选图。
2. 候选图通过 `api-server` 写入 `public/generated-puzzle-covers/...`
2. 候选图通过 `api-server` 写入 OSS兼容展示路径统一为 `/generated-puzzle-assets/...`,禁止再落到仓库 `public/` 目录
3. Axum 把候选图 URL、assetId、prompt snapshot 回写到 Spacetime session draft。
4. 创作者在结果页选择其中 1 张作为正式图。
@@ -207,7 +207,7 @@ Rust DTO 只承载对前端公开的 HTTP contract不直接泄露 `module-puz
1. 结果页图片生成、重生、应用正式图完整可用。
2. 发布链有正式图片可校验。
3.额外扩到模型供应商集成
3.再依赖本地 `public/` 占位目录,避免开发工作区混入运行时生成文件
### 6.1 发布前编辑真相补充

View File

@@ -0,0 +1,68 @@
# 拼图玩法单机运行态与真实图片生成方案 2026-04-24
## 1. 本次收口目标
这次收口只做两件事:
1. 拼图结果页中的候选图生成不再返回本地 SVG 占位图,而是接入 Rust `api-server` 现有的真实外部生图链。
2. 拼图第一版运行态改为单机本地运行,不再把交换、拖动、通关进度和下一关状态保存到后端。
## 2. 第一版单机范围
第一版“单机版本”的准确定义如下:
1. 玩家从拼图广场或作品详情进入玩法时,前端基于作品详情在本地构造一次 `PuzzleRunSnapshot`
2. 交换拼图块、拖动拼图块、关卡是否拼完,全部由前端本地计算。
3. 本地运行态不调用 `/api/runtime/puzzle/runs/*` 写回当前过程状态。
4. 关闭玩法后,这次运行态直接失效,不做断点续玩,不做跨端同步。
5. 后端仍然负责:
- Agent 会话
- 结果页草稿编译
- 正式候选图生成
- 封面确认
- 作品发布
- 作品列表 / 详情 / 广场读取
这意味着第一版拼图玩法是“创作后端化、游玩本地化”的结构,而不是“所有状态都走后端”的结构。
## 3. 真实图片生成链
拼图正式候选图统一复用当前仓库已经跑通的 Rust 资产主链:
1. `api-server` 根据拼图草稿的关卡名和结果页 prompt 组装正式文生图 prompt。
2. 调用 DashScope 文生图接口创建异步任务。
3. 轮询任务直到拿到正式图片地址。
4. 下载图片二进制。
5. 上传到私有 OSS。
6.`module-assets` / `spacetime-client` 的资产真相链中确认对象并绑定到拼图实体。
7. 对前端返回 `/generated-puzzle-assets/*` 兼容路径,而不是本地 `svg` 占位路径。
## 4. 路径与边界
### 4.1 候选图输出路径
拼图正式候选图统一使用:
`/generated-puzzle-assets/*`
不能继续写到仓库本地 `public/generated-puzzle-covers/*`
### 4.2 运行态边界
第一版单机运行态保留现有 DTO 结构,目的是不重做界面层。
但 DTO 的来源变化为:
1. 进入玩法时从作品详情构造本地 `run`
2. 交换 / 拖动 / 通关时由前端工具函数返回新的 `run`
3. 当前不依赖后端 `start/swap/drag/next-level` 接口完成主链
## 5. 当前实现判断标准
当下面结果成立时,视为这一轮目标达成:
1. `generate_puzzle_images` 返回的 `imageSrc` 不再是本地 `svg` 占位图。
2. 返回路径切到 `/generated-puzzle-assets/*`
3. 未配置 DashScope 或 OSS 时,接口明确返回 provider 级错误,而不是静默回退占位图。
4. 玩家进入拼图玩法后,即使后端运行态接口不可用,也能在本地完成交换与拖动。
5. 关闭玩法后不保留当前 run 进度。

View File

@@ -0,0 +1,59 @@
# SpacetimeDB Maincloud 发布与 api-server 适配方案
## 目标
新增一条明确的 npm 命令链,用于把 `server-rs/crates/spacetime-module` 发布到 SpacetimeDB Maincloud并让 `api-server` 可以使用同一套 Maincloud 数据库配置启动。
## 环境变量约定
Maincloud 发布不复用本地 `spacetime.local.json`,避免误把本地开发库名发布到云端。需要显式提供:
| 变量 | 用途 |
| --- | --- |
| `GENARRATIVE_SPACETIME_MAINCLOUD_DATABASE` | Maincloud 数据库名,发布脚本优先读取 |
| `GENARRATIVE_SPACETIME_MAINCLOUD_SERVER_URL` | Maincloud 服务地址,默认 `https://maincloud.spacetimedb.com` |
| `GENARRATIVE_SPACETIME_MAINCLOUD_TOKEN` | `api-server` 连接 Maincloud 时使用的 token |
兼容 `api-server` 现有变量:
| 变量 | 用途 |
| --- | --- |
| `GENARRATIVE_SPACETIME_SERVER_URL` | `api-server` 实际连接地址 |
| `GENARRATIVE_SPACETIME_DATABASE` | `api-server` 实际连接数据库 |
| `GENARRATIVE_SPACETIME_TOKEN` | `api-server` 实际连接 token |
## npm 命令
```bash
npm run spacetime:publish:maincloud
```
执行内容:
1. 使用 `cargo build -p spacetime-module --target wasm32-unknown-unknown --release` 构建 wasm。
2. 使用 `spacetime publish <database> --server maincloud --bin-path <wasm> --yes` 发布到 Maincloud。
3. 输出 `api-server` 需要的 Maincloud 环境变量,便于部署进程复用。
如需 schema 冲突时清库发布:
```bash
npm run spacetime:publish:maincloud -- --clear-database
```
## api-server 启动
```bash
npm run api-server:maincloud
```
执行内容:
1.`.env``.env.local` 读取默认环境。
2.`GENARRATIVE_SPACETIME_MAINCLOUD_*` 映射为 `api-server` 已支持的 `GENARRATIVE_SPACETIME_*`
3. 启动 `cargo run -p api-server --manifest-path server-rs/Cargo.toml`
## 设计约束
- Maincloud 数据库名必须显式配置,不能默认读取本地 `spacetime.local.json`
- 发布脚本只处理 SpacetimeDB 模块发布,不启动本地 SpacetimeDB。
- `api-server` 继续通过 `SpacetimeClientConfig``server_url / database / token` 连接数据库,不在前端增加逻辑。