1
Some checks failed
CI / verify (push) Has been cancelled

This commit is contained in:
2026-04-26 21:07:55 +08:00
609 changed files with 4601 additions and 14501 deletions

View File

@@ -87,7 +87,7 @@
4. 重新执行:
```powershell
spacetime generate --no-config --lang rust --out-dir D:\Genarrative\server-rs\crates\spacetime-client\src\module_bindings --module-path D:\Genarrative\server-rs\crates\spacetime-module --include-private --yes
npm run spacetime:generate
```
说明:

View File

@@ -107,7 +107,7 @@
```bash
spacetime start --listen-addr 127.0.0.1:3000
spacetime publish genarrative-dev --server local --yes --module-path server-rs/crates/spacetime-module
spacetime generate --lang rust --out-dir server-rs/crates/spacetime-client/src/module_bindings --module-path server-rs/crates/spacetime-module --include-private --yes
npm run spacetime:generate
```
## 5. 为什么当前阶段不用 CLI 文本解析

View File

@@ -195,7 +195,7 @@
1. `cargo check -p module-story --manifest-path D:\\Genarrative\\server-rs\\Cargo.toml`
2. `cargo check -p spacetime-module --manifest-path D:\\Genarrative\\server-rs\\Cargo.toml`
3. `cargo check -p spacetime-module --target wasm32-unknown-unknown --manifest-path D:\\Genarrative\\server-rs\\Cargo.toml`
4. `spacetime generate --no-config --lang rust --out-dir D:\\Genarrative\\server-rs\\crates\\spacetime-client\\src\\module_bindings --module-path D:\\Genarrative\\server-rs\\crates\\spacetime-module --include-private --yes`
4. `npm run spacetime:generate`
5. `cargo check -p spacetime-client --manifest-path D:\\Genarrative\\server-rs\\Cargo.toml`
6. `cargo check -p api-server --manifest-path D:\\Genarrative\\server-rs\\Cargo.toml`
7. `npm run check:encoding`
@@ -234,7 +234,7 @@
同时,本轮还完成了以下工程收口:
1. 已重新执行 `spacetime generate --no-config --lang rust --out-dir D:\\Genarrative\\server-rs\\crates\\spacetime-client\\src\\module_bindings --module-path D:\\Genarrative\\server-rs\\crates\\spacetime-module --include-private --yes`
1. 已重新执行 `npm run spacetime:generate`
2. 已把 `spacetime-client` 中 battle query 的占位实现替换为真实 procedure 调用。
3. 已再次执行 `cargo check -p spacetime-client --manifest-path D:\\Genarrative\\server-rs\\Cargo.toml``cargo check -p api-server --manifest-path D:\\Genarrative\\server-rs\\Cargo.toml` 并通过。

View File

@@ -340,7 +340,7 @@ finalScore = tagSimilarityScore * 0.7 + sameAuthorScore * 0.3;
4. `cargo check -p module-puzzle`
5. `cargo check -p shared-contracts`
6. `cargo check -p spacetime-module`
7. `spacetime generate --no-config --lang rust --out-dir server-rs/crates/spacetime-client/src/module_bindings --module-path server-rs/crates/spacetime-module --include-private --yes`
7. `npm run spacetime:generate`
8. `cargo check -p spacetime-client`
9. `cargo check -p api-server`

View File

@@ -4,6 +4,7 @@
## 文档列表
- [RPG_AND_AGENT_CHAT_TRUE_SSE_STREAMING_2026-04-26.md](./RPG_AND_AGENT_CHAT_TRUE_SSE_STREAMING_2026-04-26.md):记录 RPG 运行时 NPC 聊天、RPG/自定义世界 Agent 与大鱼 Agent 从“拼完整 SSE 字符串后一次性返回”改为 `mpsc + Sse<Event>` 真流式输出的后端落地口径。
- [RPG_BATTLE_HEALTHBAR_AND_ACTION_PRESENTATION_FIX_2026-04-26.md](./RPG_BATTLE_HEALTHBAR_AND_ACTION_PRESENTATION_FIX_2026-04-26.md):记录 RPG 战斗血条安全锚点、服务端战斗回包前端短表现,以及 `battle_use_skill` 指定技能兜底结算的修复口径。
- [SPACETIMEDB_TABLE_CATALOG.md](./SPACETIMEDB_TABLE_CATALOG.md):持续维护当前 SpacetimeDB 表目录,按领域说明每张表的作用、字段结构、索引和常用 `spacetime sql` 查询模板。
- [RPG_OPENING_SCENE_ACT_IMAGE_PRESENTATION_SYNC_2026-04-26.md](./RPG_OPENING_SCENE_ACT_IMAGE_PRESENTATION_SYNC_2026-04-26.md):记录开局场景与普通场景复用同一场景展示解析服务,修复列表幕缩略图和详情幕背景预览图片不一致的问题。

View File

@@ -0,0 +1,51 @@
# RPG 与 Agent 聊天真流式 SSE 修复2026-04-26
## 背景
RPG 运行时 NPC 聊天和创作 Agent 聊天此前都使用 `platform-llm.stream_text(...)`,但部分 HTTP handler 会把 `reply_delta` 先拼进内存字符串,等模型完整返回、建议生成和会话落库完成后才一次性响应。前端虽然消费的是 SSE 协议,实际体验仍接近非流式。
本次落地把这类路由改成真正边生成边输出:
1. RPG 运行时 NPC 聊天:`POST /api/runtime/chat/npc/turn/stream`
2. RPG/自定义世界创作 Agent`POST /api/runtime/custom-world/agent/sessions/:sessionId/messages/stream`
3. 大鱼吃小鱼创作 Agent`POST /api/runtime/big-fish/agent/sessions/:sessionId/messages/stream`
拼图 Agent 已经使用同类 `mpsc + Sse<Event>` 结构,本轮只作为参照,不重复改动。
## 设计约束
1. 前端 SSE contract 不变,继续消费 `reply_delta / complete / session / done / error`
2. `reply_delta` 必须在模型生成过程中实时发送,不再等待完整 handler 结束。
3. Agent 的最终 `session` 仍必须等 SpacetimeDB `finalize_*` 成功后再发送,保证前端真相态与表内状态一致。
4. RPG NPC 聊天的建议、好感变化、敌对终止判定仍在完整回复后计算,最终通过 `complete` 一次性返回。
5. LLM 不可用或失败时RPG NPC 聊天继续走确定性兜底,仍发送至少一条 `reply_delta``complete`
## 落地方案
### Axum SSE 输出
相关 handler 改为返回 `Sse::new(async_stream::stream! { ... })`。模型回调不能直接 `yield`,因此使用:
1. `tokio::sync::mpsc::unbounded_channel::<String>()`
2. LLM turn 回调把最新可见回复写入 `reply_tx`
3. 外层 `tokio::select!` 同时等待 LLM 完成和 `reply_rx.recv()`
4. 每次收到文本立即 `yield Event::default().event("reply_delta")`
### Agent 写回顺序
Agent 路由保持原有 submit/finalize 分工:
1. `submit_*_message` 先写入用户消息与 operation。
2. `run_*_agent_turn` 运行模型,同时把 `replyText` 增量推给 SSE。
3. turn 完成后构造 `finalize_*_agent_message` 输入并写回 SpacetimeDB。
4. finalize 成功后再读取或映射最新 session发送 `session``done`
5. finalize 失败时发送 `error` 并结束 SSE。
## 验收
1. `cargo fmt -p api-server`
2. `cargo check -p api-server`
3. `cargo test -p api-server runtime_chat`
4. `cargo test -p api-server creation_agent_llm_turn`
5. `node scripts/check-encoding.mjs docs/technical/RPG_AND_AGENT_CHAT_TRUE_SSE_STREAMING_2026-04-26.md server-rs/crates/api-server/src/runtime_chat.rs server-rs/crates/api-server/src/custom_world.rs server-rs/crates/api-server/src/big_fish.rs docs/technical/README.md`
6. 修改后端代码后,使用 `npm run api-server:maincloud` 重启后端。

View File

@@ -47,6 +47,7 @@
- 优先 `LlmClient.stream_text(...)` 生成 `reply_delta`
- 再调用 `request_text(...)` 生成建议。
- 计算 `affinityDelta / affinityText / chatDirective` 后输出 `complete`
- SSE stream 返回给 Axum 时要求 `'static`,进入 stream 的玩家输入必须先转成 owned `String`,不能把 handler 栈上的 `&str` 借入 stream。
3. 修改 `server-rs/crates/api-server/src/auth.rs`
- `allows_internal_forwarded_auth(...)` 允许 `/api/runtime/chat/`,与 big-fish、puzzle 的内部转发鉴权策略保持一致。
- 单测覆盖 `/api/runtime/chat/npc/turn/stream`,防止后续新增 runtime 路由时再次遗漏内部转发白名单。

View File

@@ -34,7 +34,7 @@ npm run dev:rust
1. 检查 `cargo``node``spacetime` CLI。
2. Windows Git Bash 下如 `server-rs/.spacetimedb/local/bin/current/spacetimedb-cli.exe` 不存在,先把本机 `spacetime` 所在安装目录的 `bin/``spacetime.exe` 同步到 `server-rs/.spacetimedb/local/`
3. 启动 `spacetime --root-dir=server-rs/.spacetimedb/local start --edition standalone --listen-addr 127.0.0.1:3101`,确保本地数据库与 SpacetimeDB 内部日志不会落到开发者全局目录。
4. 等待 `spacetime --root-dir=server-rs/.spacetimedb/local server ping http://127.0.0.1:3101` 可用。
4. 等待 `spacetime --root-dir=server-rs/.spacetimedb/local server ping http://127.0.0.1:3101` 可用;判定标准必须包含输出中的 `Server is online:`,不能只依赖 CLI 退出码,因为 SpacetimeDB CLI `2.1.0``502 Bad Gateway` 时也可能返回退出码 `0`
5. 执行 `spacetime --root-dir=server-rs/.spacetimedb/local publish <本地数据库名> --server http://127.0.0.1:3101 --module-path server-rs/crates/spacetime-module -c=on-conflict --yes`,确保 publish 的签名身份与 standalone 的本地控制库一致,并在当前开发阶段允许新版模块表结构变化且发生 schema 冲突时清除旧模块数据。
6. 注入 `GENARRATIVE_API_*``GENARRATIVE_SPACETIME_*` 后启动 `cargo run -p api-server`;直接运行 `api-server` 时,如未显式设置 `GENARRATIVE_SPACETIME_DATABASE`,服务端也会向上查找 `spacetime.local.json` 作为本地默认库名。
7. 等待 `http://127.0.0.1:<api-port>/healthz` 返回 HTTP 响应后再启动 Vite避免前端初始化请求早于 Rust `api-server` 监听完成并在终端刷出 `ECONNREFUSED 127.0.0.1:<api-port>`
@@ -65,6 +65,23 @@ npm run dev:rust
./scripts/dev-rust-stack.sh --preserve-database
```
bindings 生成:
```bash
npm run spacetime:generate
npm run spacetime:generate -- --rust-only
```
生成规则:
1. `npm run spacetime:generate` 是本仓库刷新 SpacetimeDB Rust client bindings 的唯一推荐入口。
2. 前端不直连 SpacetimeDB不生成 TypeScript bindings前端只通过 Rust `api-server` 暴露的 HTTP / SSE contract 访问后端。
3. Rust bindings 不生成私有表绑定,不追加 `--include-private`,生成到短临时目录后同步到 `server-rs/crates/spacetime-client/src/module_bindings`
4. Windows 下 SpacetimeDB CLI `2.1.0` 会在生成结束后把所有生成文件路径一次性传给 formatterRust bindings 文件较多,若直接输出到仓库深目录,可能触发 `Could not format generated files: 文件名或扩展名太长。`。该错误不是单个最长文件路径超过限制,而是 formatter 子进程参数总长超过 Windows `CreateProcess` 限制。
5. CLI 在上述 formatter 失败时仍可能返回退出码 `0`;脚本会捕获输出,只要出现 `Could not format generated files` 就视为失败。
6. 根目录 `spacetime.json` 不配置 `generate` 目标,避免裸 `spacetime generate` 在 Windows 上直接碰到 Rust formatter 路径总长限制,也避免误生成前端 bindings。
7. 不直接手写或局部补 `server-rs/crates/spacetime-client/src/module_bindings` 下的生成文件schema 变化后重新执行本脚本,并补跑编码检查与对应类型检查。
日志提取:
```bash
@@ -86,6 +103,7 @@ npm run dev:rust:logs -- --follow
2. `spacetime --root-dir=server-rs/.spacetimedb/local list --server http://127.0.0.1:3101` 应能看到 `spacetime.local.json` 中的库名;若没有,执行 `spacetime --root-dir=server-rs/.spacetimedb/local publish <本地数据库名> --server http://127.0.0.1:3101 --module-path server-rs/crates/spacetime-module -c=on-conflict --yes`
3. 发布库名与 `GENARRATIVE_SPACETIME_DATABASE` 不一致时,`/api/runtime/custom-world-gallery` 会从 Rust `api-server` 返回 `502`,前端首页只能展示空态或错误提示,无法自行修复。
4. 如果 Vite 输出 `/api/auth/refresh``/api/auth/login-options``/api/runtime/custom-world-gallery``ECONNREFUSED`,先确认当前脚本是否已经打印 `等待 api-server 就绪` 并通过;正常情况下 Vite 只会在 `/healthz` 可访问后启动,不应再因为 Rust 监听未完成而代理失败。
5. 如果 `spacetime server ping` 打印 `Server could not be reached (502 Bad Gateway)`,即使命令退出码为 `0` 也必须视为未就绪;脚本会继续等待新启动实例,或在 root-dir 已被其他实例占用时输出占用进程。
编译警告治理:
@@ -93,6 +111,12 @@ npm run dev:rust:logs -- --follow
2. 仅供测试断言使用的辅助函数使用 `#[cfg(test)]` 限定,避免进入 `cargo run -p api-server` 的普通二进制编译。
3. 已无调用入口且无迁移价值的映射函数直接删除;如果后续新增同类 SpacetimeDB 记录映射,再按实际调用路径补回,避免提前保留死代码。
Maincloud API 重启补充:
1. `npm run api-server:maincloud` 会先读取 `.env``.env.local`,把 `GENARRATIVE_SPACETIME_MAINCLOUD_*` 映射为 `api-server` 使用的 `GENARRATIVE_SPACETIME_*`,再运行 `cargo run -p api-server --manifest-path server-rs/Cargo.toml`
2. Windows 下脚本会尽力停止本仓库 `server-rs/target/debug/api-server.exe` 对应的旧进程,避免 cargo 重新编译时 exe 被占用。
3. 旧进程已经退出或清理过程中出现瞬时等待失败时,不应阻断新的 `api-server` 启动;脚本只记录清理失败并继续启动。
## 3. Ubuntu 发布包脚本
入口:

View File

@@ -48,7 +48,7 @@ spacetime publish xushi-p4wfr --server http://127.0.0.1:3101 --module-path D:\Ge
publish 成功后,继续执行:
```bash
spacetime generate --no-config --lang rust --out-dir D:\Genarrative\server-rs\crates\spacetime-client\src\module_bindings --module-path D:\Genarrative\server-rs\crates\spacetime-module --include-private --yes
npm run spacetime:generate
```
要求:

View File

@@ -164,19 +164,18 @@ Stage 5 已接入 agent `publish_world` action但调用方仍需要显式提
本仓默认刷新命令:
```bash
spacetime generate --no-config --lang rust --out-dir server-rs/crates/spacetime-client/src/module_bindings --module-path server-rs/crates/spacetime-module --include-private --yes
npm run spacetime:generate
```
如果当前工作树里存在其他并行 `cargo build/test/check` 导致 `spacetime generate` 内部构建长期卡在锁竞争,则先在独立目标目录编译 wasm再使用 `--bin-path` 生成:
如果当前工作树里存在其他并行 `cargo build/test/check` 导致 `spacetime generate` 内部构建长期卡在锁竞争,则先结束占锁任务后重新执行仓库脚本;脚本内部会使用短临时目录生成公开 Rust bindings不生成私有表绑定。
```bash
CARGO_TARGET_DIR=server-rs/target-spacetime-bindgen cargo build --manifest-path server-rs/crates/spacetime-module/Cargo.toml --target wasm32-unknown-unknown --release
spacetime generate --no-config --lang rust --out-dir server-rs/crates/spacetime-client/src/module_bindings --bin-path server-rs/target-spacetime-bindgen/wasm32-unknown-unknown/release/spacetime_module.wasm --include-private --yes
npm run spacetime:generate
```
这样做的目的固定如下:
1. 避开根目录 `spacetime.json` 多 generate target 对单次 Rust 生成的参数冲突
1. 避开根目录 `spacetime.json` 误触发前端 bindings 或私有表 bindings 生成
2. 避开共享 `target/` 目录被其他任务占锁时的构建阻塞。
3. 保证 `create_custom_world_agent_session` / `get_custom_world_agent_session` 这类新 procedure 与输入输出类型能同步进入 Rust bindings。