1
This commit is contained in:
@@ -21,7 +21,7 @@ RPG 草稿生成进入底稿素材阶段后,角色主形象与场景幕背景
|
||||
- 背景分支使用 `JoinSet` 把 `sceneChapterBlueprints[*].acts[*]` 的每一幕背景任务一次性投递,返回后写入 `backgroundImageSrc`、`backgroundAssetId`、`generatedScenePrompt`、`generatedSceneModel`。
|
||||
- `merge_generated_act_backgrounds` 只把背景图字段合并回角色分支副本,再进入后续草稿卡编译和 SpacetimeDB 写入。
|
||||
- 幕背景 prompt 同时兼容 `backgroundPromptText`、`scenePromptText`、`visualPromptText`、`promptText`、`imagePromptText`、`backgroundPrompt`、`visualPrompt`,避免 LLM 输出字段别名导致整批背景图被误判缺失。
|
||||
- 每个角色主形象、每一幕背景图都必须独立自动重试,单项最多尝试 3 次。任一单项超过 3 次仍失败时,后台任务必须把 operation 标记为 `failed` 并停止写入草稿卡,避免生成“缺主图 / 缺背景图”的可进入世界档案。
|
||||
- 每个角色主形象、每一幕背景图都必须独立自动重试,单项最多尝试 3 次。任一单项超过 3 次仍失败时,后台任务必须把 operation 标记为 `failed` 并停止写入草稿卡,避免生成“缺主图 / 缺背景图”的可进入世界档案。\r\n- 图片任务仍然一次性投递,保证角色与幕背景两类任务不回退到串行编排;但真正请求上游生图服务时必须共用并发闸门,当前同一底稿最多同时发起 2 个上游请求,降低 DashScope 瞬时 502 / 限流导致整批失败的概率。\r\n- 幕背景图失败文案必须带第几章、第几幕和幕标题,不能只显示“第1幕 / 第2幕 / 第3幕”,否则多章节同名幕会被用户误认为同一失败项重复上报。
|
||||
- 中止前必须持久化已经成功生成的部分底稿到会话 `draftProfile`,不能因为某个角色或某一幕失败而丢掉其它已生成的 `imageSrc / generatedVisualAssetId / backgroundImageSrc / backgroundAssetId`。
|
||||
- 前端 `CharacterAnimator` 对带 `generatedVisualAssetId` 但尚无 `animationMap` 的自定义角色,所有状态优先渲染生成主图;只有真正发布了动作集后才按动作帧播放,避免运行或战斗状态回落到模板 sprite。
|
||||
|
||||
@@ -30,3 +30,4 @@ RPG 草稿生成进入底稿素材阶段后,角色主形象与场景幕背景
|
||||
如果后续图片供应商出现强限流,再在网关层做队列或供应商侧限流;不要在 RPG 底稿编排层恢复逐张串行,否则会重新退化成多张图片总耗时累加。
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -17,6 +17,9 @@
|
||||
7. 发布为可运行玩法
|
||||
8. 进入竖屏全屏实时玩法运行态
|
||||
|
||||
补充说明:
|
||||
当前工程实现中,运行态页面需要直接消费结果页已经生成的等级主图、动作图与场地背景图,不能只在结果页预览资产、进入玩法后退回圆点占位表现。
|
||||
|
||||
本稿必须满足两个硬要求:
|
||||
|
||||
1. 不能沿用 RPG 创作链里的旧命名和旧数据口径,把实时吞噬玩法硬塞进 `rpg` 或旧 `customWorld` 语义里。
|
||||
@@ -189,6 +192,9 @@ Agent 在这一玩法里不负责实时玩法裁决,它只负责 3 件事:
|
||||
3. 会总结,不只会追问
|
||||
4. 会补缺,不会平均盘问
|
||||
5. 进度基于真实锚点完成度,而不是机械轮次
|
||||
6. 当会话至少完成 `2` 轮后,工作区必须提供 `补充剩余关键字` 快捷动作。
|
||||
- 该动作只向 Agent 发送“请补充剩余关键字。”,由后端 Agent 根据当前锚点补齐缺失关键词。
|
||||
- 前端不得自行推断成长阶梯、风险节奏或视觉母题,也不得直接改写锚点状态。
|
||||
|
||||
## 7.3 大鱼吃小鱼玩法的 4 个最小高杠杆锚点
|
||||
|
||||
@@ -945,6 +951,12 @@ Agent 在这一玩法里不负责实时玩法裁决,它只负责 3 件事:
|
||||
5. `src/services/big-fish-*`
|
||||
- 前端 client、adapter、view model
|
||||
|
||||
后端字段边界要求:
|
||||
|
||||
1. LLM 输出、HTTP 响应和前端交互契约可以使用 `camelCase`,例如 `nextAnchorPack`、`gameplayPromise`、`ecologyVisualTheme`。
|
||||
2. 写入 SpacetimeDB 的 `anchor_pack_json`、`draft_json`、`asset_coverage_json`、`snapshot_json` 必须序列化 Rust 领域结构,字段名保持 `snake_case`。
|
||||
3. `api-server` 负责在 LLM/HTTP 边界显式翻译字段名,不能把前端响应层 JSON 直接透传为 SpacetimeDB 持久化 JSON。
|
||||
|
||||
---
|
||||
|
||||
## 22. 平台内脚本命名规范
|
||||
|
||||
@@ -91,6 +91,16 @@
|
||||
10. 游戏画面必须显示作者信息和关卡名。
|
||||
11. 前端只负责表现和交互输入,逻辑、数据、关卡裁决、推荐计算、状态存储全部放到 `server-rs` 后端,由 `Axum + SpacetimeDB + OSS` 方案承接。
|
||||
|
||||
### 第一版单机例外说明 2026-04-24
|
||||
|
||||
为了先把拼图玩法跑通,第一版运行态采用单机本地版本,作为上面总原则的阶段性例外:
|
||||
|
||||
1. Agent 会话、结果页草稿、正式候选图生成、封面确认、发布、作品读取,仍然全部走 Rust 后端。
|
||||
2. 进入拼图玩法后的 `run` 只在前端本地内存中存在。
|
||||
3. 交换、拖动、通关判断不写回后端。
|
||||
4. 关闭玩法后不保留本次运行态,不做断点续玩。
|
||||
5. 后续如果要做跨端续玩、多端同步或排行榜,再把运行态真相源收回后端。
|
||||
|
||||
---
|
||||
|
||||
## 4. 明确不做
|
||||
@@ -191,7 +201,10 @@
|
||||
1. 优先接住创作者的画面灵感,而不是立刻列问卷。
|
||||
2. 每轮只追问当前最影响图片生成质量的 `1` 个问题。
|
||||
3. 当创作者已经说出足够信息时,优先总结,不重复追问。
|
||||
4. 在进入结果页前,至少确认:
|
||||
4. 当会话至少完成 `2` 轮后,工作区必须提供 `补充剩余关键字` 快捷动作。
|
||||
- 该动作只向 Agent 发送“请补充剩余关键字。”,不在前端补数据、不伪造锚点状态。
|
||||
- Agent 收到后应优先补齐仍为 `待补充` / 空值的锚点关键词,并保持每次回复清爽直接。
|
||||
5. 在进入结果页前,至少确认:
|
||||
- 一句题材承诺
|
||||
- 一个主要视觉主体
|
||||
- 一组气质描述
|
||||
@@ -294,6 +307,12 @@ interface PuzzleAnchorPack {
|
||||
2. 创作者选择 `1` 张作为正式图
|
||||
3. 正式图确定后,写回作品主图
|
||||
|
||||
后端落地契约:
|
||||
|
||||
1. `api-server` 写入 SpacetimeDB 的候选图 JSON 必须使用 `module-puzzle::PuzzleGeneratedImageCandidate` 持久化结构。
|
||||
2. 持久化字段名保持 Rust 侧 `snake_case`,例如 `candidate_id`、`image_src`、`asset_id`、`actual_prompt`、`source_type`。
|
||||
3. 面向前端的 HTTP 响应仍由 `shared-contracts` 单独映射为 `camelCase`,不能把响应层字段名直接写入 SpacetimeDB JSON。
|
||||
|
||||
## 7.6 拼图图片资产要求
|
||||
|
||||
拼图图片的正式资产要求:
|
||||
|
||||
@@ -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,不复用已中止连接。
|
||||
@@ -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。
|
||||
|
||||
@@ -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 发布前编辑真相补充
|
||||
|
||||
|
||||
@@ -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 进度。
|
||||
59
docs/technical/SPACETIMEDB_MAINCLOUD_PUBLISH_2026-04-24.md
Normal file
59
docs/technical/SPACETIMEDB_MAINCLOUD_PUBLISH_2026-04-24.md
Normal 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` 连接数据库,不在前端增加逻辑。
|
||||
Reference in New Issue
Block a user