拆分大文件
This commit is contained in:
@@ -28,6 +28,9 @@ GENARRATIVE_RUNTIME_SERVER_TARGET=""
|
||||
# and by the standalone Rust dev / deploy scripts.
|
||||
GENARRATIVE_API_PORT="3100"
|
||||
GENARRATIVE_API_TARGET="http://127.0.0.1:3100"
|
||||
GENARRATIVE_ADMIN_USERNAME=""
|
||||
GENARRATIVE_ADMIN_PASSWORD=""
|
||||
GENARRATIVE_ADMIN_TOKEN_TTL_SECONDS="14400"
|
||||
GENARRATIVE_INTERNAL_API_SECRET="CHANGE_ME_FOR_PRODUCTION"
|
||||
GENARRATIVE_SPACETIME_SERVER_URL="http://127.0.0.1:3001"
|
||||
GENARRATIVE_SPACETIME_DATABASE="genarrative-dev"
|
||||
|
||||
260
docs/technical/ADMIN_CONSOLE_SERVICE_DESIGN_2026-04-23.md
Normal file
260
docs/technical/ADMIN_CONSOLE_SERVICE_DESIGN_2026-04-23.md
Normal file
@@ -0,0 +1,260 @@
|
||||
# 后台管理服务设计
|
||||
|
||||
日期:`2026-04-23`
|
||||
|
||||
## 1. 目标
|
||||
|
||||
为当前 Rust `api-server` 增加一套同源后台管理服务,满足以下首版目标:
|
||||
|
||||
1. 支持管理员用户名密码登录。
|
||||
2. 支持独立的管理员鉴权,不允许普通玩家 JWT 越权访问。
|
||||
3. 支持在后台查看当前服务与数据库概览信息。
|
||||
4. 支持在后台测试当前 `api-server` 已挂载接口。
|
||||
5. 保持首版工程足够轻量,不新建额外独立服务进程,不引入第二套前端工程。
|
||||
|
||||
## 2. 背景与约束
|
||||
|
||||
当前仓库已具备:
|
||||
|
||||
1. Rust `api-server` 主链。
|
||||
2. 基于 JWT + refresh session 的普通用户登录体系。
|
||||
3. `SpacetimeDB + spacetime-client` 的主数据面。
|
||||
|
||||
本次后台管理服务必须继续遵守:
|
||||
|
||||
1. 后端统一落在 `server-rs`,不回退到 `server-node`。
|
||||
2. 不额外新起独立管理服务进程。
|
||||
3. 首版以“一个受保护管理域 + 一个同源后台页面”为落地形态。
|
||||
4. 数据库信息必须尽量读取真实数据库侧信息,不能只展示硬编码假数据。
|
||||
|
||||
## 3. 首版范围
|
||||
|
||||
### 3.1 包含
|
||||
|
||||
1. `GET /admin`:后台管理页面入口。
|
||||
2. `POST /admin/api/login`:管理员用户名密码登录。
|
||||
3. `GET /admin/api/me`:当前管理员会话信息。
|
||||
4. `GET /admin/api/overview`:服务与数据库概览。
|
||||
5. `POST /admin/api/debug/http`:受控 HTTP 接口调试。
|
||||
6. 基于 Bearer JWT 的管理员鉴权中间件。
|
||||
|
||||
### 3.2 不包含
|
||||
|
||||
1. 多角色管理员体系。
|
||||
2. 管理员 refresh cookie / 多端会话管理。
|
||||
3. 后台直接写库、删库、执行 reducer。
|
||||
4. 任意 SQL 执行器。
|
||||
5. 新建独立 React/Vite 管理端工程。
|
||||
|
||||
## 4. 总体方案
|
||||
|
||||
### 4.1 部署形态
|
||||
|
||||
后台管理服务直接挂载在现有 `server-rs/crates/api-server` 内,作为同一个 Axum 进程的一部分。
|
||||
|
||||
原因:
|
||||
|
||||
1. 当前 `api-server` 已具备配置、JWT、错误包裹、日志与同源路由能力。
|
||||
2. 后台本质上是服务运维与调试面,不值得单独再起一个网关或 BFF。
|
||||
3. 同源可以避免开发期额外 CORS 和 cookie 域问题。
|
||||
|
||||
### 4.2 页面形态
|
||||
|
||||
后台管理页面采用 `api-server` 直接返回一份内嵌 HTML/CSS/JS 的管理页。
|
||||
|
||||
原因:
|
||||
|
||||
1. 首版目标是“可用的后台能力”,不是新建一套复杂前端基建。
|
||||
2. 管理页面交互相对简单,直接内嵌更易随服务端一起部署。
|
||||
3. 可以避免新增构建链和静态资源发布路径。
|
||||
|
||||
### 4.3 数据库信息来源
|
||||
|
||||
数据库概览不走本地 CLI shell,也不依赖前端直接访问数据库。
|
||||
|
||||
首版采用两类信息源:
|
||||
|
||||
1. 服务端配置与连接信息:来自 `api-server` 当前 `AppConfig`。
|
||||
2. SpacetimeDB 真正的数据库元信息与表行数:由 `api-server` 通过 SpacetimeDB 官方 HTTP API 读取。
|
||||
|
||||
读取口径:
|
||||
|
||||
1. `/v1/database/{database}`:读取数据库基础信息。
|
||||
2. `/v1/database/{database}/schema`:读取 schema 信息。
|
||||
3. `/v1/database/{database}/sql`:对受控表执行 `SELECT COUNT(*)` 统计。
|
||||
|
||||
说明:
|
||||
|
||||
1. 首版只做只读概览,不暴露任意 SQL 输入。
|
||||
2. 表清单由后端显式维护,避免用户在后台拼接任意查询。
|
||||
|
||||
## 5. 管理员鉴权设计
|
||||
|
||||
### 5.1 管理员账号来源
|
||||
|
||||
首版不复用普通玩家账号仓储,不把管理员账号混进 `module-auth` 用户表。
|
||||
|
||||
管理员账号来自环境变量:
|
||||
|
||||
1. `GENARRATIVE_ADMIN_USERNAME`
|
||||
2. `GENARRATIVE_ADMIN_PASSWORD`
|
||||
|
||||
原因:
|
||||
|
||||
1. 管理员是平台运维身份,不等于玩家账号。
|
||||
2. 首版目标是尽快落地可靠后台,不引入额外管理员表迁移。
|
||||
3. 环境变量方案最适合当前阶段的单后台入口。
|
||||
|
||||
### 5.2 管理员 JWT
|
||||
|
||||
后台登录成功后签发独立管理员 Bearer JWT。
|
||||
|
||||
claims 设计:
|
||||
|
||||
1. 继续复用 `platform-auth::AccessTokenClaims`。
|
||||
2. `roles` 固定包含 `admin`。
|
||||
3. `sub` 使用稳定管理员主体,例如 `admin:<username>`。
|
||||
4. `sid` 使用后台会话 ID。
|
||||
5. 不写 refresh cookie。
|
||||
|
||||
### 5.3 权限校验
|
||||
|
||||
新增 `require_admin_auth` 中间件,校验规则如下:
|
||||
|
||||
1. Bearer token 必须可被当前 JWT 配置正确验签。
|
||||
2. `roles` 中必须包含 `admin`。
|
||||
3. `sub` 必须匹配当前管理员配置主体。
|
||||
|
||||
普通用户 token 即使同样由本服务签发,只要不带 `admin` 角色,也一律拒绝访问后台接口。
|
||||
|
||||
## 6. 后台页面设计
|
||||
|
||||
首版页面包含三个主区域:
|
||||
|
||||
1. 登录卡片。
|
||||
2. 数据库概览面板。
|
||||
3. API 调试面板。
|
||||
|
||||
交互原则:
|
||||
|
||||
1. 页面简洁,不默认塞说明性长文案。
|
||||
2. 移动端优先,窄屏下卡片改纵向堆叠。
|
||||
3. API 调试结果在独立结果面板展示,不在按钮下方临时插一段文本。
|
||||
|
||||
## 7. 数据库概览设计
|
||||
|
||||
`GET /admin/api/overview` 返回以下信息:
|
||||
|
||||
1. 当前服务监听信息。
|
||||
2. 当前 `SpacetimeDB server/database` 配置。
|
||||
3. `SpacetimeDB` 数据库基础信息。
|
||||
4. 当前 schema 表清单。
|
||||
5. 首批关键表的行数统计。
|
||||
|
||||
首批关键表固定覆盖:
|
||||
|
||||
1. `runtime_setting`
|
||||
2. `runtime_snapshot`
|
||||
3. `user_browse_history`
|
||||
4. `profile_dashboard_state`
|
||||
5. `profile_wallet_ledger`
|
||||
6. `profile_played_world`
|
||||
7. `profile_save_archive`
|
||||
8. `story_session`
|
||||
9. `story_event`
|
||||
10. `battle_state`
|
||||
11. `inventory_slot`
|
||||
12. `quest_record`
|
||||
13. `quest_log`
|
||||
14. `treasure_record`
|
||||
15. `npc_state`
|
||||
16. `custom_world_profile`
|
||||
17. `custom_world_gallery_entry`
|
||||
18. `custom_world_agent_session`
|
||||
19. `custom_world_agent_message`
|
||||
20. `custom_world_agent_operation`
|
||||
21. `custom_world_draft_card`
|
||||
22. `big_fish_creation_session`
|
||||
23. `big_fish_agent_message`
|
||||
24. `big_fish_asset_slot`
|
||||
25. `big_fish_runtime_run`
|
||||
26. `puzzle_work_profile`
|
||||
27. `puzzle_agent_session`
|
||||
28. `puzzle_agent_message`
|
||||
29. `puzzle_runtime_run`
|
||||
30. `ai_task`
|
||||
31. `ai_task_stage`
|
||||
32. `ai_text_chunk`
|
||||
33. `ai_result_reference`
|
||||
34. `asset_object`
|
||||
35. `asset_entity_binding`
|
||||
|
||||
返回中的计数失败项必须带错误信息,不能静默吞掉。
|
||||
|
||||
## 8. API 调试设计
|
||||
|
||||
`POST /admin/api/debug/http` 提供一个受控 HTTP 调试代理。
|
||||
|
||||
请求参数:
|
||||
|
||||
1. `method`
|
||||
2. `path`
|
||||
3. `headers`
|
||||
4. `body`
|
||||
|
||||
限制:
|
||||
|
||||
1. 只允许访问当前服务同源相对路径。
|
||||
2. 调试回环地址由服务端按当前 `bind_host` 解析;若服务监听在 `0.0.0.0` 或 `::`,后台自动改走 loopback,避免把通配监听地址直接当成调试目标。
|
||||
2. 禁止调 `/admin/api/login` 本身,避免自套娃。
|
||||
3. 禁止覆盖 `host`、`content-length` 等危险头。
|
||||
4. 请求超时固定收口。
|
||||
5. 返回调试结果时回显状态码、响应头、响应文本预览。
|
||||
|
||||
该能力用于验证当前服务端接口,不等价于通用代理工具。
|
||||
|
||||
## 9. 配置项
|
||||
|
||||
新增以下环境变量:
|
||||
|
||||
1. `GENARRATIVE_ADMIN_USERNAME`
|
||||
2. `GENARRATIVE_ADMIN_PASSWORD`
|
||||
3. `GENARRATIVE_ADMIN_TOKEN_TTL_SECONDS`
|
||||
|
||||
默认策略:
|
||||
|
||||
1. 若未配置用户名或密码,则后台登录接口返回 `503`,后台页面显示“后台未启用”。
|
||||
2. 默认管理员 token TTL 为 `4` 小时。
|
||||
|
||||
## 10. 测试要求
|
||||
|
||||
至少覆盖:
|
||||
|
||||
1. 管理员登录成功。
|
||||
2. 管理员密码错误返回 `401`。
|
||||
3. 普通用户 token 访问后台接口返回 `403`。
|
||||
4. 未登录访问后台接口返回 `401`。
|
||||
5. 后台概览接口在未启用管理员配置时返回 `503`。
|
||||
6. API 调试接口能成功访问 `/healthz`。
|
||||
7. API 调试接口拒绝绝对 URL 和后台自身登录接口。
|
||||
|
||||
## 11. 路由清单
|
||||
|
||||
首版新增路由:
|
||||
|
||||
1. `GET /admin`
|
||||
2. `POST /admin/api/login`
|
||||
3. `GET /admin/api/me`
|
||||
4. `GET /admin/api/overview`
|
||||
5. `POST /admin/api/debug/http`
|
||||
|
||||
## 12. 完成定义
|
||||
|
||||
满足以下条件时,本任务视为完成:
|
||||
|
||||
1. `api-server` 内存在受保护后台管理域。
|
||||
2. 管理员用户名密码可登录。
|
||||
3. 普通用户 token 无法访问后台接口。
|
||||
4. 后台能看到服务和数据库真实概览。
|
||||
5. 后台能调试当前服务 HTTP 接口。
|
||||
6. 路由索引与技术文档已同步更新。
|
||||
@@ -23,7 +23,9 @@ RPG 创作结果页已经能看到完整草稿内容,但页面底部仍然持
|
||||
2. 场景章节主链字段为 `sceneChapterBlueprints`
|
||||
3. `settingText` 也会承载世界总体一句话设定
|
||||
|
||||
但 `server-rs/crates/spacetime-module/src/custom_world/mod.rs` 中的 `summarize_publish_gate_from_json(...)` 仍只检查旧字段:
|
||||
问题最初在拆分后的 `server-rs/crates/spacetime-module/src/custom_world/mod.rs` 中被修过一版,但当前线上实际执行入口仍保留在 `server-rs/crates/spacetime-module/src/lib.rs`。
|
||||
|
||||
也就是说,真正参与 Agent session snapshot、结果页 publish gate 刷新和 `publish_world` 动作校验的,仍然是 `lib.rs` 里的历史实现;而它还只检查旧字段:
|
||||
|
||||
1. `worldHook`
|
||||
2. `playerPremise`
|
||||
@@ -36,11 +38,13 @@ RPG 创作结果页已经能看到完整草稿内容,但页面底部仍然持
|
||||
2. 发布门槛检查读的是旧字段
|
||||
3. 同一个草稿在 UI 看起来“已经有内容”,但 gate 仍然误判为缺失
|
||||
|
||||
此外,正式发布编译在把 session draft 编译成发布 profile 时,也只把 `sceneChapters` 映射为 `sceneChapterBlueprints`,没有兼容当前更常见的 `sceneChapterBlueprints` 输入。
|
||||
因此会出现“拆分模块里的代码已经对齐,但页面实际 blocker 仍然不消失”的假象。
|
||||
|
||||
此外,`lib.rs` 里的最小草稿兜底结构也没有补上 `sceneChapterBlueprints` 默认槽位,导致部分恢复、回滚和草稿兜底链路继续偏向旧 schema。
|
||||
|
||||
## 3. 修复策略
|
||||
|
||||
本轮统一把发布门槛与发布编译对齐到当前前端主链 schema:
|
||||
本轮统一把实际入口 `server-rs/crates/spacetime-module/src/lib.rs` 的发布门槛与最小草稿结构对齐到当前前端主链 schema:
|
||||
|
||||
1. `world hook` 检查同时兼容:
|
||||
- `worldHook`
|
||||
@@ -60,11 +64,12 @@ RPG 创作结果页已经能看到完整草稿内容,但页面底部仍然持
|
||||
4. 主线第一幕检查优先读取:
|
||||
- `sceneChapterBlueprints[*].acts`
|
||||
- `sceneChapters[*].acts`
|
||||
5. 发布编译时,`sceneChapterBlueprints` 与旧 `sceneChapters` 都能写入最终 profile。
|
||||
5. 最小草稿兜底结构同时补上 `sceneChapterBlueprints` 空数组,避免恢复链路重新回落到旧字段集合。
|
||||
|
||||
## 4. 验收标准
|
||||
|
||||
1. 结果页已包含 `anchorContent / creatorIntent / sceneChapterBlueprints` 的草稿,不再被旧 blocker 误判。
|
||||
2. `publishReady` 会随当前 session 最新 preview 正确刷新。
|
||||
3. “发布并进入世界”在 blocker 清空后恢复可点击。
|
||||
4. 正式发布后的 compiled profile 仍保留 `sceneChapterBlueprints`。
|
||||
4. `ensure_minimal_draft_profile(...)` 生成的兜底草稿也包含 `sceneChapterBlueprints`。
|
||||
5. 新增 Rust 单测,覆盖“当前 Agent 结果 schema 不应再误报 blocker”与“最小草稿必须保留 `sceneChapterBlueprints` 默认槽位”。
|
||||
|
||||
@@ -0,0 +1,175 @@
|
||||
# Custom World `draft_foundation` 迁移到 `api-server + platform-llm` 方案
|
||||
|
||||
日期:`2026-04-23`
|
||||
|
||||
## 1. 背景
|
||||
|
||||
当前 RPG 创作 Agent 的 `draft_foundation` 虽然已经能把会话推进到结果页,但真实执行位置仍在 `spacetime-module` 的 `execute_draft_foundation_action(...)`。
|
||||
|
||||
这条链路的问题是:
|
||||
|
||||
1. `draft_foundation` 没有走 `platform-llm`。
|
||||
2. SpacetimeDB reducer 内部自己从 `seed_text / session.draft_profile_json` 兜底拼草稿,属于规则编译,不是“真实 LLM 生成”。
|
||||
3. reducer 按 SpacetimeDB 约束不应承担外部网络副作用,因此“让 reducer 里直接调 LLM”本身也是错误方向。
|
||||
|
||||
验证清单第三项要求是:
|
||||
|
||||
1. 草稿编译需要真实走 LLM。
|
||||
2. 不能再用本地占位 compile 去冒充真实生成。
|
||||
|
||||
因此这条链必须改成:
|
||||
|
||||
```text
|
||||
前端 action
|
||||
-> api-server 接收 draft_foundation
|
||||
-> platform-llm 真实生成 foundation draft
|
||||
-> spacetime-client 调用 SpacetimeDB action/procedure 写回 session / card / gate / preview
|
||||
-> 前端继续通过 operation 轮询完成态
|
||||
```
|
||||
|
||||
## 2. 本轮目标
|
||||
|
||||
本轮只解决第三项验证要求最核心的问题:
|
||||
|
||||
1. `draft_foundation` 的草稿生成必须在 `api-server` 中完成。
|
||||
2. `api-server` 必须真实调用 `platform-llm`。
|
||||
3. `spacetime-module` 只负责:
|
||||
- 校验 action 执行条件
|
||||
- 落库 session / draft card / checkpoint / publish gate / result preview
|
||||
4. 前端协议尽量不变,继续保留:
|
||||
- `POST /api/runtime/custom-world/agent/sessions/:sessionId/actions`
|
||||
- `GET /api/runtime/custom-world/agent/sessions/:sessionId/operations/:operationId`
|
||||
|
||||
本轮不做:
|
||||
|
||||
1. 把旧 Node 的 foundation draft 全量多阶段 pipeline 一次性 1:1 搬到 Rust。
|
||||
2. 额外新增前端 action 接口。
|
||||
3. 在 SpacetimeDB 内新增“可联网 procedure”去直接调 LLM。
|
||||
4. 把 `legacyResultProfile` 兼容双重编译一起迁回主链。
|
||||
|
||||
## 3. 迁移后的职责边界
|
||||
|
||||
### 3.1 `api-server`
|
||||
|
||||
负责:
|
||||
|
||||
1. 识别 `draft_foundation` action。
|
||||
2. 读取当前 session snapshot。
|
||||
3. 基于真实 `seed_text` 与 `anchor_content / creator_intent / anchor_pack / draft_profile` 组织 foundation draft prompt。
|
||||
4. 调用 `platform-llm::LlmClient` 获取首版草稿 JSON。
|
||||
5. 做最小字段归一化,保证至少满足当前 `publish gate / result preview` 所需字段。
|
||||
6. 把生成结果作为 `payload_json.draftProfile` 传给 `spacetime-client.execute_custom_world_agent_action(...)`。
|
||||
|
||||
### 3.2 `spacetime-module`
|
||||
|
||||
负责:
|
||||
|
||||
1. 校验 session 是否允许执行 `draft_foundation`。
|
||||
2. 校验 payload 中必须带有外部已生成的 `draftProfile`。
|
||||
3. 把 `draftProfile` 写入:
|
||||
- `custom_world_agent_session.draft_profile_json`
|
||||
- `custom_world_draft_card`
|
||||
- `publish_gate_json`
|
||||
- `result_preview_json`
|
||||
- `checkpoints_json`
|
||||
- `custom_world_agent_message`
|
||||
- `custom_world_agent_operation`
|
||||
4. 不再自己从 `seed_text` 兜底编译 `draftProfile`。
|
||||
5. 不再对 `draft_foundation` 的外部 `draftProfile` 做二次补全编译,避免责任边界重新漂回 SpacetimeDB。
|
||||
|
||||
### 3.3 `platform-llm`
|
||||
|
||||
负责:
|
||||
|
||||
1. 提供统一文本模型网关。
|
||||
2. 返回 foundation draft JSON 文本。
|
||||
|
||||
## 4. 最小实现策略
|
||||
|
||||
## 4.1 先保留当前 action / operation 协议
|
||||
|
||||
前端现在的行为是:
|
||||
|
||||
1. `POST /actions` 拿到一个 `operation`
|
||||
2. 进入“世界草稿生成进度”页
|
||||
3. 轮询 `GET /operations/:operationId`
|
||||
4. operation 完成后拉最新 session
|
||||
|
||||
因此本轮不改协议,只改服务端编排。
|
||||
|
||||
## 4.2 `draft_foundation` 的执行口径
|
||||
|
||||
`api-server` 接收到 `draft_foundation` 时:
|
||||
|
||||
1. 先读取当前 session。
|
||||
2. 必须使用 session 中真实的 `seed_text` 与当前锚点组织 prompt,不能误把 `session_id` 当作 seed 传给 LLM。
|
||||
3. 若 `progressPercent < 100`,直接返回错误。
|
||||
4. 调 `platform-llm` 生成 `draftProfile`。
|
||||
5. 用当前时间戳作为 action 提交时间。
|
||||
6. 调 `spacetime-client.execute_custom_world_agent_action(...)`,把 `draftProfile` 放进 payload。
|
||||
7. 返回 SpacetimeDB 已落库的 operation。
|
||||
|
||||
首版保持同步完成,不额外引入新的 action finalize procedure。
|
||||
|
||||
原因:
|
||||
|
||||
1. 这样改动范围最小。
|
||||
2. 已满足“LLM 在 api-server、SpacetimeDB 只负责落库”的验证要求。
|
||||
3. 前端没有全局短超时,本轮可先接受单次 action 等待 LLM 返回。
|
||||
|
||||
如果后续需要更强的可观测性和更长耗时容忍,再把这条链拆成 submit/finalize 两段式后台任务。
|
||||
|
||||
## 4.3 foundation draft 的最小字段要求
|
||||
|
||||
本轮生成结果至少保证以下字段存在:
|
||||
|
||||
1. `name`
|
||||
2. `subtitle`
|
||||
3. `summary`
|
||||
4. `worldHook`
|
||||
5. `playerPremise`
|
||||
6. `coreConflicts`
|
||||
7. `playableNpcs`
|
||||
8. `storyNpcs`
|
||||
9. `landmarks`
|
||||
10. `chapters`
|
||||
11. `sceneChapterBlueprints`
|
||||
|
||||
这样可以直接满足当前 Rust `publish gate` 的最小校验,不会再次出现:
|
||||
|
||||
1. 草稿明明生成了
|
||||
2. 但结果页仍然提示缺少 world hook / player premise / 主线章节 / 第一幕
|
||||
|
||||
## 5. 与旧 Node foundation draft 服务的关系
|
||||
|
||||
旧 Node 版本已经证明下面几点是成立的:
|
||||
|
||||
1. foundation draft 必须由后端调用真实 LLM。
|
||||
2. foundation draft 与 preview compiler 应该拆边界。
|
||||
3. `legacyResultProfile` 不应继续主导草稿主字段。
|
||||
|
||||
Rust 首版沿用这些结论,但不要求一次性照搬旧 Node 的全部多阶段拆分。
|
||||
|
||||
本轮只迁移:
|
||||
|
||||
1. “真实 LLM 生成 draft 主字段”这条主要求。
|
||||
2. “结果落库由 SpacetimeDB 负责”这条边界。
|
||||
|
||||
## 6. 验收标准
|
||||
|
||||
满足以下条件时,这次迁移视为完成:
|
||||
|
||||
1. `draft_foundation` 从 `api-server` 真实调用 `platform-llm`。
|
||||
2. `spacetime-module` 中 `draft_foundation` 不再允许无 `draftProfile` 自行兜底编译。
|
||||
3. 点击“生成游戏设定草稿”后,session 的 `draft_profile_json / publish_gate_json / result_preview_json` 正常写回。
|
||||
4. 结果页不再因为缺少最小底稿字段而错误阻断。
|
||||
5. 定向测试通过。
|
||||
6. 编码检查通过。
|
||||
|
||||
## 7. 相关文件
|
||||
|
||||
1. `server-rs/crates/api-server/src/custom_world.rs`
|
||||
2. `server-rs/crates/api-server/src/custom_world_foundation_draft.rs`
|
||||
3. `server-rs/crates/spacetime-module/src/lib.rs`
|
||||
4. `server-rs/crates/module-custom-world/src/lib.rs`
|
||||
5. `docs/technical/SPACETIMEDB_CUSTOM_WORLD_WORKS_AND_AGENT_EXTENSION_STAGE9_DESIGN_2026-04-22.md`
|
||||
@@ -0,0 +1,331 @@
|
||||
# 公开编号用户搜索与广场作品搜索设计
|
||||
|
||||
## 1. 背景
|
||||
|
||||
当前前端展示的“叙世号”由前端基于 `AuthUser.id` 临时拼装:
|
||||
|
||||
- 前缀固定为 `SY-`
|
||||
- 取 `user.id` 或 `username` 去除非字母数字字符后的末 8 位
|
||||
- 不足 8 位左侧补零
|
||||
|
||||
该方案只适合展示,不适合作为正式检索键,主要问题:
|
||||
|
||||
1. 它不是后端持久化字段,前后端对同一编号没有统一语义。
|
||||
2. 它依赖 `user.id` 当前格式,一旦账号 ID 生成规则调整,展示号会漂移。
|
||||
3. 只截取末 8 位存在潜在碰撞风险,不适合作为用户搜索主键。
|
||||
4. 广场作品当前仅能通过 `ownerUserId + profileId` 读取详情,不利于做公开搜索和分享。
|
||||
|
||||
本次目标是把“公开编号”升级为后端一等字段,并同时支持:
|
||||
|
||||
1. 按用户公开编号搜索用户
|
||||
2. 按作品公开编号搜索广场作品
|
||||
3. 前端统一展示后端返回的公开编号,不再本地拼接
|
||||
|
||||
## 2. 目标与非目标
|
||||
|
||||
### 2.1 目标
|
||||
|
||||
1. 为用户增加稳定唯一的公开编号 `public_user_code`
|
||||
2. 为发布到广场的作品增加稳定唯一的公开编号 `public_work_code`
|
||||
3. 提供匿名可读的公开搜索接口
|
||||
4. 平台首页 / 广场搜索框支持输入公开编号直达用户或作品
|
||||
5. 搜索兼容用户输入的不同格式,如大小写、带不带前缀、是否包含空格
|
||||
|
||||
### 2.2 非目标
|
||||
|
||||
1. 本期不实现复杂全文搜索排序系统
|
||||
2. 本期不做“用户主页”完整社交系统,只返回搜索命中的公开资料摘要
|
||||
3. 本期不把所有广场列表改造成关键词后端分页搜索
|
||||
4. 本期不修改既有业务主键 `user.id / profile_id`
|
||||
|
||||
## 3. 核心设计原则
|
||||
|
||||
1. **公开编号必须后端生成并持久化**
|
||||
2. **公开编号只做公开检索键,不替代内部主键**
|
||||
3. **前端只展示和透传公开编号,不自行拼装**
|
||||
4. **公开接口只暴露最小必要公开信息**
|
||||
5. **SpacetimeDB 查询走唯一索引或明确索引,不做无界扫描**
|
||||
|
||||
## 4. 数据模型设计
|
||||
|
||||
## 4.1 用户公开编号
|
||||
|
||||
在认证用户模型中新增字段:
|
||||
|
||||
- `public_user_code: String`
|
||||
|
||||
格式定义:
|
||||
|
||||
- 标准展示格式:`SY-00000001`
|
||||
- 前缀固定:`SY-`
|
||||
- 数字部分固定 8 位,左侧补零
|
||||
|
||||
生成规则:
|
||||
|
||||
1. 新建账号时,从认证存储中的 `next_user_id` 派生
|
||||
2. 若用户内部 ID 为 `user_{:08}`,则公开编号同步使用同一序号生成
|
||||
3. 一旦生成后永久不变
|
||||
|
||||
示例:
|
||||
|
||||
- `user_00000001` -> `SY-00000001`
|
||||
- `user_00001234` -> `SY-00001234`
|
||||
|
||||
原因:
|
||||
|
||||
1. 与当前展示习惯兼容,用户认知成本最低
|
||||
2. 不再依赖前端截断逻辑
|
||||
3. 可在后端保证唯一性与稳定性
|
||||
|
||||
## 4.2 广场作品公开编号
|
||||
|
||||
在作品真相模型与广场快照中分别新增字段:
|
||||
|
||||
- `CustomWorldProfile.public_work_code: String`
|
||||
- `CustomWorldProfile.author_public_user_code: String`
|
||||
|
||||
在广场作品快照模型 `CustomWorldGalleryEntry` 中新增字段:
|
||||
|
||||
- `public_work_code: String`
|
||||
- `author_public_user_code: String`
|
||||
|
||||
格式定义:
|
||||
|
||||
- 标准展示格式:`CW-00000001`
|
||||
- 前缀固定:`CW-`
|
||||
- 数字部分固定 8 位,左侧补零
|
||||
|
||||
生成规则:
|
||||
|
||||
1. 作品第一次发布到广场时分配
|
||||
2. 编号先写入 `CustomWorldProfile` 真相表,再同步到 `CustomWorldGalleryEntry`
|
||||
3. 同一 `profile_id` 重复发布、更新、重新上架时沿用原编号
|
||||
4. 删除后不回收编号
|
||||
|
||||
原因:
|
||||
|
||||
1. 作品公开检索和分享应使用作品级稳定编号,而不是 `ownerUserId + profileId`
|
||||
2. 同一作品反复编辑发布不应导致公开编号变化
|
||||
|
||||
## 4.3 归一化规则
|
||||
|
||||
用户与作品公开编号搜索都应在后端统一做归一化,前端仅做轻提示,不做最终判定。
|
||||
|
||||
### 用户编号归一化
|
||||
|
||||
输入样例:
|
||||
|
||||
- `SY-00000001`
|
||||
- `sy00000001`
|
||||
- `00000001`
|
||||
- ` sy-00000001 `
|
||||
|
||||
归一化步骤:
|
||||
|
||||
1. 去掉首尾空白
|
||||
2. 转大写
|
||||
3. 去掉所有非字母数字字符
|
||||
4. 若结果以 `SY` 开头,则去掉前缀
|
||||
5. 剩余部分必须为 1~8 位数字
|
||||
6. 左侧补零到 8 位
|
||||
7. 最终重建为标准格式 `SY-XXXXXXXX`
|
||||
|
||||
### 作品编号归一化
|
||||
|
||||
输入样例:
|
||||
|
||||
- `CW-00000001`
|
||||
- `cw00000001`
|
||||
- `00000001`
|
||||
|
||||
归一化步骤:
|
||||
|
||||
1. 去掉首尾空白
|
||||
2. 转大写
|
||||
3. 去掉所有非字母数字字符
|
||||
4. 若结果以 `CW` 开头,则去掉前缀
|
||||
5. 剩余部分必须为 1~8 位数字
|
||||
6. 左侧补零到 8 位
|
||||
7. 最终重建为标准格式 `CW-XXXXXXXX`
|
||||
|
||||
## 5. 后端接口设计
|
||||
|
||||
## 5.1 用户公开编号搜索
|
||||
|
||||
新增匿名可读接口:
|
||||
|
||||
- `GET /api/auth/public-users/by-code/{code}`
|
||||
- `GET /api/auth/public-users/by-id/{userId}`
|
||||
|
||||
响应:
|
||||
|
||||
```json
|
||||
{
|
||||
"user": {
|
||||
"id": "user_00000001",
|
||||
"publicUserCode": "SY-00000001",
|
||||
"displayName": "旅人一号"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
说明:
|
||||
|
||||
1. `id` 返回内部 ID 仅供当前工程内部跳转与资源读取使用,不在 UI 上直接暴露为文案
|
||||
2. 不返回手机号、登录方式、绑定状态、tokenVersion 等敏感字段
|
||||
3. 未命中返回 `404`
|
||||
4. `by-id` 仅接受内部 `user_XXXXXXXX` 这类用户 ID,用于工程内跳转、运营排查或已有资源引用,不替代公开叙世号主搜索语义
|
||||
|
||||
## 5.2 广场作品公开编号搜索
|
||||
|
||||
新增匿名可读接口:
|
||||
|
||||
- `GET /api/runtime/custom-world-gallery/by-code/{code}`
|
||||
|
||||
响应结构与现有广场详情接口一致:
|
||||
|
||||
```json
|
||||
{
|
||||
"entry": {
|
||||
"ownerUserId": "user_00000001",
|
||||
"profileId": "world-public-1",
|
||||
"publicWorkCode": "CW-00000001",
|
||||
"publicUserCode": "SY-00000001",
|
||||
"authorDisplayName": "旅人一号",
|
||||
"worldName": "雾港旧梦"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
说明:
|
||||
|
||||
1. 作品搜索命中后仍使用现有详情页承载
|
||||
2. 详情返回里补入 `publicWorkCode` 和作者 `publicUserCode`
|
||||
|
||||
## 6. SpacetimeDB 与认证存储改造
|
||||
|
||||
## 6.1 认证域
|
||||
|
||||
认证域目前是 `module-auth` 内存存储,不是 SpacetimeDB 表。
|
||||
|
||||
需要改造:
|
||||
|
||||
1. `AuthUser` 增加 `public_user_code`
|
||||
2. `create_user / create_phone_user / create_pending_wechat_user` 统一生成公开编号
|
||||
3. `AuthUserPayload` / 前端 `AuthUser` 合约增加 `publicUserCode`
|
||||
4. `auth/me`、密码登录、手机登录、微信登录、绑手机返回都补齐该字段
|
||||
|
||||
## 6.2 广场作品表
|
||||
|
||||
`CustomWorldProfile` 增加:
|
||||
|
||||
1. `public_work_code: String`
|
||||
2. `author_public_user_code: String`
|
||||
|
||||
`CustomWorldGalleryEntry` 增加:
|
||||
|
||||
1. `public_work_code: String`
|
||||
2. `author_public_user_code: String`
|
||||
|
||||
索引建议:
|
||||
|
||||
1. `public_work_code` 唯一索引
|
||||
2. 保留现有 `owner_user_id` 索引
|
||||
3. `profile_id` 仍作为主键 / 唯一查找键之一
|
||||
|
||||
其中 `author_public_user_code` 本期可先不建索引,除非后续明确需要“按用户公开号列出该作者作品”。
|
||||
|
||||
## 6.3 作品公开编号分配策略
|
||||
|
||||
需要在模块内新增稳定计数状态,例如:
|
||||
|
||||
- `gallery_public_work_counter`
|
||||
|
||||
要求:
|
||||
|
||||
1. 每次首次发布作品时分配一次
|
||||
2. 若作品已存在公开编号,则从 `CustomWorldProfile` 直接复用并同步到广场快照
|
||||
3. 不因下架、删除、重新发布而重新编号
|
||||
|
||||
## 7. 前端交互设计
|
||||
|
||||
## 7.1 账号展示
|
||||
|
||||
当前首页资料卡和桌面顶部都展示前端拼装叙世号,改为:
|
||||
|
||||
1. 直接展示 `authUi.user.publicUserCode`
|
||||
2. 复制按钮复制后端返回值
|
||||
3. 若字段缺失才进入兼容兜底逻辑,但兼容逻辑仅作过渡
|
||||
|
||||
## 7.2 广场作品卡
|
||||
|
||||
广场作品卡和详情页增加:
|
||||
|
||||
1. 作品号 `CW-XXXXXXXX`
|
||||
2. 作者叙世号 `SY-XXXXXXXX`
|
||||
|
||||
展示要求:
|
||||
|
||||
1. 以轻量辅助信息形式出现
|
||||
2. 不在卡片默认堆过多说明文字
|
||||
3. 移动端优先,避免挤压主要标题和摘要
|
||||
|
||||
## 7.3 搜索入口
|
||||
|
||||
平台首页顶部搜索框当前只是静态文案,需要接成真实输入与行为:
|
||||
|
||||
1. 当输入命中 `SY` 格式时,优先走用户公开号搜索
|
||||
2. 当输入命中 `CW` 格式时,优先走作品公开号搜索
|
||||
3. 当输入纯 1~8 位数字时:
|
||||
- 先尝试作品号
|
||||
- 未命中再尝试用户号
|
||||
4. 暂不命中公开编号格式时,保持当前占位,不在本期强做全文关键词搜索
|
||||
|
||||
用户搜索命中后的最小行为:
|
||||
|
||||
1. 打开独立用户搜索结果面板或对话框
|
||||
2. 展示头像字母、显示名、叙世号
|
||||
3. 提供“查看该作者作品”入口
|
||||
|
||||
作品搜索命中后的行为:
|
||||
|
||||
1. 直接打开广场作品详情
|
||||
|
||||
## 8. 安全与边界
|
||||
|
||||
1. 用户公开搜索接口只允许返回公开资料摘要
|
||||
2. 不允许通过公开接口枚举登录方式、绑定状态、设备信息、手机号掩码
|
||||
3. 作品公开搜索接口只返回已公开发布的作品
|
||||
4. 对不存在的编号统一返回未找到,避免泄露更多状态差异
|
||||
|
||||
## 9. 实现步骤
|
||||
|
||||
1. 改 `module-auth` 用户模型与返回合约,补 `public_user_code`
|
||||
2. 改 `shared-contracts / packages/shared`,前后端统一 `publicUserCode`
|
||||
3. 改首页展示,去掉前端本地拼装依赖
|
||||
4. 改 SpacetimeDB `CustomWorldGalleryEntry` 表与快照,补 `public_work_code / author_public_user_code`
|
||||
5. 新增广场按公开作品号读取 procedure / api-server 路由
|
||||
6. 新增认证域按公开用户号读取接口
|
||||
7. 改平台首页搜索框,支持按公开编号跳转
|
||||
8. 补测试:
|
||||
- 公开编号归一化
|
||||
- 用户搜索命中/未命中
|
||||
- 作品搜索命中/未命中
|
||||
- 前端展示与复制
|
||||
|
||||
## 10. 验收标准
|
||||
|
||||
1. 新注册、游客自动登录、手机号登录、微信登录用户都能拿到稳定 `publicUserCode`
|
||||
2. 首页与账户入口展示统一使用后端 `publicUserCode`
|
||||
3. 新发布广场作品自动获得稳定 `publicWorkCode`
|
||||
4. 输入 `SY-00000001` 可命中对应用户
|
||||
5. 输入 `CW-00000001` 可命中对应广场作品并打开详情
|
||||
6. 输入 `00000001` 时仍能归一化识别并命中
|
||||
7. 既有广场列表、详情和发布流程不回归
|
||||
|
||||
## 11. 当前落地说明
|
||||
|
||||
1. 首页叙世号展示已优先读取后端 `publicUserCode`,原本基于 `AuthUser.id/username` 的前端拼装仅保留为兼容兜底,避免老会话未刷新时界面直接空白。
|
||||
2. 用户公开搜索与广场作品公开搜索均已改为调用后端匿名接口,前端只负责输入、展示与跳转,不再自行决定最终编号格式。
|
||||
3. 自定义世界发布链路已改为从认证服务读取真实 `public_user_code` 写入作品真相与广场读模型,不再从内部 `user_id` 临时反推 `SY-XXXXXXXX`。
|
||||
4. 当前作品号 `public_work_code` 仍采用基于 `profile_id` 的稳定 fallback 方案生成 `CW-XXXXXXXX`;若后续补独立计数表,需要在不改变读写接口的前提下替换生成来源。
|
||||
230
docs/technical/PUZZLE_AGENT_LLM_REPLY_INTEGRATION_2026-04-23.md
Normal file
230
docs/technical/PUZZLE_AGENT_LLM_REPLY_INTEGRATION_2026-04-23.md
Normal file
@@ -0,0 +1,230 @@
|
||||
# 拼图 Agent 聊天接入大模型设计
|
||||
|
||||
日期:`2026-04-23`
|
||||
|
||||
## 1. 背景
|
||||
|
||||
当前拼图创作链已经具备:
|
||||
|
||||
1. `PuzzleAgentWorkspace` 前端聊天壳层
|
||||
2. Rust `api-server` 的 `submit / stream / action` HTTP facade
|
||||
3. `spacetime-module` 的 `puzzle_agent_session / puzzle_agent_message` 真相表
|
||||
4. `module-puzzle` 的锚点推断、草稿编译、发布校验与运行态规则
|
||||
|
||||
但 `submit_puzzle_agent_message` 仍然是 deterministic 占位逻辑:
|
||||
|
||||
1. 用户发言后,`spacetime-module` 直接推断 `anchor_pack`
|
||||
2. 同一事务里直接写入固定 assistant 总结文案
|
||||
3. SSE `/messages/stream` 只是把 `last_assistant_reply` 一次性回放给前端
|
||||
|
||||
这导致拼图 Agent 看起来有聊天面板,但没有真实 LLM 共创能力。
|
||||
|
||||
## 2. 目标
|
||||
|
||||
本轮只恢复拼图 Agent 聊天主链的真实 LLM 接入,不扩到图片模型和复杂多阶段编排:
|
||||
|
||||
1. 用户发消息后,assistant 回复必须来自 LLM
|
||||
2. LLM 输出必须同时产出:
|
||||
- `replyText`
|
||||
- `progressPercent`
|
||||
- `nextAnchorPack`
|
||||
3. SSE `reply_delta` 必须来自真实流式增量解析
|
||||
4. finalize 后一次性回写最新 session 真相
|
||||
5. LLM 不可用或解析失败时,不再写固定 assistant 假回复
|
||||
|
||||
## 3. 分层边界
|
||||
|
||||
### 3.1 `module-puzzle`
|
||||
|
||||
只负责:
|
||||
|
||||
1. `PuzzleAnchorPack`、`PuzzleCreatorIntent`、`PuzzleResultDraft` 纯领域模型
|
||||
2. 锚点清洗、草稿编译、发布校验
|
||||
3. 运行态拼图规则
|
||||
|
||||
明确不负责:
|
||||
|
||||
1. 网络请求
|
||||
2. LLM prompt 组织
|
||||
3. SSE 推流
|
||||
|
||||
### 3.2 `spacetime-module`
|
||||
|
||||
只负责:
|
||||
|
||||
1. `submit_puzzle_agent_message`
|
||||
- 校验 session / ownership / message id
|
||||
- 只写入 user message
|
||||
- 不再直接写 assistant message
|
||||
- 不再直接推进 `current_turn / progress_percent / anchor_pack_json`
|
||||
2. `finalize_puzzle_agent_message_turn`
|
||||
- 追加 assistant message
|
||||
- 覆盖 `anchor_pack_json`
|
||||
- 回写 `current_turn / progress_percent / stage / last_assistant_reply / updated_at`
|
||||
|
||||
### 3.3 `api-server`
|
||||
|
||||
负责:
|
||||
|
||||
1. 读取拼图 session 快照
|
||||
2. 组织 LLM prompt
|
||||
3. 流式截取 `replyText`
|
||||
4. 回合结束后组装 finalize 输入
|
||||
5. 调用 SpacetimeDB finalize procedure
|
||||
|
||||
## 4. 目标链路
|
||||
|
||||
### 4.1 阶段 A:提交消息
|
||||
|
||||
`submit_puzzle_agent_message`
|
||||
|
||||
职责:
|
||||
|
||||
1. 写入 user message
|
||||
2. 返回 submit 后的 session 快照
|
||||
3. 该快照中尚未新增 assistant message
|
||||
4. 该快照中的 `anchorPack / progressPercent / currentTurn` 保持提交前真相
|
||||
|
||||
### 4.2 阶段 B:完成单轮推理
|
||||
|
||||
`finalize_puzzle_agent_message_turn`
|
||||
|
||||
职责:
|
||||
|
||||
1. 追加 assistant message
|
||||
2. 回写:
|
||||
- `current_turn`
|
||||
- `progress_percent`
|
||||
- `stage`
|
||||
- `anchor_pack_json`
|
||||
- `last_assistant_reply`
|
||||
- `updated_at`
|
||||
3. 若已存在 `draft_json`,允许继续保留,不在聊天 finalize 中改写结果页草稿
|
||||
|
||||
## 5. LLM 输出契约
|
||||
|
||||
拼图聊天不需要复刻 Custom World 那么重的多层结构,本轮冻结为单个 JSON:
|
||||
|
||||
```json
|
||||
{
|
||||
"replyText": "我已经收住画面方向了,接下来你更想强调夜雨反光,还是猫咪本身的奇幻感?",
|
||||
"progressPercent": 46,
|
||||
"nextAnchorPack": {
|
||||
"themePromise": {
|
||||
"key": "themePromise",
|
||||
"label": "题材承诺",
|
||||
"value": "雨夜中的奇幻探索",
|
||||
"status": "confirmed"
|
||||
},
|
||||
"visualSubject": {
|
||||
"key": "visualSubject",
|
||||
"label": "画面主体",
|
||||
"value": "发光猫咪站在遗迹台阶上",
|
||||
"status": "confirmed"
|
||||
},
|
||||
"visualMood": {
|
||||
"key": "visualMood",
|
||||
"label": "视觉气质",
|
||||
"value": "潮湿、梦幻、带轻微悬疑",
|
||||
"status": "confirmed"
|
||||
},
|
||||
"compositionHooks": {
|
||||
"key": "compositionHooks",
|
||||
"label": "拼图记忆点",
|
||||
"value": "台阶透视、倒影、远处遗迹门洞",
|
||||
"status": "inferred"
|
||||
},
|
||||
"tagsAndForbidden": {
|
||||
"key": "tagsAndForbidden",
|
||||
"label": "标签与禁忌",
|
||||
"value": "雨夜、猫咪、神庙遗迹;禁止文字水印",
|
||||
"status": "inferred"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
要求:
|
||||
|
||||
1. 只能输出 JSON
|
||||
2. `progressPercent` 范围固定 `0..100`
|
||||
3. `nextAnchorPack` 必须是完整对象,不允许只给增量 patch
|
||||
|
||||
## 6. Prompt 设计
|
||||
|
||||
本轮不追求“复杂创作状态识别器”,只做拼图场景最小可用 prompt:
|
||||
|
||||
1. system prompt 明确模型角色是“拼图视觉共创策划”
|
||||
2. 明确 5 个锚点:
|
||||
- 题材承诺
|
||||
- 画面主体
|
||||
- 视觉气质
|
||||
- 拼图记忆点
|
||||
- 标签与禁忌
|
||||
3. 明确回复必须:
|
||||
- 自然中文
|
||||
- 一次只推进一个最关键问题
|
||||
- 不提“字段”“锚点”“JSON 结构”等内部词
|
||||
4. user prompt 携带:
|
||||
- 当前 turn / progress
|
||||
- 当前 anchor pack
|
||||
- 最近聊天记录
|
||||
- 输出 JSON 契约
|
||||
|
||||
## 7. SSE 口径
|
||||
|
||||
`POST /api/runtime/puzzle/agent/sessions/:sessionId/messages/stream`
|
||||
|
||||
成功时按顺序输出:
|
||||
|
||||
1. 多个 `reply_delta`
|
||||
2. 一个 `session`
|
||||
3. 一个 `done`
|
||||
|
||||
失败时输出:
|
||||
|
||||
1. `error`
|
||||
|
||||
要求:
|
||||
|
||||
1. `reply_delta.text` 来自真实流式累计 `replyText`
|
||||
2. `session` 必须来自 finalize 后重新读取的最新 session
|
||||
|
||||
## 8. 错误策略
|
||||
|
||||
1. 如果 `llm_client` 未配置:
|
||||
- 普通接口返回 `502`
|
||||
- SSE 返回 `error`
|
||||
- 不写 assistant message
|
||||
2. 如果 LLM 响应解析失败:
|
||||
- 普通接口返回 `502`
|
||||
- SSE 返回 `error`
|
||||
- 只保留 user message
|
||||
3. 如果 finalize 失败:
|
||||
- 普通接口返回 `502`
|
||||
- SSE 返回 `error`
|
||||
- 以前端重新拉 session 为准
|
||||
|
||||
## 9. 实现清单
|
||||
|
||||
1. `module-puzzle`
|
||||
- 新增 `PuzzleAgentMessageFinalizeInput`
|
||||
2. `spacetime-module/src/puzzle.rs`
|
||||
- 新增 `finalize_puzzle_agent_message_turn`
|
||||
- 修改 `submit_puzzle_agent_message`
|
||||
3. `spacetime-client`
|
||||
- 刷新 Rust bindings
|
||||
- 新增 `finalize_puzzle_agent_message(...)`
|
||||
4. `api-server`
|
||||
- 新增 `puzzle_agent_turn.rs`
|
||||
- `puzzle.rs` 的普通消息与 SSE 改接 turn service
|
||||
5. `docs/technical/README.md`
|
||||
- 补入本文档索引
|
||||
|
||||
## 10. 验收
|
||||
|
||||
1. 拼图 Agent 普通发送接口不再返回固定 assistant 文案
|
||||
2. 拼图 Agent SSE 可看到逐步增长的 `reply_delta`
|
||||
3. finalize 后 `session.messages` 中新增 assistant chat 消息
|
||||
4. `session.anchorPack / progressPercent / currentTurn / lastAssistantReply` 已真实更新
|
||||
5. LLM 关闭时不会再写入伪造 assistant 回复
|
||||
@@ -4,7 +4,13 @@
|
||||
|
||||
## 文档列表
|
||||
|
||||
- [ADMIN_CONSOLE_SERVICE_DESIGN_2026-04-23.md](./ADMIN_CONSOLE_SERVICE_DESIGN_2026-04-23.md):冻结 Rust `api-server` 内后台管理服务首版方案,明确管理员用户名密码登录、管理员 JWT 鉴权、数据库概览、受控 API 调试台与同源管理页面的落地边界。
|
||||
- [SPACETIME_MODULE_LIB_RS_SPLIT_EXECUTION_2026-04-23.md](./SPACETIME_MODULE_LIB_RS_SPLIT_EXECUTION_2026-04-23.md):冻结 `server-rs/crates/spacetime-module/src/lib.rs` 的模块地图、二级落位点与迁移顺序,要求后续 SpacetimeDB 主工程改动按对应模块落位,不再继续堆回单大文件。
|
||||
- [CUSTOM_WORLD_DRAFT_FOUNDATION_API_SERVER_LLM_MIGRATION_2026-04-23.md](./CUSTOM_WORLD_DRAFT_FOUNDATION_API_SERVER_LLM_MIGRATION_2026-04-23.md):冻结 `draft_foundation` 从 SpacetimeDB 内部规则编译迁到 `api-server + platform-llm` 的边界,明确草稿必须由 `api-server` 真实调 LLM 生成,SpacetimeDB 只负责落库。
|
||||
- [CUSTOM_WORLD_AGENT_LLM_REPLY_RESTORE_2026-04-22.md](./CUSTOM_WORLD_AGENT_LLM_REPLY_RESTORE_2026-04-22.md):恢复 Custom World Agent 聊天必须走大模型推理的 Rust 落地方案,冻结 submit/finalize 两阶段职责、旧 Node 提示词原样搬运、SSE 流式回复与 session 回写边界。
|
||||
- [PUZZLE_AGENT_LLM_REPLY_INTEGRATION_2026-04-23.md](./PUZZLE_AGENT_LLM_REPLY_INTEGRATION_2026-04-23.md):冻结拼图 Agent 聊天接入真实 LLM 的最小 Rust 落地方案,明确 submit 只写 user message、`api-server` 承接推理、SSE 流式回放与 finalize 回写 session 真相的边界。
|
||||
- [UNIFIED_CREATION_DRAFT_SESSION_RESTORE_2026-04-23.md](./UNIFIED_CREATION_DRAFT_SESSION_RESTORE_2026-04-23.md):冻结创作中心全草稿恢复 Agent 会话的统一口径,覆盖 RPG、Big Fish、Puzzle 三类草稿的会话索引、结果页分流和 Big Fish works 最小投影边界。
|
||||
- [PUZZLE_DRAFT_SESSION_RESTORE_2026-04-23.md](./PUZZLE_DRAFT_SESSION_RESTORE_2026-04-23.md):冻结拼图结果页草稿进入创作中心后的恢复口径,明确 draft 作品投影、`sourceSessionId` 反查 Agent session、编译时同步落草稿卡与发布复用同一作品记录。
|
||||
- [RUST_LOCAL_DEV_SPACETIMEDB_PUBLISH_GUARD_AND_AGENT_LLM_FAILURE_POLICY_2026-04-23.md](./RUST_LOCAL_DEV_SPACETIMEDB_PUBLISH_GUARD_AND_AGENT_LLM_FAILURE_POLICY_2026-04-23.md):冻结 Rust 本地联调启动前必须 publish/generate 最新 `spacetime-module` 的守卫,以及 Custom World Agent 在 LLM 失败时禁止写固定 assistant 回复的 finalize 与 HTTP/SSE 错误策略。
|
||||
- [CREATION_AGENT_CHAT_SCROLL_FOLLOW_POLICY_FIX_2026-04-23.md](./CREATION_AGENT_CHAT_SCROLL_FOLLOW_POLICY_FIX_2026-04-23.md):记录统一创作聊天工作区从“每次更新都强制滚到底”改为“仅在用户仍停留在底部附近时跟随”的滚动策略修复,避免流式回复持续抢走阅读位置。
|
||||
- [CREATION_AGENT_STREAMING_MESSAGE_STABILITY_FIX_2026-04-23.md](./CREATION_AGENT_STREAMING_MESSAGE_STABILITY_FIX_2026-04-23.md):记录创作 Agent 聊天流式文本、玩家乐观消息、最终 session 回写和草稿切换的展示稳定性修复,避免乱码、闪消、插队和旧草稿闪烁。
|
||||
@@ -14,7 +20,7 @@
|
||||
- [CREATION_CATEGORY_OPENING_TIMEOUT_GUARD_FIX_2026-04-22.md](./CREATION_CATEGORY_OPENING_TIMEOUT_GUARD_FIX_2026-04-22.md):记录创作中心点击类别后长时间停留在“正在开启”的根因与修复口径,收口前端创建会话启动超时、中文错误提示以及 Big Fish / 拼图代理上游超时兜底。
|
||||
- [JENKINS_RUST_BUILD_DEPLOY_PIPELINES_2026-04-23.md](./JENKINS_RUST_BUILD_DEPLOY_PIPELINES_2026-04-23.md):冻结 Jenkins `构建 / 部署 / 构建并部署` 三条流水线的职责、版本号传递、上游触发门禁、本地目录部署脚本与 `/home/ubuntu/Genarrative-deploy/` 覆盖策略。
|
||||
- [RUST_LOCAL_AND_REMOTE_DEPLOYMENT_SCRIPTS_2026-04-22.md](./RUST_LOCAL_AND_REMOTE_DEPLOYMENT_SCRIPTS_2026-04-22.md):冻结 Rust 本地一键联调脚本与 Ubuntu 发布包构建脚本的执行口径,覆盖 `npm run dev:rust`、`npm run build:rust:ubuntu`、Vite release、Linux `api-server`、SpacetimeDB wasm、启动停止脚本、默认 scp 上传和安全清库开关。
|
||||
- [RUST_API_SERVER_ROUTE_INDEX_2026-04-22.md](./RUST_API_SERVER_ROUTE_INDEX_2026-04-22.md):记录当前 Rust `api-server` 已挂载的 96 条 Axum 路由,按 auth、assets、runtime、custom world、story、generated path 等挂载面归类,用于对照 Node 能力基线与切流 smoke 清单。
|
||||
- [RUST_API_SERVER_ROUTE_INDEX_2026-04-22.md](./RUST_API_SERVER_ROUTE_INDEX_2026-04-22.md):记录当前 Rust `api-server` 已挂载的 101 条 Axum 路由,并补充管理后台入口与管理接口索引,按 auth、assets、runtime、custom world、story、generated path 等挂载面归类,用于对照 Node 能力基线与切流 smoke 清单。
|
||||
- [BACKEND_REWRITE_CROSS_CUTTING_GOVERNANCE_2026-04-22.md](./BACKEND_REWRITE_CROSS_CUTTING_GOVERNANCE_2026-04-22.md):冻结后端重写收口阶段的横向治理规则,覆盖 TypeScript contract 到 Rust DTO 映射、SpacetimeDB schema 演进、大对象 / workflow cache 存储边界和文档维护门禁。
|
||||
- [PLATFORM_LLM_TEXT_GATEWAY_DESIGN_2026-04-21.md](./PLATFORM_LLM_TEXT_GATEWAY_DESIGN_2026-04-21.md):`platform-llm` 文本模型网关首版设计,冻结 OpenAI 兼容 `/chat/completions`、SSE 增量解析、错误模型与重试边界。
|
||||
- [API_SERVER_PLATFORM_LLM_PROXY_DESIGN_2026-04-21.md](./API_SERVER_PLATFORM_LLM_PROXY_DESIGN_2026-04-21.md):`api-server` 接入 `platform-llm` 的最小代理设计,冻结 `/api/llm/chat/completions` 的配置、状态注入与首版非流式兼容边界。
|
||||
@@ -30,6 +36,10 @@
|
||||
- [PHONE_SMS_SEND_CODE_OBSERVABILITY_FIX_2026-04-23.md](./PHONE_SMS_SEND_CODE_OBSERVABILITY_FIX_2026-04-23.md):冻结手机号验证码发送链路的日志补强口径,确保 `api-server`、`module-auth`、`platform-auth` 能直接暴露发送前后与错误分类关键字段。
|
||||
- [PHONE_SMS_DELIVERY_OBSERVABILITY_AND_RECEIPT_DESIGN_2026-04-22.md](./PHONE_SMS_DELIVERY_OBSERVABILITY_AND_RECEIPT_DESIGN_2026-04-22.md):冻结短信平台受理成功与最终送达状态的区分方式、追踪字段、送达回执接口和前端提示文案边界。
|
||||
- [PHONE_SMS_REAL_PROVIDER_MANUAL_VERIFICATION_RUNBOOK_2026-04-23.md](./PHONE_SMS_REAL_PROVIDER_MANUAL_VERIFICATION_RUNBOOK_2026-04-23.md):冻结验证清单第一项“真实短信验证码链路”的本地启动、前端操作、日志观察点、通过标准与失败排查步骤。
|
||||
- [ASSET_EXTERNAL_GENERATION_MANUAL_VERIFICATION_RUNBOOK_2026-04-23.md](./ASSET_EXTERNAL_GENERATION_MANUAL_VERIFICATION_RUNBOOK_2026-04-23.md):冻结验证清单第四项“图片、视频、动作真实外部生成”的人工联调口径,明确哪些入口已接真实外部图片服务、哪些入口仍是 Stage 1 占位链,以及前端点击路径、日志观察点和通过标准。
|
||||
- [M6_CHARACTER_VISUAL_ASSET_EXTERNAL_GENERATION_STAGE2_2026-04-23.md](./M6_CHARACTER_VISUAL_ASSET_EXTERNAL_GENERATION_STAGE2_2026-04-23.md):冻结角色主形象从 Stage 1 SVG 占位草稿切到 DashScope 真实出图的 Stage 2 口径,覆盖模型、参考图解析、去底处理、OSS 草稿存储和失败策略。
|
||||
- [M6_CHARACTER_ANIMATION_ASSET_EXTERNAL_GENERATION_STAGE2_2026-04-23.md](./M6_CHARACTER_ANIMATION_ASSET_EXTERNAL_GENERATION_STAGE2_2026-04-23.md):冻结角色动作从 Stage 1 占位视频切到真实 Ark/DashScope 生成的 Stage 2 口径,优先明确 image-to-video 主链、Ark 固定参数、媒体上传与错误回退规则。
|
||||
- [M6_CHARACTER_ANIMATION_BACKEND_FRAME_EXTRACTION_AND_PUBLISH_STAGE3_2026-04-23.md](./M6_CHARACTER_ANIMATION_BACKEND_FRAME_EXTRACTION_AND_PUBLISH_STAGE3_2026-04-23.md):冻结角色动作正式发布链从“前端抽帧回传”继续迁到“后端抽帧、去绿幕、上传 OSS、落库绑定”的 Stage 3 口径,明确 `ffmpeg/ffprobe` 依赖、contract 扩展与兼容策略。
|
||||
- [WECHAT_LOGIN_AXUM_IMPLEMENTATION_DESIGN_2026-04-21.md](./WECHAT_LOGIN_AXUM_IMPLEMENTATION_DESIGN_2026-04-21.md):Rust `api-server` 微信登录实现设计,冻结微信 provider 接入、系统 JWT 签发边界、`wechat/start` / `wechat/callback` / `wechat/bind-phone` 闭环,以及与后续 `SpacetimeDB` claims 透传的关系。
|
||||
- [WECHAT_LOGIN_REAL_INTEGRATION_RUNBOOK_2026-04-21.md](./WECHAT_LOGIN_REAL_INTEGRATION_RUNBOOK_2026-04-21.md):微信登录从本地 mock 到真实微信开放平台联调的执行手册,覆盖环境变量、回调域名、代理头要求、验证步骤与常见失败排查。
|
||||
- [PASSWORD_ENTRY_FLOW_DESIGN_2026-04-21.md](./PASSWORD_ENTRY_FLOW_DESIGN_2026-04-21.md):密码登录与自动建号落地设计,冻结 `/api/auth/entry`、幂等兼容策略、模块边界以及与 JWT / refresh cookie 的衔接方式。
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Rust API Server 路由索引(2026-04-22)
|
||||
# Rust API Server 路由索引(2026-04-23)
|
||||
|
||||
更新时间:`2026-04-22`
|
||||
更新时间:`2026-04-23`
|
||||
|
||||
## 1. 文档目标
|
||||
|
||||
@@ -10,27 +10,36 @@
|
||||
|
||||
## 2. 当前统计
|
||||
|
||||
当前 Rust `api-server` 从 `app.rs` 可抽取到 `96` 条路由:
|
||||
当前 Rust `api-server` 从 `app.rs` 可抽取到 `101` 条路由:
|
||||
|
||||
1. 内部鉴权调试接口:`2` 条。
|
||||
2. AI task 接口:`9` 条。
|
||||
3. assets / OSS 接口:`15` 条。
|
||||
4. auth 接口:`12` 条。
|
||||
5. custom world / agent 接口:`23` 条。
|
||||
6. llm proxy 接口:`1` 条。
|
||||
7. profile / runtime profile 接口:`12` 条。
|
||||
8. runtime story / story gameplay 接口:`15` 条。
|
||||
9. legacy generated 静态路径兼容:`6` 条。
|
||||
10. health check:`1` 条。
|
||||
1. 管理后台接口:`5` 条。
|
||||
2. 内部鉴权调试接口:`2` 条。
|
||||
3. AI task 接口:`9` 条。
|
||||
4. assets / OSS 接口:`15` 条。
|
||||
5. auth 接口:`12` 条。
|
||||
6. custom world / agent 接口:`23` 条。
|
||||
7. llm proxy 接口:`1` 条。
|
||||
8. profile / runtime profile 接口:`12` 条。
|
||||
9. runtime story / story gameplay 接口:`15` 条。
|
||||
10. legacy generated 静态路径兼容:`6` 条。
|
||||
11. health check:`1` 条。
|
||||
|
||||
## 3. 路由清单
|
||||
|
||||
### 3.1 内部鉴权调试
|
||||
### 3.1 管理后台
|
||||
|
||||
1. `GET /admin`
|
||||
2. `POST /admin/api/login`
|
||||
3. `GET /admin/api/me`
|
||||
4. `GET /admin/api/overview`
|
||||
5. `POST /admin/api/debug/http`
|
||||
|
||||
### 3.2 内部鉴权调试
|
||||
|
||||
1. `GET /_internal/auth/claims`
|
||||
2. `GET /_internal/auth/refresh-cookie`
|
||||
|
||||
### 3.2 AI Task
|
||||
### 3.3 AI Task
|
||||
|
||||
1. `POST /api/ai/tasks`
|
||||
2. `POST /api/ai/tasks/{task_id}/start`
|
||||
@@ -42,7 +51,7 @@
|
||||
8. `POST /api/ai/tasks/{task_id}/stages/{stage_kind}/start`
|
||||
9. `POST /api/ai/tasks/{task_id}/stages/{stage_kind}/complete`
|
||||
|
||||
### 3.3 Assets / OSS
|
||||
### 3.4 Assets / OSS
|
||||
|
||||
1. `POST /api/assets/direct-upload-tickets`
|
||||
2. `POST /api/assets/sts-upload-credentials`
|
||||
@@ -60,7 +69,7 @@
|
||||
14. `GET /api/assets/character-workflow-cache/{character_id}`
|
||||
15. `GET / POST /api/assets/character-workflow-cache`
|
||||
|
||||
### 3.4 Auth
|
||||
### 3.5 Auth
|
||||
|
||||
1. `GET /api/auth/login-options`
|
||||
2. `GET /api/auth/me`
|
||||
@@ -75,7 +84,7 @@
|
||||
11. `POST /api/auth/wechat/bind-phone`
|
||||
12. `POST /api/auth/entry`
|
||||
|
||||
### 3.5 Custom World / Agent
|
||||
### 3.6 Custom World / Agent
|
||||
|
||||
1. `GET /api/runtime/custom-world-library`
|
||||
2. `GET /api/runtime/custom-world-library/{profile_id}`
|
||||
@@ -101,11 +110,11 @@
|
||||
22. `POST /api/runtime/custom-world/cover-image`
|
||||
23. `POST /api/runtime/custom-world/cover-upload`
|
||||
|
||||
### 3.6 LLM Proxy
|
||||
### 3.7 LLM Proxy
|
||||
|
||||
1. `POST /api/llm/chat/completions`
|
||||
|
||||
### 3.7 Profile / Runtime Profile
|
||||
### 3.8 Profile / Runtime Profile
|
||||
|
||||
1. `GET /api/profile/dashboard`
|
||||
2. `GET /api/runtime/profile/dashboard`
|
||||
@@ -120,7 +129,7 @@
|
||||
11. `POST /api/profile/save-archives/{world_key}`
|
||||
12. `POST /api/runtime/profile/save-archives/{world_key}`
|
||||
|
||||
### 3.8 Runtime Story / Gameplay
|
||||
### 3.9 Runtime Story / Gameplay
|
||||
|
||||
1. `POST /api/runtime/save/snapshot`
|
||||
2. `GET /api/runtime/settings`
|
||||
@@ -138,7 +147,7 @@
|
||||
14. `POST /api/story/npc/battle`
|
||||
15. `GET /api/runtime/sessions/{runtime_session_id}/inventory`
|
||||
|
||||
### 3.9 Legacy Generated 路径
|
||||
### 3.10 Legacy Generated 路径
|
||||
|
||||
1. `GET /generated-character-drafts/{*path}`
|
||||
2. `GET /generated-characters/{*path}`
|
||||
@@ -147,7 +156,7 @@
|
||||
5. `GET /generated-custom-world-covers/{*path}`
|
||||
6. `GET /generated-qwen-sprites/{*path}`
|
||||
|
||||
### 3.10 Health
|
||||
### 3.11 Health
|
||||
|
||||
1. `GET /healthz`
|
||||
|
||||
|
||||
@@ -0,0 +1,241 @@
|
||||
# spacetime-module `lib.rs` 拆分执行方案
|
||||
|
||||
日期:`2026-04-23`
|
||||
|
||||
## 1. 背景
|
||||
|
||||
当前 `server-rs/crates/spacetime-module/src/lib.rs` 已经超过 `9000` 行,同时混合了承载:
|
||||
|
||||
1. SpacetimeDB 主工程入口
|
||||
2. 跨域共享类型
|
||||
3. Big Fish
|
||||
4. Asset Metadata
|
||||
5. Runtime
|
||||
6. Gameplay
|
||||
7. Custom World
|
||||
8. AI
|
||||
9. Puzzle
|
||||
10. 测试
|
||||
|
||||
这会直接导致:
|
||||
|
||||
1. 任意一个领域改动都要在同一个超大文件里定位
|
||||
2. 不同玩法与领域的 reducer / procedure / helper 互相缠绕
|
||||
3. 结构上已经与 `M7` 冻结的“按业务与 SpacetimeDB 聚合层次拆分目录”目标不一致
|
||||
|
||||
## 2. 本轮约束
|
||||
|
||||
本轮拆分严格遵守以下规则:
|
||||
|
||||
1. 只做物理结构收口,不改 table 名、reducer 名、procedure 名。
|
||||
2. 不改现有 schema 字段名、字段顺序语义与已有对外 contract。
|
||||
3. 不把外部副作用搬进 SpacetimeDB reducer / procedure。
|
||||
4. 允许在过渡期继续保留少量跨域 helper 在 `lib.rs`,但新增内容禁止继续直接堆回 `lib.rs`。
|
||||
|
||||
## 3. 模块地图
|
||||
|
||||
### 3.1 根入口
|
||||
|
||||
`server-rs/crates/spacetime-module/src/lib.rs`
|
||||
|
||||
后续只允许保留:
|
||||
|
||||
1. `use` 聚合
|
||||
2. `mod` 声明
|
||||
3. 少量跨域共享 helper
|
||||
4. 迁移过渡期测试
|
||||
|
||||
禁止继续新增某个具体业务域的 table / reducer / procedure / tx helper。
|
||||
|
||||
导入导出风格同步冻结为:
|
||||
|
||||
1. `src/lib.rs` 对外统一优先使用 `pub use xxx::*;` 重新导出主工程模块与外部模块 crate 内容。
|
||||
2. 已拆出的业务模块内部统一优先使用 `use crate::*;`,避免每个文件重复维护大段显式 `use` 列表。
|
||||
3. 子模块只有遇到命名冲突、宏限制或无法从 crate 根重导出的外部符号时,才允许补局部显式 `use`。
|
||||
4. 后续拆分时不再因为导入列表变长而把业务实现留在 `lib.rs`。
|
||||
|
||||
### 3.2 已冻结的一级模块
|
||||
|
||||
1. `src/entry.rs`
|
||||
- 模块初始化入口
|
||||
- `#[spacetimedb::reducer(init)]`
|
||||
2. `src/domain_types.rs`
|
||||
- 跨域共享的 SpacetimeDB 类型
|
||||
- 目前主要承载 NPC 开战桥接输入输出
|
||||
3. `src/asset_metadata/mod.rs`
|
||||
- `asset_object`
|
||||
- `asset_entity_binding`
|
||||
- 资产确认与绑定 reducer / procedure
|
||||
4. `src/big_fish/mod.rs`
|
||||
- Big Fish session / message / asset slot / runtime run
|
||||
- Big Fish procedure 与 tx helper
|
||||
5. `src/runtime/mod.rs`
|
||||
- Runtime settings / snapshots / browse history / dashboard / wallet / save archive
|
||||
6. `src/gameplay/mod.rs`
|
||||
- `story / combat / inventory / npc / quest / runtime_item / progression`
|
||||
7. `src/custom_world/mod.rs`
|
||||
- profile / session / agent / publish / gallery / works
|
||||
8. `src/ai/mod.rs`
|
||||
- ai task / stage / chunk / reference
|
||||
9. `src/puzzle.rs`
|
||||
- 拼图玩法当前仍为单文件域模块,后续再决定是否继续拆目录
|
||||
|
||||
### 3.3 本轮先创建的二级落位点
|
||||
|
||||
#### `asset_metadata/`
|
||||
|
||||
1. `objects.rs`
|
||||
2. `bindings.rs`
|
||||
|
||||
#### `big_fish/`
|
||||
|
||||
1. `tables.rs`
|
||||
2. `session.rs`
|
||||
3. `assets.rs`
|
||||
4. `runtime.rs`
|
||||
|
||||
#### `runtime/`
|
||||
|
||||
1. `settings.rs`
|
||||
2. `snapshots.rs`
|
||||
3. `browse_history.rs`
|
||||
4. `profile.rs`
|
||||
|
||||
#### `gameplay/`
|
||||
|
||||
1. `combat.rs`
|
||||
2. `inventory.rs`
|
||||
3. `npc.rs`
|
||||
4. `progression.rs`
|
||||
5. `quest.rs`
|
||||
6. `runtime_item.rs`
|
||||
7. `story.rs`
|
||||
|
||||
#### `custom_world/`
|
||||
|
||||
1. `profile.rs`
|
||||
2. `session.rs`
|
||||
3. `agent.rs`
|
||||
4. `publishing.rs`
|
||||
5. `gallery.rs`
|
||||
6. `works.rs`
|
||||
|
||||
#### `ai/`
|
||||
|
||||
1. `tasks.rs`
|
||||
2. `stages.rs`
|
||||
3. `snapshots.rs`
|
||||
|
||||
## 4. 迁移顺序
|
||||
|
||||
为了降低脏工作区下的冲突风险,本轮按下面顺序推进:
|
||||
|
||||
1. 先冻结文档与 README 路由规则。
|
||||
2. 先创建二级空模块文件,作为后续内容落位点。
|
||||
3. 第一批先迁:
|
||||
- `entry.rs`
|
||||
- `domain_types.rs`
|
||||
- `asset_metadata/mod.rs`
|
||||
- `big_fish/mod.rs`
|
||||
4. 第二批再迁:
|
||||
- `runtime/mod.rs`
|
||||
- `gameplay/mod.rs`
|
||||
5. 第三批再迁:
|
||||
- `custom_world/mod.rs`
|
||||
- `ai/mod.rs`
|
||||
6. `puzzle.rs` 暂时保持单文件,不在本轮强拆。
|
||||
|
||||
## 5. 本轮完成标准
|
||||
|
||||
1. `README.md` 明确声明后续新增逻辑的落位规则。
|
||||
2. 一级模块与二级空模块文件创建完成。
|
||||
3. `lib.rs` 不再承载 `init`、共享 domain types、asset metadata、big fish 的实现细节。
|
||||
4. `cargo check -p spacetime-module --lib` 继续通过。
|
||||
5. 中文文档与 Rust 文件经过编码检查,没有写坏。
|
||||
|
||||
## 6. `runtime` 域实拆记录
|
||||
|
||||
`2026-04-23` 追加执行 `runtime` 域真实内容拆分,目标是把根入口中的 runtime 表、procedure 与同域 tx helper 收口到 `src/runtime/`,同时保持对外 API 名称不变。
|
||||
|
||||
### 6.1 已落位文件
|
||||
|
||||
1. `server-rs/crates/spacetime-module/src/runtime/mod.rs`
|
||||
- 仅保留二级模块声明与 `pub use xxx::*;` 聚合导出。
|
||||
2. `server-rs/crates/spacetime-module/src/runtime/settings.rs`
|
||||
- 承载 `RuntimeSetting`
|
||||
- 承载 runtime setting 的读取、upsert procedure 与快照构建 helper。
|
||||
3. `server-rs/crates/spacetime-module/src/runtime/snapshots.rs`
|
||||
- 承载 `RuntimeSnapshotRow`
|
||||
- 承载 runtime snapshot 的读取、upsert、delete 与 JSON 解析 helper。
|
||||
4. `server-rs/crates/spacetime-module/src/runtime/browse_history.rs`
|
||||
- 承载 `UserBrowseHistory`
|
||||
- 承载平台浏览历史 list、upsert、clear procedure 与行转换 helper。
|
||||
5. `server-rs/crates/spacetime-module/src/runtime/profile.rs`
|
||||
- 承载 `ProfileDashboardState`
|
||||
- 承载 `ProfileWalletLedger`
|
||||
- 承载 `ProfilePlayedWorld`
|
||||
- 承载 `ProfileSaveArchive`
|
||||
- 承载 profile dashboard、wallet ledger、play stats、save archive 投影与 snapshot 同步 helper。
|
||||
|
||||
### 6.2 根入口调整
|
||||
|
||||
`server-rs/crates/spacetime-module/src/lib.rs` 当前只通过下面方式接入 runtime 域:
|
||||
|
||||
1. `mod runtime;`
|
||||
2. `pub use runtime::*;`
|
||||
|
||||
原先留在 `lib.rs` 的 runtime setting、snapshot、profile、browse history 旧 helper 已删除,避免同名实现重复存在,也避免后续继续在根入口堆叠 runtime 业务逻辑。
|
||||
|
||||
### 6.3 后续维护规则
|
||||
|
||||
1. 对外导出继续通过 `pub use runtime::*;` 以及 `src/runtime/mod.rs` 中的 `pub use xxx::*;` 透出。
|
||||
2. `runtime` 子文件内部优先使用 `use crate::*;`,只有 SpacetimeDB table accessor trait、命名冲突或宏限制需要时才补最小显式导入。
|
||||
3. 新增 runtime 相关表、procedure、reducer 或 tx helper 时,必须先按 `settings / snapshots / browse_history / profile` 判断二级落位点;不匹配时先更新本文件与 README,再新增二级模块。
|
||||
4. `lib.rs` 只保留跨域共享 helper 和尚未迁出的过渡代码,不再接收 runtime 域新增实现。
|
||||
|
||||
## 7. `ai` 域实拆记录
|
||||
|
||||
`2026-04-23` 追加执行 `ai` 域真实内容拆分,目标是把根入口中的 AI 表、procedure 与同域 tx helper 收口到 `src/ai/`,同时保持 `module-ai` 对外输入输出 contract 与 SpacetimeDB reducer / procedure 名称不变。
|
||||
|
||||
### 7.1 已落位文件
|
||||
|
||||
1. `server-rs/crates/spacetime-module/src/ai/mod.rs`
|
||||
- 仅保留二级模块声明与聚合导出。
|
||||
- public API 通过 `pub use stages::*;` 和 `pub use tasks::*;` 透出。
|
||||
- 内部转换 helper 通过 `pub(crate) use snapshots::*;` 供 AI 子模块共享。
|
||||
2. `server-rs/crates/spacetime-module/src/ai/tasks.rs`
|
||||
- 承载 `AiTask`
|
||||
- 承载 `create_ai_task`
|
||||
- 承载 `create_ai_task_and_return`
|
||||
- 承载 `start_ai_task`
|
||||
- 承载 `complete_ai_task_and_return`
|
||||
- 承载 `fail_ai_task_and_return`
|
||||
- 承载 `cancel_ai_task_and_return`
|
||||
- 承载 task 状态迁移、读取与持久化 helper。
|
||||
3. `server-rs/crates/spacetime-module/src/ai/stages.rs`
|
||||
- 承载 `AiTaskStage`
|
||||
- 承载 `AiTextChunk`
|
||||
- 承载 `AiResultReference`
|
||||
- 承载 `start_ai_task_stage`
|
||||
- 承载 `append_ai_text_chunk_and_return`
|
||||
- 承载 `complete_ai_stage_and_return`
|
||||
- 承载 `attach_ai_result_reference_and_return`
|
||||
- 承载阶段流式文本聚合、阶段替换与 result reference 写入 helper。
|
||||
4. `server-rs/crates/spacetime-module/src/ai/snapshots.rs`
|
||||
- 承载 `AiTask / AiTaskStage / AiTextChunk / AiResultReference` 的 row 与 snapshot 转换 helper。
|
||||
|
||||
### 7.2 根入口调整
|
||||
|
||||
`server-rs/crates/spacetime-module/src/lib.rs` 当前只通过下面方式接入 AI 域:
|
||||
|
||||
1. `mod ai;`
|
||||
2. `pub use ai::*;`
|
||||
|
||||
原先留在 `lib.rs` 的 AI 表、procedure 与 helper 已删除,避免根入口继续堆叠 AI 业务实现。
|
||||
|
||||
### 7.3 后续维护规则
|
||||
|
||||
1. 对外导出继续通过 `pub use ai::*;` 以及 `src/ai/mod.rs` 中的 `pub use xxx::*;` 透出。
|
||||
2. `ai` 子文件内部优先使用 `use crate::*;`,只有 `module_ai` 中的归一化函数、校验函数、ID 前缀常量等需要避免 glob 歧义的符号才补最小显式导入。
|
||||
3. 新增 AI 相关表、procedure、reducer 或 tx helper 时,必须先按 `tasks / stages / snapshots` 判断二级落位点;不匹配时先更新本文件与 README,再新增二级模块。
|
||||
4. `lib.rs` 不再接收 AI 域新增实现。
|
||||
@@ -3,6 +3,7 @@ export type AuthLoginMethod = 'password' | 'phone' | 'wechat';
|
||||
|
||||
export type AuthUser = {
|
||||
id: string;
|
||||
publicUserCode: string;
|
||||
username: string;
|
||||
displayName: string;
|
||||
phoneNumberMasked: string | null;
|
||||
@@ -11,6 +12,16 @@ export type AuthUser = {
|
||||
wechatBound: boolean;
|
||||
};
|
||||
|
||||
export type PublicUserSummary = {
|
||||
id: string;
|
||||
publicUserCode: string;
|
||||
displayName: string;
|
||||
};
|
||||
|
||||
export type PublicUserSearchResponse = {
|
||||
user: PublicUserSummary;
|
||||
};
|
||||
|
||||
export type AuthEntryRequest = {
|
||||
username: string;
|
||||
password: string;
|
||||
|
||||
@@ -629,6 +629,8 @@ export function createRpgWorldLibraryEntryFixture(): CustomWorldLibraryEntry<Cus
|
||||
return cloneFixture({
|
||||
ownerUserId: RPG_CREATION_FIXTURE_USER_ID,
|
||||
profileId: RPG_CREATION_FIXTURE_PROFILE_ID,
|
||||
publicWorkCode: 'cw-fixture-001',
|
||||
authorPublicUserCode: 'sy-fixture-user',
|
||||
profile,
|
||||
visibility: 'published',
|
||||
publishedAt: RPG_CREATION_FIXTURE_PUBLISHED_AT,
|
||||
|
||||
@@ -118,6 +118,8 @@ export type CustomWorldProfileRecord = JsonObject & {
|
||||
export type CustomWorldLibraryEntry<TProfile = CustomWorldProfileRecord> = {
|
||||
ownerUserId: string;
|
||||
profileId: string;
|
||||
publicWorkCode: string | null;
|
||||
authorPublicUserCode: string | null;
|
||||
profile: TProfile;
|
||||
visibility: CustomWorldPublicationStatus;
|
||||
publishedAt: string | null;
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
use std::collections::BTreeSet;
|
||||
use std::{
|
||||
collections::BTreeSet,
|
||||
net::{IpAddr, Ipv4Addr, Ipv6Addr},
|
||||
};
|
||||
|
||||
use axum::{
|
||||
Json,
|
||||
@@ -21,10 +24,13 @@ use shared_contracts::admin::{
|
||||
use time::{OffsetDateTime, format_description::well_known::Rfc3339};
|
||||
|
||||
use crate::{
|
||||
api_response::json_success_body, http_error::AppError, request_context::RequestContext,
|
||||
api_response::json_success_body,
|
||||
http_error::AppError,
|
||||
request_context::RequestContext,
|
||||
state::{AdminRuntime, AppState},
|
||||
};
|
||||
|
||||
// 首版调试台只允许有限大小的请求体,避免把后台当作通用代理大包转发器。
|
||||
const MAX_DEBUG_BODY_BYTES: usize = 128 * 1024;
|
||||
const BLOCKED_DEBUG_HEADERS: &[&str] = &[
|
||||
"host",
|
||||
@@ -33,6 +39,7 @@ const BLOCKED_DEBUG_HEADERS: &[&str] = &[
|
||||
"transfer-encoding",
|
||||
"expect",
|
||||
];
|
||||
// 数据库概览首版只统计受控白名单表,禁止后台页面直接输入任意 SQL。
|
||||
const DATABASE_OVERVIEW_TABLES: &[&str] = &[
|
||||
"runtime_setting",
|
||||
"runtime_snapshot",
|
||||
@@ -124,9 +131,9 @@ pub async fn admin_login(
|
||||
Extension(request_context): Extension<RequestContext>,
|
||||
Json(payload): Json<AdminLoginRequest>,
|
||||
) -> Result<Json<Value>, AppError> {
|
||||
let runtime = state
|
||||
.admin_runtime()
|
||||
.ok_or_else(|| AppError::from_status(StatusCode::SERVICE_UNAVAILABLE).with_message("后台管理未启用"))?;
|
||||
let runtime = state.admin_runtime().ok_or_else(|| {
|
||||
AppError::from_status(StatusCode::SERVICE_UNAVAILABLE).with_message("后台管理未启用")
|
||||
})?;
|
||||
|
||||
let expected_username = runtime.username().trim();
|
||||
let expected_password = runtime.password().trim();
|
||||
@@ -139,16 +146,18 @@ pub async fn admin_login(
|
||||
}
|
||||
|
||||
if submitted_username != expected_username || submitted_password != expected_password {
|
||||
return Err(AppError::from_status(StatusCode::UNAUTHORIZED).with_message("管理员用户名或密码错误"));
|
||||
return Err(
|
||||
AppError::from_status(StatusCode::UNAUTHORIZED).with_message("管理员用户名或密码错误")
|
||||
);
|
||||
}
|
||||
|
||||
let now = OffsetDateTime::now_utc();
|
||||
let claims = runtime
|
||||
.build_claims(now)
|
||||
.map_err(|error| AppError::from_status(StatusCode::INTERNAL_SERVER_ERROR).with_message(error))?;
|
||||
let token = runtime
|
||||
.sign_token(&claims)
|
||||
.map_err(|error| AppError::from_status(StatusCode::INTERNAL_SERVER_ERROR).with_message(error))?;
|
||||
let claims = runtime.build_claims(now).map_err(|error| {
|
||||
AppError::from_status(StatusCode::INTERNAL_SERVER_ERROR).with_message(error)
|
||||
})?;
|
||||
let token = runtime.sign_token(&claims).map_err(|error| {
|
||||
AppError::from_status(StatusCode::INTERNAL_SERVER_ERROR).with_message(error)
|
||||
})?;
|
||||
|
||||
Ok(json_success_body(
|
||||
Some(&request_context),
|
||||
@@ -176,9 +185,9 @@ pub async fn admin_overview(
|
||||
Extension(request_context): Extension<RequestContext>,
|
||||
Extension(_admin): Extension<AuthenticatedAdmin>,
|
||||
) -> Result<Json<Value>, AppError> {
|
||||
let runtime = state
|
||||
.admin_runtime()
|
||||
.ok_or_else(|| AppError::from_status(StatusCode::SERVICE_UNAVAILABLE).with_message("后台管理未启用"))?;
|
||||
let runtime = state.admin_runtime().ok_or_else(|| {
|
||||
AppError::from_status(StatusCode::SERVICE_UNAVAILABLE).with_message("后台管理未启用")
|
||||
})?;
|
||||
|
||||
let overview = build_admin_overview(&state, runtime).await?;
|
||||
Ok(json_success_body(Some(&request_context), overview))
|
||||
@@ -199,9 +208,10 @@ pub async fn require_admin_auth(
|
||||
mut request: Request,
|
||||
next: Next,
|
||||
) -> Result<Response, AppError> {
|
||||
let runtime = state
|
||||
.admin_runtime()
|
||||
.ok_or_else(|| AppError::from_status(StatusCode::SERVICE_UNAVAILABLE).with_message("后台管理未启用"))?;
|
||||
// 后台鉴权必须同时满足:令牌验签通过、主体匹配当前管理员、roles 含 admin。
|
||||
let runtime = state.admin_runtime().ok_or_else(|| {
|
||||
AppError::from_status(StatusCode::SERVICE_UNAVAILABLE).with_message("后台管理未启用")
|
||||
})?;
|
||||
let bearer_token = extract_bearer_token(request.headers())?;
|
||||
let claims = runtime
|
||||
.verify_token(&bearer_token)
|
||||
@@ -213,7 +223,9 @@ pub async fn require_admin_auth(
|
||||
|
||||
request
|
||||
.extensions_mut()
|
||||
.insert(AuthenticatedAdmin::new(build_admin_session_payload(admin_session)));
|
||||
.insert(AuthenticatedAdmin::new(build_admin_session_payload(
|
||||
admin_session,
|
||||
)));
|
||||
Ok(next.run(request).await)
|
||||
}
|
||||
|
||||
@@ -252,10 +264,16 @@ async fn build_admin_overview(
|
||||
}
|
||||
|
||||
async fn fetch_database_overview(state: &AppState) -> AdminDatabaseOverviewPayload {
|
||||
// 概览直接读取 SpacetimeDB HTTP API,保证后台看到的是真实数据库元信息而不是本地缓存。
|
||||
let client = Client::new();
|
||||
let server_root = state.config.spacetime_server_url.trim_end_matches('/');
|
||||
let database = state.config.spacetime_database.trim();
|
||||
let token = state.config.spacetime_token.as_deref().map(str::trim).filter(|value| !value.is_empty());
|
||||
let token = state
|
||||
.config
|
||||
.spacetime_token
|
||||
.as_deref()
|
||||
.map(str::trim)
|
||||
.filter(|value| !value.is_empty());
|
||||
let mut fetch_errors = Vec::new();
|
||||
|
||||
let database_info = fetch_spacetime_json::<SpacetimeDatabaseInfoResponse>(
|
||||
@@ -321,9 +339,15 @@ async fn fetch_database_overview(state: &AppState) -> AdminDatabaseOverviewPaylo
|
||||
schema_table_names.sort();
|
||||
|
||||
AdminDatabaseOverviewPayload {
|
||||
database_identity: database_info.as_ref().and_then(|value| value.database_identity.clone()),
|
||||
owner_identity: database_info.as_ref().and_then(|value| value.owner_identity.clone()),
|
||||
host_type: database_info.as_ref().and_then(|value| value.host_type.clone()),
|
||||
database_identity: database_info
|
||||
.as_ref()
|
||||
.and_then(|value| value.database_identity.clone()),
|
||||
owner_identity: database_info
|
||||
.as_ref()
|
||||
.and_then(|value| value.owner_identity.clone()),
|
||||
host_type: database_info
|
||||
.as_ref()
|
||||
.and_then(|value| value.host_type.clone()),
|
||||
schema_table_names,
|
||||
table_stats,
|
||||
fetch_errors,
|
||||
@@ -426,15 +450,12 @@ async fn execute_admin_debug_http(
|
||||
state: &AppState,
|
||||
payload: AdminDebugHttpRequest,
|
||||
) -> Result<AdminDebugHttpResponse, AppError> {
|
||||
// 调试请求始终回打当前 api-server,同源受控,不允许作为外部代理使用。
|
||||
let method = Method::from_bytes(payload.method.trim().as_bytes()).map_err(|_| {
|
||||
AppError::from_status(StatusCode::BAD_REQUEST).with_message("HTTP 方法不合法")
|
||||
})?;
|
||||
let path = normalize_debug_path(&payload.path)?;
|
||||
let base_url = format!(
|
||||
"http://{}:{}",
|
||||
state.config.bind_host.trim(),
|
||||
state.config.bind_port
|
||||
);
|
||||
let base_url = build_debug_base_url(&state.config.bind_host, state.config.bind_port);
|
||||
let target_url = format!("{base_url}{path}");
|
||||
let body_text = payload.body.unwrap_or_default();
|
||||
if body_text.len() > MAX_DEBUG_BODY_BYTES {
|
||||
@@ -451,7 +472,10 @@ async fn execute_admin_debug_http(
|
||||
|
||||
for header in payload.headers.unwrap_or_default() {
|
||||
let header_name = header.name.trim().to_ascii_lowercase();
|
||||
if BLOCKED_DEBUG_HEADERS.iter().any(|blocked| *blocked == header_name) {
|
||||
if BLOCKED_DEBUG_HEADERS
|
||||
.iter()
|
||||
.any(|blocked| *blocked == header_name)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
let name = HeaderName::from_bytes(header_name.as_bytes()).map_err(|_| {
|
||||
@@ -464,7 +488,8 @@ async fn execute_admin_debug_http(
|
||||
}
|
||||
|
||||
let response = request.send().await.map_err(|error| {
|
||||
AppError::from_status(StatusCode::BAD_GATEWAY).with_message(format!("调试请求失败:{error}"))
|
||||
AppError::from_status(StatusCode::BAD_GATEWAY)
|
||||
.with_message(format!("调试请求失败:{error}"))
|
||||
})?;
|
||||
let status = response.status();
|
||||
let headers = response
|
||||
@@ -476,7 +501,8 @@ async fn execute_admin_debug_http(
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
let response_body = response.bytes().await.map_err(|error| {
|
||||
AppError::from_status(StatusCode::BAD_GATEWAY).with_message(format!("调试响应读取失败:{error}"))
|
||||
AppError::from_status(StatusCode::BAD_GATEWAY)
|
||||
.with_message(format!("调试响应读取失败:{error}"))
|
||||
})?;
|
||||
let body_preview = build_body_preview(&response_body);
|
||||
let body_json = serde_json::from_slice::<Value>(&response_body).ok();
|
||||
@@ -490,19 +516,56 @@ async fn execute_admin_debug_http(
|
||||
})
|
||||
}
|
||||
|
||||
fn build_debug_base_url(bind_host: &str, bind_port: u16) -> String {
|
||||
let debug_host = resolve_debug_host(bind_host);
|
||||
let authority_host = format_http_authority_host(&debug_host);
|
||||
format!("http://{authority_host}:{bind_port}")
|
||||
}
|
||||
|
||||
fn resolve_debug_host(bind_host: &str) -> String {
|
||||
let trimmed = bind_host.trim();
|
||||
if trimmed.is_empty() {
|
||||
return Ipv4Addr::LOCALHOST.to_string();
|
||||
}
|
||||
|
||||
match trimmed.parse::<IpAddr>() {
|
||||
Ok(IpAddr::V4(ip)) if ip.is_unspecified() => Ipv4Addr::LOCALHOST.to_string(),
|
||||
Ok(IpAddr::V6(ip)) if ip.is_unspecified() => Ipv6Addr::LOCALHOST.to_string(),
|
||||
Ok(ip) => ip.to_string(),
|
||||
Err(_) => trimmed.to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
fn format_http_authority_host(host: &str) -> String {
|
||||
if host.starts_with('[') && host.ends_with(']') {
|
||||
return host.to_string();
|
||||
}
|
||||
if host.parse::<Ipv6Addr>().is_ok() {
|
||||
return format!("[{host}]");
|
||||
}
|
||||
host.to_string()
|
||||
}
|
||||
|
||||
fn normalize_debug_path(path: &str) -> Result<String, AppError> {
|
||||
// 只允许 `/xxx` 形式的同源相对路径,明确拒绝绝对 URL 与后台登录接口。
|
||||
let trimmed = path.trim();
|
||||
if trimmed.is_empty() {
|
||||
return Err(AppError::from_status(StatusCode::BAD_REQUEST).with_message("调试路径不能为空"));
|
||||
}
|
||||
if trimmed.starts_with("http://") || trimmed.starts_with("https://") {
|
||||
return Err(AppError::from_status(StatusCode::BAD_REQUEST).with_message("只允许调试同源相对路径"));
|
||||
return Err(
|
||||
AppError::from_status(StatusCode::BAD_REQUEST).with_message("只允许调试同源相对路径")
|
||||
);
|
||||
}
|
||||
if !trimmed.starts_with('/') {
|
||||
return Err(AppError::from_status(StatusCode::BAD_REQUEST).with_message("调试路径必须以 / 开头"));
|
||||
return Err(
|
||||
AppError::from_status(StatusCode::BAD_REQUEST).with_message("调试路径必须以 / 开头")
|
||||
);
|
||||
}
|
||||
if trimmed == "/admin/api/login" {
|
||||
return Err(AppError::from_status(StatusCode::BAD_REQUEST).with_message("禁止调试后台登录接口"));
|
||||
return Err(
|
||||
AppError::from_status(StatusCode::BAD_REQUEST).with_message("禁止调试后台登录接口")
|
||||
);
|
||||
}
|
||||
Ok(trimmed.to_string())
|
||||
}
|
||||
@@ -540,6 +603,7 @@ fn build_admin_session_payload(session: crate::state::AdminSession) -> AdminSess
|
||||
}
|
||||
}
|
||||
|
||||
// 首版后台页面内嵌在 api-server,避免新增独立前端工程与静态资源发布链。
|
||||
static ADMIN_CONSOLE_HTML: &str = r#"<!doctype html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
@@ -1051,23 +1115,46 @@ static ADMIN_CONSOLE_HTML: &str = r#"<!doctype html>
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{build_body_preview, normalize_debug_path, trim_preview};
|
||||
use axum::http::StatusCode;
|
||||
use super::{build_body_preview, build_debug_base_url, normalize_debug_path, trim_preview};
|
||||
use axum::{http::StatusCode, response::IntoResponse};
|
||||
|
||||
#[test]
|
||||
fn normalize_debug_path_rejects_absolute_url() {
|
||||
let error = normalize_debug_path("https://example.com/api").expect_err("absolute url should fail");
|
||||
let error =
|
||||
normalize_debug_path("https://example.com/api").expect_err("absolute url should fail");
|
||||
|
||||
assert_eq!(error.into_response().status(), StatusCode::BAD_REQUEST);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn normalize_debug_path_rejects_admin_login_route() {
|
||||
let error = normalize_debug_path("/admin/api/login").expect_err("admin login route should fail");
|
||||
let error =
|
||||
normalize_debug_path("/admin/api/login").expect_err("admin login route should fail");
|
||||
|
||||
assert_eq!(error.into_response().status(), StatusCode::BAD_REQUEST);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn normalize_debug_path_accepts_healthz() {
|
||||
let path = normalize_debug_path("/healthz").expect("healthz path should pass validation");
|
||||
|
||||
assert_eq!(path, "/healthz");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn build_debug_base_url_rewrites_wildcard_ipv4_to_loopback() {
|
||||
let url = build_debug_base_url("0.0.0.0", 3200);
|
||||
|
||||
assert_eq!(url, "http://127.0.0.1:3200");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn build_debug_base_url_wraps_ipv6_host() {
|
||||
let url = build_debug_base_url("::1", 3200);
|
||||
|
||||
assert_eq!(url, "http://[::1]:3200");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn trim_preview_limits_length() {
|
||||
let text = "a".repeat(5000);
|
||||
|
||||
@@ -30,7 +30,7 @@ use crate::{
|
||||
require_bearer_auth,
|
||||
},
|
||||
auth_me::auth_me,
|
||||
auth_public_user::get_public_user_by_code,
|
||||
auth_public_user::{get_public_user_by_code, get_public_user_by_id},
|
||||
auth_sessions::auth_sessions,
|
||||
big_fish::{
|
||||
create_big_fish_session, execute_big_fish_action, get_big_fish_run, get_big_fish_session,
|
||||
@@ -159,6 +159,10 @@ pub fn build_router(state: AppState) -> Router {
|
||||
"/api/auth/public-users/by-code/{code}",
|
||||
get(get_public_user_by_code),
|
||||
)
|
||||
.route(
|
||||
"/api/auth/public-users/by-id/{user_id}",
|
||||
get(get_public_user_by_id),
|
||||
)
|
||||
.route(
|
||||
"/generated-character-drafts/{*path}",
|
||||
get(proxy_generated_character_drafts),
|
||||
@@ -959,8 +963,10 @@ mod tests {
|
||||
use platform_auth::{
|
||||
AccessTokenClaims, AccessTokenClaimsInput, AuthProvider, BindingStatus, sign_access_token,
|
||||
};
|
||||
use reqwest::Client;
|
||||
use serde_json::Value;
|
||||
use time::OffsetDateTime;
|
||||
use tokio::net::TcpListener;
|
||||
use tower::ServiceExt;
|
||||
|
||||
use crate::{config::AppConfig, state::AppState};
|
||||
@@ -1018,7 +1024,7 @@ mod tests {
|
||||
assert_eq!(payload["ok"], Value::Bool(true));
|
||||
assert_eq!(
|
||||
payload["service"],
|
||||
Value::String("genarrative-node-server".to_string())
|
||||
Value::String("genarrative-api-server".to_string())
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1052,7 +1058,7 @@ mod tests {
|
||||
assert_eq!(payload["ok"], Value::Bool(true));
|
||||
assert_eq!(
|
||||
payload["data"]["service"],
|
||||
Value::String("genarrative-node-server".to_string())
|
||||
Value::String("genarrative-api-server".to_string())
|
||||
);
|
||||
assert_eq!(
|
||||
payload["meta"]["requestId"],
|
||||
@@ -2986,7 +2992,10 @@ mod tests {
|
||||
serde_json::from_slice(&body).expect("response body should be valid json");
|
||||
|
||||
assert!(payload["token"].as_str().is_some());
|
||||
assert_eq!(payload["admin"]["username"], Value::String("root".to_string()));
|
||||
assert_eq!(
|
||||
payload["admin"]["username"],
|
||||
Value::String("root".to_string())
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
@@ -3043,49 +3052,78 @@ mod tests {
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn admin_debug_http_can_probe_healthz() {
|
||||
async fn admin_debug_http_can_probe_healthz_when_authenticated() {
|
||||
let mut config = AppConfig::default();
|
||||
config.admin_username = Some("root".to_string());
|
||||
config.admin_password = Some("secret123".to_string());
|
||||
let listener = TcpListener::bind("127.0.0.1:0")
|
||||
.await
|
||||
.expect("listener should bind");
|
||||
let local_addr = listener
|
||||
.local_addr()
|
||||
.expect("listener should expose local addr");
|
||||
config.bind_host = "127.0.0.1".to_string();
|
||||
config.bind_port = local_addr.port();
|
||||
let app = build_router(AppState::new(config).expect("state should build"));
|
||||
let server = tokio::spawn(async move {
|
||||
axum::serve(listener, app)
|
||||
.await
|
||||
.expect("test admin server should serve");
|
||||
});
|
||||
let http_client = Client::new();
|
||||
let base_url = format!("http://{}", local_addr);
|
||||
|
||||
let login_response = app
|
||||
.clone()
|
||||
.oneshot(
|
||||
Request::builder()
|
||||
.method("POST")
|
||||
.uri("/admin/api/login")
|
||||
.header("content-type", "application/json")
|
||||
.body(Body::from(
|
||||
serde_json::json!({
|
||||
"username": "root",
|
||||
"password": "secret123"
|
||||
})
|
||||
.to_string(),
|
||||
))
|
||||
.expect("login request should build"),
|
||||
)
|
||||
let login_payload: Value = http_client
|
||||
.post(format!("{base_url}/admin/api/login"))
|
||||
.json(&serde_json::json!({
|
||||
"username": "root",
|
||||
"password": "secret123"
|
||||
}))
|
||||
.send()
|
||||
.await
|
||||
.expect("login should succeed");
|
||||
let login_body = login_response
|
||||
.into_body()
|
||||
.collect()
|
||||
.expect("login request should succeed")
|
||||
.json()
|
||||
.await
|
||||
.expect("login body should collect")
|
||||
.to_bytes();
|
||||
let login_payload: Value =
|
||||
serde_json::from_slice(&login_body).expect("login payload should be json");
|
||||
.expect("login payload should be json");
|
||||
let access_token = login_payload["token"]
|
||||
.as_str()
|
||||
.expect("token should exist")
|
||||
.to_string();
|
||||
|
||||
let payload: Value = http_client
|
||||
.post(format!("{base_url}/admin/api/debug/http"))
|
||||
.bearer_auth(access_token)
|
||||
.json(&serde_json::json!({
|
||||
"method": "GET",
|
||||
"path": "/healthz",
|
||||
"headers": [],
|
||||
"body": ""
|
||||
}))
|
||||
.send()
|
||||
.await
|
||||
.expect("debug request should succeed")
|
||||
.json()
|
||||
.await
|
||||
.expect("debug payload should be json");
|
||||
|
||||
server.abort();
|
||||
let _ = server.await;
|
||||
|
||||
assert_eq!(payload["status"], Value::Number(200.into()));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn admin_debug_http_requires_authentication() {
|
||||
let mut config = AppConfig::default();
|
||||
config.admin_username = Some("root".to_string());
|
||||
config.admin_password = Some("secret123".to_string());
|
||||
let app = build_router(AppState::new(config).expect("state should build"));
|
||||
|
||||
let debug_response = app
|
||||
.oneshot(
|
||||
Request::builder()
|
||||
.method("POST")
|
||||
.uri("/admin/api/debug/http")
|
||||
.header("authorization", format!("Bearer {access_token}"))
|
||||
.header("content-type", "application/json")
|
||||
.body(Body::from(
|
||||
serde_json::json!({
|
||||
@@ -3101,16 +3139,6 @@ mod tests {
|
||||
.await
|
||||
.expect("debug request should succeed");
|
||||
|
||||
assert_eq!(debug_response.status(), StatusCode::OK);
|
||||
let body = debug_response
|
||||
.into_body()
|
||||
.collect()
|
||||
.await
|
||||
.expect("debug body should collect")
|
||||
.to_bytes();
|
||||
let payload: Value =
|
||||
serde_json::from_slice(&body).expect("debug payload should be json");
|
||||
|
||||
assert_eq!(payload["status"], Value::Number(200.into()));
|
||||
assert_eq!(debug_response.status(), StatusCode::UNAUTHORIZED);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,11 +6,8 @@ use axum::{
|
||||
use shared_contracts::auth::PublicUserSearchResponse;
|
||||
|
||||
use crate::{
|
||||
api_response::json_success_body,
|
||||
auth_payload::map_public_user_summary_payload,
|
||||
http_error::AppError,
|
||||
request_context::RequestContext,
|
||||
state::AppState,
|
||||
api_response::json_success_body, auth_payload::map_public_user_summary_payload,
|
||||
http_error::AppError, request_context::RequestContext, state::AppState,
|
||||
};
|
||||
|
||||
pub async fn get_public_user_by_code(
|
||||
@@ -34,6 +31,32 @@ pub async fn get_public_user_by_code(
|
||||
))
|
||||
}
|
||||
|
||||
pub async fn get_public_user_by_id(
|
||||
State(state): State<AppState>,
|
||||
Extension(request_context): Extension<RequestContext>,
|
||||
Path(user_id): Path<String>,
|
||||
) -> Result<Json<serde_json::Value>, AppError> {
|
||||
let user_id = user_id.trim();
|
||||
if user_id.is_empty() {
|
||||
return Err(AppError::from_status(StatusCode::BAD_REQUEST).with_message("用户 ID 不能为空"));
|
||||
}
|
||||
|
||||
let user = state
|
||||
.auth_user_service()
|
||||
.get_user_by_id(user_id)
|
||||
.map_err(map_public_user_id_search_error)?
|
||||
.ok_or_else(|| {
|
||||
AppError::from_status(StatusCode::NOT_FOUND).with_message("未找到对应用户")
|
||||
})?;
|
||||
|
||||
Ok(json_success_body(
|
||||
Some(&request_context),
|
||||
PublicUserSearchResponse {
|
||||
user: map_public_user_summary_payload(user),
|
||||
},
|
||||
))
|
||||
}
|
||||
|
||||
fn map_public_user_search_error(error: module_auth::PasswordEntryError) -> AppError {
|
||||
match error {
|
||||
module_auth::PasswordEntryError::InvalidPublicUserCode => {
|
||||
@@ -48,3 +71,14 @@ fn map_public_user_search_error(error: module_auth::PasswordEntryError) -> AppEr
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn map_public_user_id_search_error(error: module_auth::LogoutError) -> AppError {
|
||||
match error {
|
||||
module_auth::LogoutError::UserNotFound => {
|
||||
AppError::from_status(StatusCode::NOT_FOUND).with_message("未找到对应用户")
|
||||
}
|
||||
module_auth::LogoutError::Store(_) => {
|
||||
AppError::from_status(StatusCode::INTERNAL_SERVER_ERROR).with_message(error.to_string())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,9 +13,7 @@ use module_assets::{
|
||||
AssetObjectAccessPolicy, AssetObjectFieldError, build_asset_entity_binding_input,
|
||||
build_asset_object_upsert_input, generate_asset_binding_id, generate_asset_object_id,
|
||||
};
|
||||
use platform_oss::{
|
||||
LegacyAssetPrefix, OssHeadObjectRequest, OssObjectAccess, OssPutObjectRequest,
|
||||
};
|
||||
use platform_oss::{LegacyAssetPrefix, OssHeadObjectRequest, OssObjectAccess, OssPutObjectRequest};
|
||||
use serde_json::{Map, Value, json};
|
||||
use shared_contracts::big_fish::{
|
||||
BigFishActionResponse, BigFishAgentMessageResponse, BigFishAnchorItemResponse,
|
||||
@@ -711,8 +709,7 @@ struct BigFishFormalAssetContext {
|
||||
|
||||
const BIG_FISH_TEXT_TO_IMAGE_MODEL: &str = "wan2.2-t2i-flash";
|
||||
const BIG_FISH_ENTITY_KIND: &str = "big_fish_session";
|
||||
const BIG_FISH_DEFAULT_NEGATIVE_PROMPT: &str =
|
||||
"文字,水印,logo,UI界面,对话框,边框,多余肢体,畸形鱼体,低清晰度,模糊,压缩噪点,现代摄影棚,写实照片背景";
|
||||
const BIG_FISH_DEFAULT_NEGATIVE_PROMPT: &str = "文字,水印,logo,UI界面,对话框,边框,多余肢体,畸形鱼体,低清晰度,模糊,压缩噪点,现代摄影棚,写实照片背景";
|
||||
|
||||
async fn generate_big_fish_formal_asset(
|
||||
state: &AppState,
|
||||
@@ -839,10 +836,12 @@ fn build_big_fish_formal_asset_context(
|
||||
asset_id,
|
||||
],
|
||||
}),
|
||||
_ => Err(AppError::from_status(StatusCode::BAD_REQUEST).with_details(json!({
|
||||
"provider": "big-fish",
|
||||
"message": format!("assetKind `{asset_kind}` 不支持正式图片生成。"),
|
||||
}))),
|
||||
_ => Err(
|
||||
AppError::from_status(StatusCode::BAD_REQUEST).with_details(json!({
|
||||
"provider": "big-fish",
|
||||
"message": format!("assetKind `{asset_kind}` 不支持正式图片生成。"),
|
||||
})),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1028,9 +1027,9 @@ async fn create_big_fish_text_to_image_generation(
|
||||
}))
|
||||
.send()
|
||||
.await
|
||||
.map_err(|error| map_big_fish_dashscope_request_error(format!(
|
||||
"创建 Big Fish 图片生成任务失败:{error}"
|
||||
)))?;
|
||||
.map_err(|error| {
|
||||
map_big_fish_dashscope_request_error(format!("创建 Big Fish 图片生成任务失败:{error}"))
|
||||
})?;
|
||||
let status = response.status();
|
||||
let response_text = response.text().await.map_err(|error| {
|
||||
map_big_fish_dashscope_request_error(format!("读取 Big Fish 图片生成响应失败:{error}"))
|
||||
@@ -1041,7 +1040,8 @@ async fn create_big_fish_text_to_image_generation(
|
||||
"创建 Big Fish 图片生成任务失败",
|
||||
));
|
||||
}
|
||||
let payload = parse_big_fish_json_payload(response_text.as_str(), "解析 Big Fish 图片生成响应失败")?;
|
||||
let payload =
|
||||
parse_big_fish_json_payload(response_text.as_str(), "解析 Big Fish 图片生成响应失败")?;
|
||||
let task_id = extract_big_fish_task_id(&payload).ok_or_else(|| {
|
||||
AppError::from_status(StatusCode::BAD_GATEWAY).with_details(json!({
|
||||
"provider": "dashscope",
|
||||
@@ -1059,9 +1059,11 @@ async fn create_big_fish_text_to_image_generation(
|
||||
)
|
||||
.send()
|
||||
.await
|
||||
.map_err(|error| map_big_fish_dashscope_request_error(format!(
|
||||
"查询 Big Fish 图片生成任务失败:{error}"
|
||||
)))?;
|
||||
.map_err(|error| {
|
||||
map_big_fish_dashscope_request_error(format!(
|
||||
"查询 Big Fish 图片生成任务失败:{error}"
|
||||
))
|
||||
})?;
|
||||
let poll_status = poll_response.status();
|
||||
let poll_text = poll_response.text().await.map_err(|error| {
|
||||
map_big_fish_dashscope_request_error(format!(
|
||||
@@ -1115,11 +1117,9 @@ async fn download_big_fish_remote_image(
|
||||
image_url: &str,
|
||||
fallback_message: &str,
|
||||
) -> Result<BigFishDownloadedImage, AppError> {
|
||||
let response = http_client
|
||||
.get(image_url)
|
||||
.send()
|
||||
.await
|
||||
.map_err(|error| map_big_fish_dashscope_request_error(format!("{fallback_message}:{error}")))?;
|
||||
let response = http_client.get(image_url).send().await.map_err(|error| {
|
||||
map_big_fish_dashscope_request_error(format!("{fallback_message}:{error}"))
|
||||
})?;
|
||||
let status = response.status();
|
||||
let content_type = response
|
||||
.headers()
|
||||
@@ -1127,10 +1127,9 @@ async fn download_big_fish_remote_image(
|
||||
.and_then(|value| value.to_str().ok())
|
||||
.unwrap_or("image/jpeg")
|
||||
.to_string();
|
||||
let bytes = response
|
||||
.bytes()
|
||||
.await
|
||||
.map_err(|error| map_big_fish_dashscope_request_error(format!("{fallback_message}:{error}")))?;
|
||||
let bytes = response.bytes().await.map_err(|error| {
|
||||
map_big_fish_dashscope_request_error(format!("{fallback_message}:{error}"))
|
||||
})?;
|
||||
if !status.is_success() {
|
||||
return Err(
|
||||
AppError::from_status(StatusCode::BAD_GATEWAY).with_details(json!({
|
||||
|
||||
@@ -735,7 +735,7 @@ async fn persist_animation_preview_video(
|
||||
"provider": "character-animation",
|
||||
"message": "当前策略需要真实生成视频结果,不再支持回退到仓库占位预览视频。",
|
||||
})),
|
||||
)
|
||||
);
|
||||
}
|
||||
};
|
||||
let put_result = put_character_animation_object(
|
||||
@@ -1005,7 +1005,9 @@ async fn send_ark_image_to_video_request(
|
||||
}))
|
||||
.send()
|
||||
.await
|
||||
.map_err(|error| map_character_animation_upstream_error(format!("请求 Ark 视频服务失败:{error}")))
|
||||
.map_err(|error| {
|
||||
map_character_animation_upstream_error(format!("请求 Ark 视频服务失败:{error}"))
|
||||
})
|
||||
}
|
||||
|
||||
async fn wait_for_ark_content_generation_task(
|
||||
@@ -1026,7 +1028,9 @@ async fn wait_for_ark_content_generation_task(
|
||||
)
|
||||
.send()
|
||||
.await
|
||||
.map_err(|error| map_character_animation_upstream_error(format!("查询 Ark 视频任务失败:{error}")))?;
|
||||
.map_err(|error| {
|
||||
map_character_animation_upstream_error(format!("查询 Ark 视频任务失败:{error}"))
|
||||
})?;
|
||||
let status = response.status();
|
||||
let text = response.text().await.map_err(|error| {
|
||||
map_character_animation_upstream_error(format!("读取 Ark 视频任务响应失败:{error}"))
|
||||
@@ -1062,11 +1066,13 @@ async fn wait_for_ark_content_generation_task(
|
||||
sleep(Duration::from_millis(ARK_VIDEO_TASK_POLL_INTERVAL_MS)).await;
|
||||
}
|
||||
|
||||
Err(AppError::from_status(StatusCode::BAD_GATEWAY).with_details(json!({
|
||||
"provider": "ark",
|
||||
"message": "视频生成任务执行超时,请稍后重试。",
|
||||
"taskId": task_id,
|
||||
})))
|
||||
Err(
|
||||
AppError::from_status(StatusCode::BAD_GATEWAY).with_details(json!({
|
||||
"provider": "ark",
|
||||
"message": "视频生成任务执行超时,请稍后重试。",
|
||||
"taskId": task_id,
|
||||
})),
|
||||
)
|
||||
}
|
||||
|
||||
async fn download_generated_video(
|
||||
@@ -1074,11 +1080,9 @@ async fn download_generated_video(
|
||||
video_url: &str,
|
||||
fallback_message: &str,
|
||||
) -> Result<MediaPayload, AppError> {
|
||||
let response = http_client
|
||||
.get(video_url)
|
||||
.send()
|
||||
.await
|
||||
.map_err(|error| map_character_animation_upstream_error(format!("{fallback_message}:{error}")))?;
|
||||
let response = http_client.get(video_url).send().await.map_err(|error| {
|
||||
map_character_animation_upstream_error(format!("{fallback_message}:{error}"))
|
||||
})?;
|
||||
let status = response.status();
|
||||
let content_type = response
|
||||
.headers()
|
||||
@@ -1090,11 +1094,13 @@ async fn download_generated_video(
|
||||
map_character_animation_upstream_error(format!("{fallback_message}:{error}"))
|
||||
})?;
|
||||
if !status.is_success() {
|
||||
return Err(AppError::from_status(StatusCode::BAD_GATEWAY).with_details(json!({
|
||||
"provider": "character-animation",
|
||||
"message": fallback_message,
|
||||
"status": status.as_u16(),
|
||||
})));
|
||||
return Err(
|
||||
AppError::from_status(StatusCode::BAD_GATEWAY).with_details(json!({
|
||||
"provider": "character-animation",
|
||||
"message": fallback_message,
|
||||
"status": status.as_u16(),
|
||||
})),
|
||||
);
|
||||
}
|
||||
Ok(MediaPayload {
|
||||
mime_type: content_type.clone(),
|
||||
@@ -1728,12 +1734,9 @@ fn build_character_animation_prompt(
|
||||
loop_,
|
||||
use_chroma_key,
|
||||
),
|
||||
CharacterAnimationStrategy::ImageSequence => build_image_sequence_prompt(
|
||||
animation,
|
||||
prompt_text,
|
||||
frame_count,
|
||||
use_chroma_key,
|
||||
),
|
||||
CharacterAnimationStrategy::ImageSequence => {
|
||||
build_image_sequence_prompt(animation, prompt_text, frame_count, use_chroma_key)
|
||||
}
|
||||
CharacterAnimationStrategy::MotionTransfer
|
||||
| CharacterAnimationStrategy::ReferenceToVideo => build_npc_animation_prompt(
|
||||
animation,
|
||||
@@ -1755,7 +1758,10 @@ fn build_image_sequence_prompt(
|
||||
use_chroma_key: bool,
|
||||
) -> String {
|
||||
[
|
||||
format!("同一角色连续 {} 帧动作序列,动作主题是 {}。", frame_count, animation),
|
||||
format!(
|
||||
"同一角色连续 {} 帧动作序列,动作主题是 {}。",
|
||||
frame_count, animation
|
||||
),
|
||||
"固定机位,单人,全身,侧身朝右,保持同一套服装、发型、武器和体型。".to_string(),
|
||||
"帧间动作连续,姿态逐步推进,不要换人,不要跳变,不要多余物体。".to_string(),
|
||||
if use_chroma_key {
|
||||
@@ -1784,16 +1790,16 @@ fn build_npc_animation_prompt(
|
||||
let character_brief = build_compact_animation_character_brief(character_brief_text);
|
||||
let action_detail_text = sanitize_animation_prompt_text(prompt_text, 140);
|
||||
let loop_rule = if loop_ {
|
||||
"这是循环动作,直接进入动作循环中段,不要开场静止站桩,不要把主参考图原样作为第一帧。".to_string()
|
||||
"这是循环动作,直接进入动作循环中段,不要开场静止站桩,不要把主参考图原样作为第一帧。"
|
||||
.to_string()
|
||||
} else if animation == "die" {
|
||||
"这是死亡终结动作,首帧参考主图角色形象即可,尾帧停在死亡结束姿态,不要回到主图形象。".to_string()
|
||||
"这是死亡终结动作,首帧参考主图角色形象即可,尾帧停在死亡结束姿态,不要回到主图形象。"
|
||||
.to_string()
|
||||
} else {
|
||||
"这是非循环动作,首帧和尾帧都要回到参考主图角色形象,中段完成动作变化。".to_string()
|
||||
};
|
||||
|
||||
if let Some(template) = action_template_id
|
||||
.and_then(|id| find_motion_template(id))
|
||||
{
|
||||
if let Some(template) = action_template_id.and_then(|id| find_motion_template(id)) {
|
||||
return [
|
||||
format!(
|
||||
"单人 NPC 全身动作视频,动作主题是 {}。角色固定为同一人,右向斜侧身,镜头稳定,轮廓清晰,武器不可丢失。",
|
||||
@@ -1843,7 +1849,11 @@ fn build_npc_animation_prompt(
|
||||
} else {
|
||||
action_detail_text
|
||||
},
|
||||
format!("目标帧率 {} fps,时长约 {} 秒。", fps.clamp(1, 60), duration_seconds.clamp(1, 8)),
|
||||
format!(
|
||||
"目标帧率 {} fps,时长约 {} 秒。",
|
||||
fps.clamp(1, 60),
|
||||
duration_seconds.clamp(1, 8)
|
||||
),
|
||||
loop_rule,
|
||||
]
|
||||
.into_iter()
|
||||
@@ -1906,8 +1916,12 @@ fn build_ark_character_animation_prompt(
|
||||
}
|
||||
|
||||
[
|
||||
format!("单人 NPC 全身动作视频,动作英文名是 {}。", normalized_animation_name),
|
||||
"角色固定为图片1和图片2中的同一人,侧身朝右,镜头稳定,轮廓清晰,武器不可丢失。".to_string(),
|
||||
format!(
|
||||
"单人 NPC 全身动作视频,动作英文名是 {}。",
|
||||
normalized_animation_name
|
||||
),
|
||||
"角色固定为图片1和图片2中的同一人,侧身朝右,镜头稳定,轮廓清晰,武器不可丢失。"
|
||||
.to_string(),
|
||||
"动作连贯,避免服装、发型、面部、武器随机漂移,不要多角色,不要镜头切换。".to_string(),
|
||||
if use_chroma_key {
|
||||
"背景为纯绿色绿幕,无其他人物和场景元素,方便后期抠像。".to_string()
|
||||
@@ -2341,11 +2355,7 @@ fn finalize_animation_frame_payload(
|
||||
);
|
||||
}
|
||||
|
||||
let normalized = contain_rgba_image(
|
||||
&image,
|
||||
frame_width.max(1),
|
||||
frame_height.max(1),
|
||||
);
|
||||
let normalized = contain_rgba_image(&image, frame_width.max(1), frame_height.max(1));
|
||||
let mut encoded = Vec::new();
|
||||
let encoder = PngEncoder::new(&mut encoded);
|
||||
encoder
|
||||
@@ -2665,7 +2675,14 @@ fn is_completed_generation_task_status(status: &str) -> bool {
|
||||
fn is_failed_generation_task_status(status: &str) -> bool {
|
||||
matches!(
|
||||
status,
|
||||
"failed" | "canceled" | "cancelled" | "error" | "aborted" | "rejected" | "expired" | "unknown"
|
||||
"failed"
|
||||
| "canceled"
|
||||
| "cancelled"
|
||||
| "error"
|
||||
| "aborted"
|
||||
| "rejected"
|
||||
| "expired"
|
||||
| "unknown"
|
||||
)
|
||||
}
|
||||
|
||||
@@ -3110,9 +3127,8 @@ fn remove_background_from_rgba(pixels: &mut [u8], width: usize, height: usize) -
|
||||
}
|
||||
let offset = pixel_index * 4;
|
||||
let alpha = pixels[offset + 3];
|
||||
let strong_candidate = alpha < 40
|
||||
|| green_scores[pixel_index] > 0.12
|
||||
|| white_scores[pixel_index] > 0.32;
|
||||
let strong_candidate =
|
||||
alpha < 40 || green_scores[pixel_index] > 0.12 || white_scores[pixel_index] > 0.32;
|
||||
if !strong_candidate {
|
||||
return;
|
||||
}
|
||||
@@ -3137,9 +3153,21 @@ fn remove_background_from_rgba(pixels: &mut [u8], width: usize, height: usize) -
|
||||
let y = pixel_index / width;
|
||||
let neighbor_indexes = [
|
||||
if x > 0 { Some(pixel_index - 1) } else { None },
|
||||
if x + 1 < width { Some(pixel_index + 1) } else { None },
|
||||
if y > 0 { Some(pixel_index - width) } else { None },
|
||||
if y + 1 < height { Some(pixel_index + width) } else { None },
|
||||
if x + 1 < width {
|
||||
Some(pixel_index + 1)
|
||||
} else {
|
||||
None
|
||||
},
|
||||
if y > 0 {
|
||||
Some(pixel_index - width)
|
||||
} else {
|
||||
None
|
||||
},
|
||||
if y + 1 < height {
|
||||
Some(pixel_index + width)
|
||||
} else {
|
||||
None
|
||||
},
|
||||
];
|
||||
|
||||
for next_pixel_index in neighbor_indexes.into_iter().flatten() {
|
||||
@@ -3153,9 +3181,7 @@ fn remove_background_from_rgba(pixels: &mut [u8], width: usize, height: usize) -
|
||||
let next_hint = background_hints[next_pixel_index];
|
||||
let reachable_soft_edge = next_hint > 0.08
|
||||
&& next_alpha < SOFT_EDGE_ALPHA_THRESHOLD
|
||||
&& (next_green_score > 0.04
|
||||
|| next_white_score > 0.08
|
||||
|| next_alpha < 180);
|
||||
&& (next_green_score > 0.04 || next_white_score > 0.08 || next_alpha < 180);
|
||||
|
||||
if next_alpha < 40
|
||||
|| next_green_score > 0.12
|
||||
@@ -3203,8 +3229,7 @@ fn remove_background_from_rgba(pixels: &mut [u8], width: usize, height: usize) -
|
||||
}
|
||||
}
|
||||
|
||||
if adjacent_background_count >= 2
|
||||
|| (adjacent_background_count >= 1 && hint > 0.18)
|
||||
if adjacent_background_count >= 2 || (adjacent_background_count >= 1 && hint > 0.18)
|
||||
{
|
||||
expanded_mask[pixel_index] = 1;
|
||||
}
|
||||
@@ -3237,10 +3262,7 @@ fn remove_background_from_rgba(pixels: &mut [u8], width: usize, height: usize) -
|
||||
}
|
||||
let next_x = x as i32 + offset_x;
|
||||
let next_y = y as i32 + offset_y;
|
||||
if next_x < 0
|
||||
|| next_x >= width as i32
|
||||
|| next_y < 0
|
||||
|| next_y >= height as i32
|
||||
if next_x < 0 || next_x >= width as i32 || next_y < 0 || next_y >= height as i32
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -3295,10 +3317,7 @@ fn remove_background_from_rgba(pixels: &mut [u8], width: usize, height: usize) -
|
||||
}
|
||||
let next_x = x as i32 + offset_x;
|
||||
let next_y = y as i32 + offset_y;
|
||||
if next_x < 0
|
||||
|| next_x >= width as i32
|
||||
|| next_y < 0
|
||||
|| next_y >= height as i32
|
||||
if next_x < 0 || next_x >= width as i32 || next_y < 0 || next_y >= height as i32
|
||||
{
|
||||
touches_transparent_edge = true;
|
||||
continue;
|
||||
@@ -3320,7 +3339,11 @@ fn remove_background_from_rgba(pixels: &mut [u8], width: usize, height: usize) -
|
||||
let white_score = white_scores[pixel_index];
|
||||
let contamination = green_score
|
||||
.max(white_score)
|
||||
.max(if background_mask[pixel_index] != 0 { 0.35 } else { 0.0 })
|
||||
.max(if background_mask[pixel_index] != 0 {
|
||||
0.35
|
||||
} else {
|
||||
0.0
|
||||
})
|
||||
.max(if alpha < 220 {
|
||||
((220 - alpha) as f32 / 220.0) * 0.25
|
||||
} else {
|
||||
@@ -3343,7 +3366,8 @@ fn remove_background_from_rgba(pixels: &mut [u8], width: usize, height: usize) -
|
||||
&background_mask,
|
||||
&background_hints,
|
||||
);
|
||||
let blend = clamp01(contamination.max(if touches_transparent_edge { 0.22 } else { 0.0 }));
|
||||
let blend =
|
||||
clamp01(contamination.max(if touches_transparent_edge { 0.22 } else { 0.0 }));
|
||||
|
||||
if let Some((sample_red, sample_green, sample_blue)) = sample {
|
||||
red = lerp(red, sample_red as f32, blend);
|
||||
@@ -3360,7 +3384,8 @@ fn remove_background_from_rgba(pixels: &mut [u8], width: usize, height: usize) -
|
||||
}
|
||||
} else {
|
||||
if green_score > 0.04 {
|
||||
green = green.max(red.max(blue))
|
||||
green = green
|
||||
.max(red.max(blue))
|
||||
.max((green - (green - red.max(blue)) * 0.78).round());
|
||||
}
|
||||
|
||||
|
||||
@@ -104,11 +104,11 @@ pub async fn generate_character_visual(
|
||||
text_output: Some(prompt.clone()),
|
||||
structured_payload_json: Some(
|
||||
json!({
|
||||
"characterId": character_id,
|
||||
"sourceMode": payload.source_mode,
|
||||
"size": size,
|
||||
"referenceImageCount": payload.reference_image_data_urls.len(),
|
||||
})
|
||||
"characterId": character_id,
|
||||
"sourceMode": payload.source_mode,
|
||||
"size": size,
|
||||
"referenceImageCount": payload.reference_image_data_urls.len(),
|
||||
})
|
||||
.to_string(),
|
||||
),
|
||||
warning_messages: Vec::new(),
|
||||
@@ -832,12 +832,9 @@ async fn resolve_reference_image_as_data_url(
|
||||
.and_then(|value| value.to_str().ok())
|
||||
.unwrap_or("image/png")
|
||||
.to_string();
|
||||
let body = response
|
||||
.bytes()
|
||||
.await
|
||||
.map_err(|error| {
|
||||
map_dashscope_request_error(format!("读取角色主形象参考图内容失败:{error}"))
|
||||
})?;
|
||||
let body = response.bytes().await.map_err(|error| {
|
||||
map_dashscope_request_error(format!("读取角色主形象参考图内容失败:{error}"))
|
||||
})?;
|
||||
if !status.is_success() {
|
||||
return Err(
|
||||
AppError::from_status(StatusCode::BAD_GATEWAY).with_details(json!({
|
||||
@@ -911,9 +908,7 @@ async fn create_character_visual_generation(
|
||||
}))
|
||||
.send()
|
||||
.await
|
||||
.map_err(|error| {
|
||||
map_dashscope_request_error(format!("创建角色主形象任务失败:{error}"))
|
||||
})?;
|
||||
.map_err(|error| map_dashscope_request_error(format!("创建角色主形象任务失败:{error}")))?;
|
||||
let response_status = response.status();
|
||||
let response_text = response.text().await.map_err(|error| {
|
||||
map_dashscope_request_error(format!("读取角色主形象任务响应失败:{error}"))
|
||||
@@ -963,19 +958,23 @@ async fn create_character_visual_generation(
|
||||
if task_status == "SUCCEEDED" {
|
||||
let image_urls = extract_image_urls(&poll_json.payload);
|
||||
if image_urls.is_empty() {
|
||||
return Err(AppError::from_status(StatusCode::BAD_GATEWAY).with_details(
|
||||
json!({
|
||||
return Err(
|
||||
AppError::from_status(StatusCode::BAD_GATEWAY).with_details(json!({
|
||||
"provider": "dashscope",
|
||||
"message": "角色主形象生成成功,但没有返回可下载图片。",
|
||||
}),
|
||||
));
|
||||
})),
|
||||
);
|
||||
}
|
||||
|
||||
let mut images = Vec::with_capacity(image_urls.len());
|
||||
for image_url in image_urls {
|
||||
images.push(
|
||||
download_generated_image(http_client, image_url.as_str(), "下载角色主形象候选图失败。")
|
||||
.await?,
|
||||
download_generated_image(
|
||||
http_client,
|
||||
image_url.as_str(),
|
||||
"下载角色主形象候选图失败。",
|
||||
)
|
||||
.await?,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -992,13 +991,18 @@ async fn create_character_visual_generation(
|
||||
));
|
||||
}
|
||||
|
||||
sleep(Duration::from_millis(CHARACTER_VISUAL_TASK_POLL_INTERVAL_MS)).await;
|
||||
sleep(Duration::from_millis(
|
||||
CHARACTER_VISUAL_TASK_POLL_INTERVAL_MS,
|
||||
))
|
||||
.await;
|
||||
}
|
||||
|
||||
Err(AppError::from_status(StatusCode::BAD_GATEWAY).with_details(json!({
|
||||
"provider": "dashscope",
|
||||
"message": "角色主形象任务执行超时,请稍后重试。",
|
||||
})))
|
||||
Err(
|
||||
AppError::from_status(StatusCode::BAD_GATEWAY).with_details(json!({
|
||||
"provider": "dashscope",
|
||||
"message": "角色主形象任务执行超时,请稍后重试。",
|
||||
})),
|
||||
)
|
||||
}
|
||||
|
||||
async fn download_generated_image(
|
||||
@@ -1023,11 +1027,13 @@ async fn download_generated_image(
|
||||
.await
|
||||
.map_err(|error| map_dashscope_request_error(format!("{fallback_message}:{error}")))?;
|
||||
if !status.is_success() {
|
||||
return Err(AppError::from_status(StatusCode::BAD_GATEWAY).with_details(json!({
|
||||
"provider": "dashscope",
|
||||
"message": fallback_message,
|
||||
"status": status.as_u16(),
|
||||
})));
|
||||
return Err(
|
||||
AppError::from_status(StatusCode::BAD_GATEWAY).with_details(json!({
|
||||
"provider": "dashscope",
|
||||
"message": fallback_message,
|
||||
"status": status.as_u16(),
|
||||
})),
|
||||
);
|
||||
}
|
||||
|
||||
let normalized_mime_type = normalize_downloaded_image_mime_type(content_type.as_str());
|
||||
@@ -1244,7 +1250,10 @@ fn map_character_visual_oss_error(error: platform_oss::OssError) -> AppError {
|
||||
}))
|
||||
}
|
||||
|
||||
fn parse_json_payload(raw_text: &str, fallback_message: &str) -> Result<ParsedJsonPayload, AppError> {
|
||||
fn parse_json_payload(
|
||||
raw_text: &str,
|
||||
fallback_message: &str,
|
||||
) -> Result<ParsedJsonPayload, AppError> {
|
||||
serde_json::from_str::<Value>(raw_text)
|
||||
.map(|payload| ParsedJsonPayload { payload })
|
||||
.map_err(|error| {
|
||||
@@ -1541,11 +1550,7 @@ fn collect_foreground_neighbor_color(
|
||||
))
|
||||
}
|
||||
|
||||
pub(crate) fn remove_background_from_rgba(
|
||||
pixels: &mut [u8],
|
||||
width: usize,
|
||||
height: usize,
|
||||
) -> bool {
|
||||
pub(crate) fn remove_background_from_rgba(pixels: &mut [u8], width: usize, height: usize) -> bool {
|
||||
const SOFT_EDGE_ALPHA_THRESHOLD: u8 = 224;
|
||||
const FOREGROUND_NEIGHBOR_ALPHA_THRESHOLD: u8 = 96;
|
||||
|
||||
@@ -1574,26 +1579,24 @@ pub(crate) fn remove_background_from_rgba(
|
||||
|
||||
green_scores[pixel_index] = green_score;
|
||||
white_scores[pixel_index] = white_score;
|
||||
background_hints[pixel_index] =
|
||||
green_score.max(white_score).max(transparency_hint);
|
||||
background_hints[pixel_index] = green_score.max(white_score).max(transparency_hint);
|
||||
}
|
||||
|
||||
let try_seed_background =
|
||||
|pixel_index: usize, background_mask: &mut [u8], queue: &mut Vec<usize>| {
|
||||
if background_mask[pixel_index] != 0 {
|
||||
return;
|
||||
}
|
||||
let offset = pixel_index * 4;
|
||||
let alpha = pixels[offset + 3];
|
||||
let strong_candidate = alpha < 40
|
||||
|| green_scores[pixel_index] > 0.12
|
||||
|| white_scores[pixel_index] > 0.32;
|
||||
if !strong_candidate {
|
||||
return;
|
||||
}
|
||||
background_mask[pixel_index] = 1;
|
||||
queue.push(pixel_index);
|
||||
};
|
||||
if background_mask[pixel_index] != 0 {
|
||||
return;
|
||||
}
|
||||
let offset = pixel_index * 4;
|
||||
let alpha = pixels[offset + 3];
|
||||
let strong_candidate =
|
||||
alpha < 40 || green_scores[pixel_index] > 0.12 || white_scores[pixel_index] > 0.32;
|
||||
if !strong_candidate {
|
||||
return;
|
||||
}
|
||||
background_mask[pixel_index] = 1;
|
||||
queue.push(pixel_index);
|
||||
};
|
||||
|
||||
for x in 0..width {
|
||||
try_seed_background(x, &mut background_mask, &mut queue);
|
||||
@@ -1612,9 +1615,21 @@ pub(crate) fn remove_background_from_rgba(
|
||||
let y = pixel_index / width;
|
||||
let neighbor_indexes = [
|
||||
if x > 0 { Some(pixel_index - 1) } else { None },
|
||||
if x + 1 < width { Some(pixel_index + 1) } else { None },
|
||||
if y > 0 { Some(pixel_index - width) } else { None },
|
||||
if y + 1 < height { Some(pixel_index + width) } else { None },
|
||||
if x + 1 < width {
|
||||
Some(pixel_index + 1)
|
||||
} else {
|
||||
None
|
||||
},
|
||||
if y > 0 {
|
||||
Some(pixel_index - width)
|
||||
} else {
|
||||
None
|
||||
},
|
||||
if y + 1 < height {
|
||||
Some(pixel_index + width)
|
||||
} else {
|
||||
None
|
||||
},
|
||||
];
|
||||
|
||||
for next_pixel_index in neighbor_indexes.into_iter().flatten() {
|
||||
@@ -1628,9 +1643,7 @@ pub(crate) fn remove_background_from_rgba(
|
||||
let next_hint = background_hints[next_pixel_index];
|
||||
let reachable_soft_edge = next_hint > 0.08
|
||||
&& next_alpha < SOFT_EDGE_ALPHA_THRESHOLD
|
||||
&& (next_green_score > 0.04
|
||||
|| next_white_score > 0.08
|
||||
|| next_alpha < 180);
|
||||
&& (next_green_score > 0.04 || next_white_score > 0.08 || next_alpha < 180);
|
||||
|
||||
if next_alpha < 40
|
||||
|| next_green_score > 0.12
|
||||
@@ -1678,8 +1691,7 @@ pub(crate) fn remove_background_from_rgba(
|
||||
}
|
||||
}
|
||||
|
||||
if adjacent_background_count >= 2
|
||||
|| (adjacent_background_count >= 1 && hint > 0.18)
|
||||
if adjacent_background_count >= 2 || (adjacent_background_count >= 1 && hint > 0.18)
|
||||
{
|
||||
expanded_mask[pixel_index] = 1;
|
||||
}
|
||||
@@ -1712,10 +1724,7 @@ pub(crate) fn remove_background_from_rgba(
|
||||
}
|
||||
let next_x = x as i32 + offset_x;
|
||||
let next_y = y as i32 + offset_y;
|
||||
if next_x < 0
|
||||
|| next_x >= width as i32
|
||||
|| next_y < 0
|
||||
|| next_y >= height as i32
|
||||
if next_x < 0 || next_x >= width as i32 || next_y < 0 || next_y >= height as i32
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -1770,10 +1779,7 @@ pub(crate) fn remove_background_from_rgba(
|
||||
}
|
||||
let next_x = x as i32 + offset_x;
|
||||
let next_y = y as i32 + offset_y;
|
||||
if next_x < 0
|
||||
|| next_x >= width as i32
|
||||
|| next_y < 0
|
||||
|| next_y >= height as i32
|
||||
if next_x < 0 || next_x >= width as i32 || next_y < 0 || next_y >= height as i32
|
||||
{
|
||||
touches_transparent_edge = true;
|
||||
continue;
|
||||
@@ -1795,7 +1801,11 @@ pub(crate) fn remove_background_from_rgba(
|
||||
let white_score = white_scores[pixel_index];
|
||||
let contamination = green_score
|
||||
.max(white_score)
|
||||
.max(if background_mask[pixel_index] != 0 { 0.35 } else { 0.0 })
|
||||
.max(if background_mask[pixel_index] != 0 {
|
||||
0.35
|
||||
} else {
|
||||
0.0
|
||||
})
|
||||
.max(if alpha < 220 {
|
||||
((220 - alpha) as f32 / 220.0) * 0.25
|
||||
} else {
|
||||
@@ -1818,7 +1828,8 @@ pub(crate) fn remove_background_from_rgba(
|
||||
&background_mask,
|
||||
&background_hints,
|
||||
);
|
||||
let blend = clamp01(contamination.max(if touches_transparent_edge { 0.22 } else { 0.0 }));
|
||||
let blend =
|
||||
clamp01(contamination.max(if touches_transparent_edge { 0.22 } else { 0.0 }));
|
||||
|
||||
if let Some((sample_red, sample_green, sample_blue)) = sample {
|
||||
red = lerp(red, sample_red as f32, blend);
|
||||
@@ -1835,7 +1846,8 @@ pub(crate) fn remove_background_from_rgba(
|
||||
}
|
||||
} else {
|
||||
if green_score > 0.04 {
|
||||
green = green.max(red.max(blue))
|
||||
green = green
|
||||
.max(red.max(blue))
|
||||
.max((green - (green - red.max(blue)) * 0.78).round());
|
||||
}
|
||||
|
||||
|
||||
@@ -478,8 +478,7 @@ impl AppConfig {
|
||||
"ARK_CHARACTER_VIDEO_REQUEST_TIMEOUT_MS",
|
||||
"DASHSCOPE_CHARACTER_VIDEO_REQUEST_TIMEOUT_MS",
|
||||
]) {
|
||||
config.ark_character_video_request_timeout_ms =
|
||||
ark_character_video_request_timeout_ms;
|
||||
config.ark_character_video_request_timeout_ms = ark_character_video_request_timeout_ms;
|
||||
}
|
||||
|
||||
if let Some(ark_character_video_model) = read_first_non_empty_env(&[
|
||||
@@ -501,9 +500,9 @@ impl AppConfig {
|
||||
config.character_animation_ffprobe_path = character_animation_ffprobe_path;
|
||||
}
|
||||
|
||||
if let Some(character_animation_frame_extract_timeout_ms) = read_first_positive_u64_env(&[
|
||||
"CHARACTER_ANIMATION_FRAME_EXTRACT_TIMEOUT_MS",
|
||||
]) {
|
||||
if let Some(character_animation_frame_extract_timeout_ms) =
|
||||
read_first_positive_u64_env(&["CHARACTER_ANIMATION_FRAME_EXTRACT_TIMEOUT_MS"])
|
||||
{
|
||||
config.character_animation_frame_extract_timeout_ms =
|
||||
character_animation_frame_extract_timeout_ms;
|
||||
}
|
||||
|
||||
@@ -611,7 +611,10 @@ where
|
||||
empty_json_array()
|
||||
};
|
||||
let asset_coverage_json = if should_stay_in_draft_stage {
|
||||
serialize_json(&request.session.asset_coverage, &empty_agent_asset_coverage_json())
|
||||
serialize_json(
|
||||
&request.session.asset_coverage,
|
||||
&empty_agent_asset_coverage_json(),
|
||||
)
|
||||
} else {
|
||||
empty_agent_asset_coverage_json()
|
||||
};
|
||||
@@ -732,7 +735,10 @@ pub(crate) fn build_failed_finalize_record_input(
|
||||
stage: session.stage.clone(),
|
||||
progress_percent: session.progress_percent,
|
||||
focus_card_id: session.focus_card_id.clone(),
|
||||
anchor_content_json: serialize_json(&session.anchor_content, &empty_agent_anchor_content_json()),
|
||||
anchor_content_json: serialize_json(
|
||||
&session.anchor_content,
|
||||
&empty_agent_anchor_content_json(),
|
||||
),
|
||||
creator_intent_json: serialize_optional_json_object(&session.creator_intent),
|
||||
creator_intent_readiness_json: serialize_json(
|
||||
&session.creator_intent_readiness,
|
||||
@@ -753,7 +759,10 @@ pub(crate) fn build_failed_finalize_record_input(
|
||||
&JsonValue::Array(session.quality_findings.clone()),
|
||||
&empty_json_array(),
|
||||
),
|
||||
asset_coverage_json: serialize_json(&session.asset_coverage, &empty_agent_asset_coverage_json()),
|
||||
asset_coverage_json: serialize_json(
|
||||
&session.asset_coverage,
|
||||
&empty_agent_asset_coverage_json(),
|
||||
),
|
||||
error_message: Some(error_message),
|
||||
updated_at_micros,
|
||||
}
|
||||
@@ -771,13 +780,18 @@ async fn stream_single_turn<F>(
|
||||
where
|
||||
F: FnMut(&str),
|
||||
{
|
||||
let llm_client = llm_client.ok_or_else(|| {
|
||||
CustomWorldTurnError::new("当前模型不可用,请稍后重试。")
|
||||
})?;
|
||||
let llm_client =
|
||||
llm_client.ok_or_else(|| CustomWorldTurnError::new("当前模型不可用,请稍后重试。"))?;
|
||||
let chat_history = build_chat_history(messages);
|
||||
let dynamic_state =
|
||||
resolve_dynamic_state(llm_client, current_turn, progress_percent, quick_fill_requested, current_anchor_content, &chat_history)
|
||||
.await;
|
||||
let dynamic_state = resolve_dynamic_state(
|
||||
llm_client,
|
||||
current_turn,
|
||||
progress_percent,
|
||||
quick_fill_requested,
|
||||
current_anchor_content,
|
||||
&chat_history,
|
||||
)
|
||||
.await;
|
||||
let prompt = build_eight_anchor_single_turn_prompt(
|
||||
current_turn,
|
||||
progress_percent,
|
||||
@@ -806,27 +820,21 @@ where
|
||||
)
|
||||
.await;
|
||||
|
||||
let response = response.map_err(|_| {
|
||||
CustomWorldTurnError::new("这一轮设定生成失败,请稍后重试。")
|
||||
})?;
|
||||
let response =
|
||||
response.map_err(|_| CustomWorldTurnError::new("这一轮设定生成失败,请稍后重试。"))?;
|
||||
|
||||
let parsed = parse_json_response_text(response.content.as_str()).map_err(|_| {
|
||||
CustomWorldTurnError::new("模型返回结果解析失败,请稍后重试。")
|
||||
})?;
|
||||
let parsed = parse_json_response_text(response.content.as_str())
|
||||
.map_err(|_| CustomWorldTurnError::new("模型返回结果解析失败,请稍后重试。"))?;
|
||||
|
||||
let next_anchor_content = normalize_eight_anchor_content(
|
||||
parsed
|
||||
.get("nextAnchorContent")
|
||||
.unwrap_or(&JsonValue::Null),
|
||||
);
|
||||
let next_anchor_content =
|
||||
normalize_eight_anchor_content(parsed.get("nextAnchorContent").unwrap_or(&JsonValue::Null));
|
||||
let progress_percent = if quick_fill_requested {
|
||||
100
|
||||
} else {
|
||||
clamp_progress_percent(parsed.get("progressPercent"))
|
||||
};
|
||||
let reply_text = to_text(parsed.get("replyText")).ok_or_else(|| {
|
||||
CustomWorldTurnError::new("模型返回结果缺少有效回复,请稍后重试。")
|
||||
})?;
|
||||
let reply_text = to_text(parsed.get("replyText"))
|
||||
.ok_or_else(|| CustomWorldTurnError::new("模型返回结果缺少有效回复,请稍后重试。"))?;
|
||||
if reply_text != latest_reply_text {
|
||||
on_reply_update(reply_text.as_str());
|
||||
}
|
||||
@@ -907,13 +915,19 @@ fn build_prompt_dynamic_state(
|
||||
let Some(inference) = inference else {
|
||||
return fallback;
|
||||
};
|
||||
let user_input_signal = inference.user_input_signal.unwrap_or(fallback.user_input_signal);
|
||||
let user_input_signal = inference
|
||||
.user_input_signal
|
||||
.unwrap_or(fallback.user_input_signal);
|
||||
let drift_risk = inference.drift_risk.unwrap_or(fallback.drift_risk);
|
||||
let conversation_mode = inference.conversation_mode.unwrap_or(fallback.conversation_mode);
|
||||
let conversation_mode = inference
|
||||
.conversation_mode
|
||||
.unwrap_or(fallback.conversation_mode);
|
||||
let judgement_summary = inference
|
||||
.judgement_summary
|
||||
.filter(|value| !value.trim().is_empty())
|
||||
.unwrap_or_else(|| summarize_dynamic_state(user_input_signal, drift_risk, conversation_mode));
|
||||
.unwrap_or_else(|| {
|
||||
summarize_dynamic_state(user_input_signal, drift_risk, conversation_mode)
|
||||
});
|
||||
|
||||
PromptDynamicState {
|
||||
current_turn,
|
||||
@@ -966,7 +980,11 @@ fn build_prompt_dynamic_state_inference_prompt(
|
||||
chat_history: &[JsonValue],
|
||||
) -> (String, String) {
|
||||
(
|
||||
[STATE_INFERENCE_SYSTEM_PROMPT, STATE_INFERENCE_OUTPUT_CONTRACT].join("\n\n"),
|
||||
[
|
||||
STATE_INFERENCE_SYSTEM_PROMPT,
|
||||
STATE_INFERENCE_OUTPUT_CONTRACT,
|
||||
]
|
||||
.join("\n\n"),
|
||||
[
|
||||
format!("当前轮次:{current_turn}"),
|
||||
format!("当前完成度:{progress_percent}"),
|
||||
@@ -1010,7 +1028,8 @@ fn build_chat_history(messages: &[CustomWorldAgentMessageRecord]) -> Vec<JsonVal
|
||||
messages
|
||||
.iter()
|
||||
.filter(|message| {
|
||||
(message.role == "user" || message.role == "assistant") && !message.text.trim().is_empty()
|
||||
(message.role == "user" || message.role == "assistant")
|
||||
&& !message.text.trim().is_empty()
|
||||
})
|
||||
.map(|message| {
|
||||
json!({
|
||||
@@ -1059,8 +1078,7 @@ fn build_creator_intent_from_eight_anchor_content(
|
||||
.iter()
|
||||
.cloned()
|
||||
.chain(
|
||||
(!value.hidden_crisis.trim().is_empty())
|
||||
.then_some(value.hidden_crisis.clone()),
|
||||
(!value.hidden_crisis.trim().is_empty()).then_some(value.hidden_crisis.clone()),
|
||||
)
|
||||
.collect::<Vec<_>>()
|
||||
})
|
||||
@@ -1205,7 +1223,10 @@ fn evaluate_creator_intent_readiness(intent: &CreatorIntentRecord) -> CreatorInt
|
||||
}
|
||||
}
|
||||
|
||||
fn resolve_creator_intent_stage(has_user_input: bool, readiness: &CreatorIntentReadiness) -> &'static str {
|
||||
fn resolve_creator_intent_stage(
|
||||
has_user_input: bool,
|
||||
readiness: &CreatorIntentReadiness,
|
||||
) -> &'static str {
|
||||
if readiness.is_ready {
|
||||
"foundation_review"
|
||||
} else if has_user_input {
|
||||
@@ -1509,11 +1530,16 @@ fn detect_user_input_signal(chat_history: &[JsonValue]) -> PromptUserInputSignal
|
||||
if latest_user_text.is_empty() {
|
||||
return PromptUserInputSignal::Sparse;
|
||||
}
|
||||
if contains_any(&latest_user_text, &["不是", "改成", "改为", "换成", "重来", "推翻", "修正"])
|
||||
{
|
||||
if contains_any(
|
||||
&latest_user_text,
|
||||
&["不是", "改成", "改为", "换成", "重来", "推翻", "修正"],
|
||||
) {
|
||||
return PromptUserInputSignal::Correction;
|
||||
}
|
||||
if contains_any(&latest_user_text, &["你帮我想", "你来定", "你决定", "你补完"]) {
|
||||
if contains_any(
|
||||
&latest_user_text,
|
||||
&["你帮我想", "你来定", "你决定", "你补完"],
|
||||
) {
|
||||
return PromptUserInputSignal::Delegate;
|
||||
}
|
||||
let segments = split_sentences(&latest_user_text);
|
||||
@@ -1535,8 +1561,14 @@ fn detect_drift_risk(
|
||||
let recent_user_messages = chat_history
|
||||
.iter()
|
||||
.filter_map(|entry| {
|
||||
(entry.get("role").and_then(JsonValue::as_str) == Some("user"))
|
||||
.then(|| entry.get("content").and_then(JsonValue::as_str).unwrap_or("").trim().to_string())
|
||||
(entry.get("role").and_then(JsonValue::as_str) == Some("user")).then(|| {
|
||||
entry
|
||||
.get("content")
|
||||
.and_then(JsonValue::as_str)
|
||||
.unwrap_or("")
|
||||
.trim()
|
||||
.to_string()
|
||||
})
|
||||
})
|
||||
.filter(|value| !value.is_empty())
|
||||
.rev()
|
||||
@@ -1545,11 +1577,19 @@ fn detect_drift_risk(
|
||||
|
||||
let correction_count = recent_user_messages
|
||||
.iter()
|
||||
.filter(|entry| contains_any(entry, &["不是", "改成", "改为", "换成", "推翻", "重来", "修正"]))
|
||||
.filter(|entry| {
|
||||
contains_any(
|
||||
entry,
|
||||
&["不是", "改成", "改为", "换成", "推翻", "重来", "修正"],
|
||||
)
|
||||
})
|
||||
.count();
|
||||
if correction_count >= 2
|
||||
|| (progress_percent >= 65
|
||||
&& contains_any(&latest_user_text, &["不是", "改成", "改为", "换成", "重来", "推翻"]))
|
||||
&& contains_any(
|
||||
&latest_user_text,
|
||||
&["不是", "改成", "改为", "换成", "重来", "推翻"],
|
||||
))
|
||||
{
|
||||
return PromptDriftRisk::High;
|
||||
}
|
||||
@@ -1652,7 +1692,8 @@ fn render_dynamic_state_context(dynamic_state: &PromptDynamicState) -> String {
|
||||
fn render_current_anchor_context(anchor_content: &EightAnchorContent) -> String {
|
||||
format!(
|
||||
"当前完整设定结构如下。\n你必须把它视为上一版有效世界底子。\n\n如果用户没有否定其中某部分内容,且该部分仍然成立,可以继续保留。\n如果用户明确修正了某部分内容,新的完整设定结构必须体现修正后的版本。\n\n当前完整设定结构:\n{}",
|
||||
serde_json::to_string_pretty(anchor_content).unwrap_or_else(|_| empty_agent_anchor_content_json())
|
||||
serde_json::to_string_pretty(anchor_content)
|
||||
.unwrap_or_else(|_| empty_agent_anchor_content_json())
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1757,7 +1798,8 @@ fn parse_conversation_mode(value: Option<&JsonValue>) -> Option<PromptConversati
|
||||
|
||||
fn mode_rules(mode: PromptConversationMode) -> &'static str {
|
||||
match mode {
|
||||
PromptConversationMode::Bootstrap => r#"当前模式:bootstrap
|
||||
PromptConversationMode::Bootstrap => {
|
||||
r#"当前模式:bootstrap
|
||||
|
||||
目标:
|
||||
1. 先把世界的基本方向抓住
|
||||
@@ -1777,8 +1819,10 @@ fn mode_rules(mode: PromptConversationMode) -> &'static str {
|
||||
1. 让用户觉得“现在很容易继续往下说”
|
||||
2. 不要制造被考试、被拷问、被策划问卷追着跑的感觉
|
||||
3. replyText 最好短、稳、可接话
|
||||
4. 如果用户信息很少,也不要显得冷淡或机械"#,
|
||||
PromptConversationMode::Expand => r#"当前模式:expand
|
||||
4. 如果用户信息很少,也不要显得冷淡或机械"#
|
||||
}
|
||||
PromptConversationMode::Expand => {
|
||||
r#"当前模式:expand
|
||||
|
||||
目标:
|
||||
1. 在保持现有方向的前提下,把设定结构逐步补全
|
||||
@@ -1797,8 +1841,10 @@ fn mode_rules(mode: PromptConversationMode) -> &'static str {
|
||||
1. 让用户感到“我刚说的内容都被接住了”
|
||||
2. 回复里可以带一点顺势整理感,但不要太像会议纪要
|
||||
3. 不要无视用户刚提供的高价值细节
|
||||
4. 不要让用户觉得系统在自顾自重写世界"#,
|
||||
PromptConversationMode::Compress => r#"当前模式:compress
|
||||
4. 不要让用户觉得系统在自顾自重写世界"#
|
||||
}
|
||||
PromptConversationMode::Compress => {
|
||||
r#"当前模式:compress
|
||||
|
||||
目标:
|
||||
1. 开始收束当前设定
|
||||
@@ -1818,8 +1864,10 @@ fn mode_rules(mode: PromptConversationMode) -> &'static str {
|
||||
1. 让用户感觉世界正在变得更稳,而不是越来越散
|
||||
2. 让推进感更明确,但不要显得催促
|
||||
3. 回复语气应更笃定一些,减少反复横跳
|
||||
4. 不要把用户刚补进来的细节又冲淡掉"#,
|
||||
PromptConversationMode::RepairDirection => r#"当前模式:repair_direction
|
||||
4. 不要把用户刚补进来的细节又冲淡掉"#
|
||||
}
|
||||
PromptConversationMode::RepairDirection => {
|
||||
r#"当前模式:repair_direction
|
||||
|
||||
目标:
|
||||
1. 处理用户对既有设定的修正
|
||||
@@ -1838,8 +1886,10 @@ fn mode_rules(mode: PromptConversationMode) -> &'static str {
|
||||
1. 让用户感到“我刚刚的纠偏真的生效了”
|
||||
2. 不要和用户辩论旧方案为什么也行
|
||||
3. 不要表现出对修正的不情愿
|
||||
4. 回复要体现重心已经切到新方向,而不是停留在旧世界观惯性里"#,
|
||||
PromptConversationMode::ForceComplete => r#"当前模式:force_complete
|
||||
4. 回复要体现重心已经切到新方向,而不是停留在旧世界观惯性里"#
|
||||
}
|
||||
PromptConversationMode::ForceComplete => {
|
||||
r#"当前模式:force_complete
|
||||
|
||||
目标:
|
||||
1. 基于当前方向直接补齐剩余设定
|
||||
@@ -1860,8 +1910,10 @@ fn mode_rules(mode: PromptConversationMode) -> &'static str {
|
||||
1. 让用户感到“系统已经帮我把能补的补好了”
|
||||
2. 不要在这一步突然冒出很多陌生设定把用户吓出戏
|
||||
3. 回复要有完成感,但不要太官话
|
||||
4. 清楚告诉用户下一步可以做什么"#,
|
||||
PromptConversationMode::Closing => r#"当前模式:closing
|
||||
4. 清楚告诉用户下一步可以做什么"#
|
||||
}
|
||||
PromptConversationMode::Closing => {
|
||||
r#"当前模式:closing
|
||||
|
||||
目标:
|
||||
1. 尽量形成一版可用的设定底子
|
||||
@@ -1880,26 +1932,37 @@ fn mode_rules(mode: PromptConversationMode) -> &'static str {
|
||||
1. 让用户感觉作品已经快成了,而不是还在无穷试探
|
||||
2. 回复可以更像确认和轻推,不要继续像前期那样频繁试探
|
||||
3. 保持留白感,不要把所有东西都一次说死
|
||||
4. 让用户自然过渡到下一阶段,而不是突然被切断对话"#,
|
||||
4. 让用户自然过渡到下一阶段,而不是突然被切断对话"#
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn user_signal_rules(signal: PromptUserInputSignal) -> &'static str {
|
||||
match signal {
|
||||
PromptUserInputSignal::Rich => r#"本轮用户输入信息密度高。
|
||||
PromptUserInputSignal::Rich => {
|
||||
r#"本轮用户输入信息密度高。
|
||||
请尽量从这一轮里提取多个锚点,不要只更新单一方向。
|
||||
如果一条输入同时影响世界方向、冲突和关系,请在新的完整设定结构中一起体现。"#,
|
||||
PromptUserInputSignal::Normal => r#"本轮用户输入为正常补充。
|
||||
请优先顺着当前方向稳定更新,不要主动扩写太多新设定。"#,
|
||||
PromptUserInputSignal::Sparse => r#"本轮用户输入较少或较虚。
|
||||
如果一条输入同时影响世界方向、冲突和关系,请在新的完整设定结构中一起体现。"#
|
||||
}
|
||||
PromptUserInputSignal::Normal => {
|
||||
r#"本轮用户输入为正常补充。
|
||||
请优先顺着当前方向稳定更新,不要主动扩写太多新设定。"#
|
||||
}
|
||||
PromptUserInputSignal::Sparse => {
|
||||
r#"本轮用户输入较少或较虚。
|
||||
请保留上一版中仍然成立的内容,不要为了凑完整度而强行发明过多新设定。
|
||||
replyText 要让用户容易继续往下说。"#,
|
||||
PromptUserInputSignal::Correction => r#"本轮用户在修正或推翻旧设定。
|
||||
replyText 要让用户容易继续往下说。"#
|
||||
}
|
||||
PromptUserInputSignal::Correction => {
|
||||
r#"本轮用户在修正或推翻旧设定。
|
||||
请优先吸收修正,不要机械复读旧版本。
|
||||
新的完整设定结构必须以修正后的方向为准。"#,
|
||||
PromptUserInputSignal::Delegate => r#"本轮用户把部分决定权交给你。
|
||||
新的完整设定结构必须以修正后的方向为准。"#
|
||||
}
|
||||
PromptUserInputSignal::Delegate => {
|
||||
r#"本轮用户把部分决定权交给你。
|
||||
你可以在 replyText 中给出有限度的建议,但不要突然补满整套设定。
|
||||
新的完整设定结构仍应尽量建立在已有世界方向上,而不是完全重做。"#,
|
||||
新的完整设定结构仍应尽量建立在已有世界方向上,而不是完全重做。"#
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1985,7 +2048,11 @@ fn clamp_text(value: &str, max_length: usize) -> String {
|
||||
if normalized.chars().count() <= max_length {
|
||||
return normalized;
|
||||
}
|
||||
normalized.chars().take(max_length.saturating_sub(1)).collect::<String>() + "…"
|
||||
normalized
|
||||
.chars()
|
||||
.take(max_length.saturating_sub(1))
|
||||
.collect::<String>()
|
||||
+ "…"
|
||||
}
|
||||
|
||||
fn clamp_progress_percent(value: Option<&JsonValue>) -> u32 {
|
||||
@@ -1996,7 +2063,8 @@ fn clamp_progress_percent(value: Option<&JsonValue>) -> u32 {
|
||||
}
|
||||
|
||||
fn to_text(value: Option<&JsonValue>) -> Option<String> {
|
||||
value.and_then(JsonValue::as_str)
|
||||
value
|
||||
.and_then(JsonValue::as_str)
|
||||
.map(str::trim)
|
||||
.filter(|value| !value.is_empty())
|
||||
.map(str::to_string)
|
||||
|
||||
@@ -16,10 +16,10 @@ use tracing::{info, warn};
|
||||
|
||||
use crate::{
|
||||
api_response::json_success_body,
|
||||
auth_payload::map_auth_user_payload,
|
||||
auth_session::{
|
||||
attach_set_cookie_header, build_refresh_session_cookie_header, create_auth_session,
|
||||
},
|
||||
auth_payload::map_auth_user_payload,
|
||||
http_error::AppError,
|
||||
request_context::RequestContext,
|
||||
session_client::resolve_session_client_context,
|
||||
|
||||
@@ -52,12 +52,15 @@ use spacetime_client::{
|
||||
use std::convert::Infallible;
|
||||
|
||||
use crate::{
|
||||
api_response::json_success_body, auth::AuthenticatedAccessToken, http_error::AppError,
|
||||
api_response::json_success_body,
|
||||
auth::AuthenticatedAccessToken,
|
||||
http_error::AppError,
|
||||
puzzle_agent_turn::{
|
||||
PuzzleAgentTurnRequest, build_failed_finalize_record_input, build_finalize_record_input,
|
||||
run_puzzle_agent_turn,
|
||||
},
|
||||
request_context::RequestContext, state::AppState,
|
||||
request_context::RequestContext,
|
||||
state::AppState,
|
||||
};
|
||||
|
||||
const PUZZLE_AGENT_API_BASE_PROVIDER: &str = "puzzle-agent";
|
||||
@@ -1269,7 +1272,9 @@ fn build_puzzle_welcome_text(seed_text: &str) -> String {
|
||||
}
|
||||
|
||||
fn build_stable_puzzle_work_ids(session_id: &str) -> (String, String) {
|
||||
let stable_suffix = session_id.strip_prefix("puzzle-session-").unwrap_or(session_id);
|
||||
let stable_suffix = session_id
|
||||
.strip_prefix("puzzle-session-")
|
||||
.unwrap_or(session_id);
|
||||
(
|
||||
format!("puzzle-work-{stable_suffix}"),
|
||||
format!("puzzle-profile-{stable_suffix}"),
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
use module_puzzle::{
|
||||
PuzzleAgentStage, PuzzleAnchorPack, PuzzleAnchorStatus, empty_anchor_pack,
|
||||
};
|
||||
use module_puzzle::{PuzzleAgentStage, PuzzleAnchorPack, PuzzleAnchorStatus, empty_anchor_pack};
|
||||
use platform_llm::{LlmClient, LlmMessage, LlmStreamDelta, LlmTextRequest};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::{Value as JsonValue, json};
|
||||
@@ -157,10 +155,13 @@ where
|
||||
|
||||
Ok(PuzzleAgentTurnResult {
|
||||
assistant_reply_text: output.reply_text,
|
||||
stage: resolve_puzzle_agent_stage(output.progress_percent).as_str().to_string(),
|
||||
stage: resolve_puzzle_agent_stage(output.progress_percent)
|
||||
.as_str()
|
||||
.to_string(),
|
||||
progress_percent: output.progress_percent,
|
||||
anchor_pack_json: serde_json::to_string(&output.next_anchor_pack)
|
||||
.unwrap_or_else(|_| serde_json::to_string(&empty_anchor_pack()).unwrap_or_else(|_| "{}".to_string())),
|
||||
anchor_pack_json: serde_json::to_string(&output.next_anchor_pack).unwrap_or_else(|_| {
|
||||
serde_json::to_string(&empty_anchor_pack()).unwrap_or_else(|_| "{}".to_string())
|
||||
}),
|
||||
error_message: None,
|
||||
})
|
||||
}
|
||||
@@ -193,7 +194,9 @@ pub(crate) fn build_failed_finalize_record_input(
|
||||
updated_at_micros: i64,
|
||||
) -> PuzzleAgentMessageFinalizeRecordInput {
|
||||
let anchor_pack_json = serde_json::to_string(&map_record_anchor_pack(&session.anchor_pack))
|
||||
.unwrap_or_else(|_| serde_json::to_string(&empty_anchor_pack()).unwrap_or_else(|_| "{}".to_string()));
|
||||
.unwrap_or_else(|_| {
|
||||
serde_json::to_string(&empty_anchor_pack()).unwrap_or_else(|_| "{}".to_string())
|
||||
});
|
||||
PuzzleAgentMessageFinalizeRecordInput {
|
||||
session_id,
|
||||
owner_user_id,
|
||||
@@ -214,8 +217,9 @@ fn build_puzzle_agent_prompt(session: &PuzzleAgentSessionRecord) -> String {
|
||||
progress = session.progress_percent,
|
||||
anchor_pack = serde_json::to_string_pretty(&map_record_anchor_pack(&session.anchor_pack))
|
||||
.unwrap_or_else(|_| "{}".to_string()),
|
||||
chat_history = serde_json::to_string_pretty(&build_chat_history(session.messages.as_slice()))
|
||||
.unwrap_or_else(|_| "[]".to_string()),
|
||||
chat_history =
|
||||
serde_json::to_string_pretty(&build_chat_history(session.messages.as_slice()))
|
||||
.unwrap_or_else(|_| "[]".to_string()),
|
||||
contract = PUZZLE_AGENT_OUTPUT_CONTRACT,
|
||||
)
|
||||
}
|
||||
@@ -279,7 +283,9 @@ fn map_record_anchor_pack(record: &spacetime_client::PuzzleAnchorPackRecord) ->
|
||||
}
|
||||
}
|
||||
|
||||
fn map_record_anchor_item(record: &spacetime_client::PuzzleAnchorItemRecord) -> module_puzzle::PuzzleAnchorItem {
|
||||
fn map_record_anchor_item(
|
||||
record: &spacetime_client::PuzzleAnchorItemRecord,
|
||||
) -> module_puzzle::PuzzleAnchorItem {
|
||||
module_puzzle::PuzzleAnchorItem {
|
||||
key: record.key.clone(),
|
||||
label: record.label.clone(),
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
use std::{error::Error, fmt, sync::Arc};
|
||||
|
||||
#[cfg(test)]
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
sync::{Arc, Mutex},
|
||||
};
|
||||
use std::{collections::HashMap, sync::Mutex};
|
||||
|
||||
use module_ai::{AiTaskService, InMemoryAiTaskStore};
|
||||
use module_auth::{
|
||||
@@ -16,9 +13,8 @@ use module_runtime::RuntimeSnapshotRecord;
|
||||
use module_runtime::{SAVE_SNAPSHOT_VERSION, format_utc_micros};
|
||||
use platform_auth::{
|
||||
AccessTokenClaims, AccessTokenClaimsInput, AuthProvider, BindingStatus, JwtConfig, JwtError,
|
||||
RefreshCookieConfig, RefreshCookieError, RefreshCookieSameSite,
|
||||
sign_access_token, verify_access_token,
|
||||
SmsAuthConfig, SmsAuthProvider, SmsAuthProviderKind, SmsProviderError,
|
||||
RefreshCookieConfig, RefreshCookieError, RefreshCookieSameSite, SmsAuthConfig, SmsAuthProvider,
|
||||
SmsAuthProviderKind, SmsProviderError, sign_access_token, verify_access_token,
|
||||
};
|
||||
use platform_llm::{LlmClient, LlmConfig, LlmError};
|
||||
use platform_oss::{OssClient, OssConfig, OssError};
|
||||
@@ -57,6 +53,7 @@ pub struct AppState {
|
||||
test_runtime_snapshot_store: Arc<Mutex<HashMap<String, RuntimeSnapshotRecord>>>,
|
||||
}
|
||||
|
||||
// 后台管理员运行态独立于普通玩家登录体系,只从环境变量构造。
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct AdminRuntime {
|
||||
username: Arc<str>,
|
||||
@@ -565,6 +562,7 @@ fn build_llm_client(config: &AppConfig) -> Result<Option<LlmClient>, AppStateIni
|
||||
Ok(Some(LlmClient::new(llm_config)?))
|
||||
}
|
||||
|
||||
// 只有在用户名和密码都已配置时才启用后台,避免半配置状态暴露伪入口。
|
||||
fn build_admin_runtime(
|
||||
config: &AppConfig,
|
||||
base_jwt_config: &JwtConfig,
|
||||
|
||||
@@ -636,8 +636,14 @@ impl PhoneAuthService {
|
||||
phone_national_masked = normalized_phone.masked_national_number.as_str(),
|
||||
cooldown_seconds = provider_result.cooldown_seconds,
|
||||
expires_in_seconds = provider_result.expires_in_seconds,
|
||||
provider_request_id = provider_result.provider_request_id.as_deref().unwrap_or("unknown"),
|
||||
provider_out_id = provider_result.provider_out_id.as_deref().unwrap_or("unknown"),
|
||||
provider_request_id = provider_result
|
||||
.provider_request_id
|
||||
.as_deref()
|
||||
.unwrap_or("unknown"),
|
||||
provider_out_id = provider_result
|
||||
.provider_out_id
|
||||
.as_deref()
|
||||
.unwrap_or("unknown"),
|
||||
"手机号验证码 provider 调用成功,准备写入本地快照"
|
||||
);
|
||||
|
||||
|
||||
@@ -874,7 +874,10 @@ fn build_level_blueprint(level: u32, level_count: u32, theme: &str) -> BigFishLe
|
||||
} else {
|
||||
format!("第 {level} 阶实体,继续吞噬同级和低级个体成长")
|
||||
},
|
||||
silhouette_direction: format!("体型约为初始的 {:.1} 倍,轮廓更清晰", 1.0 + level as f32 * 0.22),
|
||||
silhouette_direction: format!(
|
||||
"体型约为初始的 {:.1} 倍,轮廓更清晰",
|
||||
1.0 + level as f32 * 0.22
|
||||
),
|
||||
size_ratio: 1.0 + (level.saturating_sub(1) as f32 * 0.22),
|
||||
visual_prompt_seed: format!("{theme} 第 {level} 级实体主图,透明背景,清晰轮廓"),
|
||||
motion_prompt_seed: format!("{theme} 第 {level} 级实体 idle_float 与 move_swim 动作"),
|
||||
@@ -909,7 +912,10 @@ fn build_asset_prompt_snapshot(
|
||||
.find(|item| item.level == level)
|
||||
.ok_or(BigFishFieldError::InvalidLevel)?;
|
||||
let motion_key = motion_key.ok_or(BigFishFieldError::InvalidAssetKind)?;
|
||||
Ok(format!("{},动作位:{}", blueprint.motion_prompt_seed, motion_key))
|
||||
Ok(format!(
|
||||
"{},动作位:{}",
|
||||
blueprint.motion_prompt_seed, motion_key
|
||||
))
|
||||
}
|
||||
BigFishAssetKind::StageBackground => Ok(draft.background.background_prompt_seed.clone()),
|
||||
}
|
||||
@@ -990,8 +996,14 @@ fn move_owned_entities(
|
||||
) {
|
||||
let input = snapshot.last_input.clone();
|
||||
if let Some(leader) = snapshot.owned_entities.first_mut() {
|
||||
leader.position.x = clamp_world(leader.position.x + input.x * params.leader_move_speed * step_seconds, true);
|
||||
leader.position.y = clamp_world(leader.position.y + input.y * params.leader_move_speed * step_seconds, false);
|
||||
leader.position.x = clamp_world(
|
||||
leader.position.x + input.x * params.leader_move_speed * step_seconds,
|
||||
true,
|
||||
);
|
||||
leader.position.y = clamp_world(
|
||||
leader.position.y + input.y * params.leader_move_speed * step_seconds,
|
||||
false,
|
||||
);
|
||||
snapshot.camera_center = leader.position.clone();
|
||||
}
|
||||
|
||||
@@ -1038,10 +1050,15 @@ fn resolve_collisions(snapshot: &mut BigFishRuntimeSnapshot, _params: &BigFishRu
|
||||
radius: entity_radius(wild.level),
|
||||
offscreen_seconds: 0.0,
|
||||
});
|
||||
snapshot.event_log.push(format!("收编 {} 级实体", wild.level));
|
||||
snapshot
|
||||
.event_log
|
||||
.push(format!("收编 {} 级实体", wild.level));
|
||||
} else {
|
||||
owned_to_remove.push(owned_index);
|
||||
snapshot.event_log.push(format!("{} 级己方实体被 {} 级野生实体吃掉", owned.level, wild.level));
|
||||
snapshot.event_log.push(format!(
|
||||
"{} 级己方实体被 {} 级野生实体吃掉",
|
||||
owned.level, wild.level
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1075,7 +1092,9 @@ fn apply_chain_merges(snapshot: &mut BigFishRuntimeSnapshot, params: &BigFishRun
|
||||
radius: entity_radius(level + 1),
|
||||
offscreen_seconds: 0.0,
|
||||
});
|
||||
snapshot.event_log.push(format!("3 个 {} 级实体合成 {} 级", level, level + 1));
|
||||
snapshot
|
||||
.event_log
|
||||
.push(format!("3 个 {} 级实体合成 {} 级", level, level + 1));
|
||||
merged = true;
|
||||
break;
|
||||
}
|
||||
@@ -1098,7 +1117,10 @@ fn refresh_player_leader(snapshot: &mut BigFishRuntimeSnapshot) {
|
||||
})
|
||||
.then_with(|| left.entity_id.cmp(&right.entity_id))
|
||||
});
|
||||
snapshot.leader_entity_id = snapshot.owned_entities.first().map(|entity| entity.entity_id.clone());
|
||||
snapshot.leader_entity_id = snapshot
|
||||
.owned_entities
|
||||
.first()
|
||||
.map(|entity| entity.entity_id.clone());
|
||||
snapshot.player_level = snapshot
|
||||
.owned_entities
|
||||
.iter()
|
||||
@@ -1113,12 +1135,16 @@ fn refresh_player_leader(snapshot: &mut BigFishRuntimeSnapshot) {
|
||||
fn apply_win_or_fail(snapshot: &mut BigFishRuntimeSnapshot, params: &BigFishRuntimeParams) {
|
||||
if snapshot.owned_entities.is_empty() {
|
||||
snapshot.status = BigFishRunStatus::Failed;
|
||||
snapshot.event_log.push("己方实体归零,本局失败".to_string());
|
||||
snapshot
|
||||
.event_log
|
||||
.push("己方实体归零,本局失败".to_string());
|
||||
return;
|
||||
}
|
||||
if snapshot.player_level >= params.win_level {
|
||||
snapshot.status = BigFishRunStatus::Won;
|
||||
snapshot.event_log.push("获得最高等级实体,通关".to_string());
|
||||
snapshot
|
||||
.event_log
|
||||
.push("获得最高等级实体,通关".to_string());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1282,7 +1308,12 @@ mod tests {
|
||||
assert_eq!(draft.runtime_params.offscreen_cull_seconds, 3.0);
|
||||
assert_eq!(draft.runtime_params.prey_spawn_delta_levels, vec![1, 2]);
|
||||
assert_eq!(draft.runtime_params.threat_spawn_delta_levels, vec![1, 2]);
|
||||
assert!(draft.levels.last().is_some_and(|level| level.is_final_level));
|
||||
assert!(
|
||||
draft
|
||||
.levels
|
||||
.last()
|
||||
.is_some_and(|level| level.is_final_level)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -1292,20 +1323,26 @@ mod tests {
|
||||
|
||||
assert!(!coverage.publish_ready);
|
||||
assert_eq!(coverage.required_level_count, 8);
|
||||
assert!(coverage.blockers.iter().any(|item| item.contains("等级主图")));
|
||||
assert!(coverage.blockers.iter().any(|item| item.contains("基础动作")));
|
||||
assert!(
|
||||
coverage
|
||||
.blockers
|
||||
.iter()
|
||||
.any(|item| item.contains("等级主图"))
|
||||
);
|
||||
assert!(
|
||||
coverage
|
||||
.blockers
|
||||
.iter()
|
||||
.any(|item| item.contains("基础动作"))
|
||||
);
|
||||
assert!(coverage.blockers.iter().any(|item| item.contains("背景图")));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn same_level_wild_entity_can_be_collected_at_start() {
|
||||
let draft = compile_default_draft(&infer_anchor_pack("深海", None));
|
||||
let mut snapshot = build_initial_runtime_snapshot(
|
||||
"run-1".to_string(),
|
||||
"session-1".to_string(),
|
||||
&draft,
|
||||
1,
|
||||
);
|
||||
let mut snapshot =
|
||||
build_initial_runtime_snapshot("run-1".to_string(), "session-1".to_string(), &draft, 1);
|
||||
snapshot.wild_entities[0].position = BigFishVector2 { x: 1.0, y: 0.0 };
|
||||
|
||||
let next = advance_runtime_snapshot(snapshot, &draft.runtime_params, 0.0, 0.0, 2);
|
||||
@@ -1383,15 +1420,14 @@ mod tests {
|
||||
});
|
||||
snapshot.updated_at_micros = 1_000_000;
|
||||
|
||||
let next = advance_runtime_snapshot(
|
||||
snapshot,
|
||||
&draft.runtime_params,
|
||||
0.0,
|
||||
0.0,
|
||||
1_250_000,
|
||||
);
|
||||
let next = advance_runtime_snapshot(snapshot, &draft.runtime_params, 0.0, 0.0, 1_250_000);
|
||||
|
||||
assert!(!next.wild_entities.iter().any(|entity| entity.entity_id == "wild-cull"));
|
||||
assert!(
|
||||
!next
|
||||
.wild_entities
|
||||
.iter()
|
||||
.any(|entity| entity.entity_id == "wild-cull")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -1413,17 +1449,12 @@ mod tests {
|
||||
});
|
||||
snapshot.updated_at_micros = 1_000_000;
|
||||
|
||||
let next = advance_runtime_snapshot(
|
||||
snapshot,
|
||||
&draft.runtime_params,
|
||||
0.0,
|
||||
0.0,
|
||||
1_200_000,
|
||||
);
|
||||
let next = advance_runtime_snapshot(snapshot, &draft.runtime_params, 0.0, 0.0, 1_200_000);
|
||||
|
||||
assert!(next
|
||||
.wild_entities
|
||||
.iter()
|
||||
.any(|entity| entity.entity_id == "wild-cull-safe"));
|
||||
assert!(
|
||||
next.wild_entities
|
||||
.iter()
|
||||
.any(|entity| entity.entity_id == "wild-cull-safe")
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
use std::{collections::{BTreeMap, BTreeSet, VecDeque}, error::Error, fmt};
|
||||
use std::{
|
||||
collections::{BTreeMap, BTreeSet, VecDeque},
|
||||
error::Error,
|
||||
fmt,
|
||||
};
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use shared_kernel::{normalize_required_string, normalize_string_list};
|
||||
@@ -606,7 +610,10 @@ pub fn infer_anchor_pack(seed_text: &str, latest_message: Option<&str>) -> Puzzl
|
||||
pack
|
||||
}
|
||||
|
||||
pub fn build_creator_intent(anchor_pack: &PuzzleAnchorPack, messages: &[PuzzleAgentMessageSnapshot]) -> PuzzleCreatorIntent {
|
||||
pub fn build_creator_intent(
|
||||
anchor_pack: &PuzzleAnchorPack,
|
||||
messages: &[PuzzleAgentMessageSnapshot],
|
||||
) -> PuzzleCreatorIntent {
|
||||
PuzzleCreatorIntent {
|
||||
source_mode: "agent_chat".to_string(),
|
||||
raw_messages_summary: messages
|
||||
@@ -624,11 +631,16 @@ pub fn build_creator_intent(anchor_pack: &PuzzleAnchorPack, messages: &[PuzzleAg
|
||||
.into_iter()
|
||||
.take(PUZZLE_MAX_TAG_COUNT)
|
||||
.collect(),
|
||||
forbidden_directives: vec![extract_forbidden_directive(&anchor_pack.tags_and_forbidden.value)],
|
||||
forbidden_directives: vec![extract_forbidden_directive(
|
||||
&anchor_pack.tags_and_forbidden.value,
|
||||
)],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn compile_result_draft(anchor_pack: &PuzzleAnchorPack, messages: &[PuzzleAgentMessageSnapshot]) -> PuzzleResultDraft {
|
||||
pub fn compile_result_draft(
|
||||
anchor_pack: &PuzzleAnchorPack,
|
||||
messages: &[PuzzleAgentMessageSnapshot],
|
||||
) -> PuzzleResultDraft {
|
||||
let creator_intent = build_creator_intent(anchor_pack, messages);
|
||||
let normalized_tags = normalize_theme_tags(creator_intent.theme_tags.clone());
|
||||
let level_name = build_level_name(anchor_pack, &normalized_tags);
|
||||
@@ -714,7 +726,10 @@ pub fn apply_selected_candidate(
|
||||
Ok(draft)
|
||||
}
|
||||
|
||||
pub fn build_result_preview(draft: &PuzzleResultDraft, author_display_name: Option<&str>) -> PuzzleResultPreviewEnvelope {
|
||||
pub fn build_result_preview(
|
||||
draft: &PuzzleResultDraft,
|
||||
author_display_name: Option<&str>,
|
||||
) -> PuzzleResultPreviewEnvelope {
|
||||
let blockers = validate_publish_requirements(draft, author_display_name);
|
||||
PuzzleResultPreviewEnvelope {
|
||||
draft: draft.clone(),
|
||||
@@ -736,14 +751,22 @@ pub fn validate_publish_requirements(
|
||||
message: "关卡名不能为空".to_string(),
|
||||
});
|
||||
}
|
||||
if draft.cover_image_src.as_deref().map(str::trim).unwrap_or("").is_empty() {
|
||||
if draft
|
||||
.cover_image_src
|
||||
.as_deref()
|
||||
.map(str::trim)
|
||||
.unwrap_or("")
|
||||
.is_empty()
|
||||
{
|
||||
blockers.push(PuzzleResultPreviewBlocker {
|
||||
id: "missing-cover-image".to_string(),
|
||||
code: "MISSING_COVER_IMAGE".to_string(),
|
||||
message: "正式拼图图片尚未确定".to_string(),
|
||||
});
|
||||
}
|
||||
if draft.theme_tags.len() < PUZZLE_MIN_TAG_COUNT || draft.theme_tags.len() > PUZZLE_MAX_TAG_COUNT {
|
||||
if draft.theme_tags.len() < PUZZLE_MIN_TAG_COUNT
|
||||
|| draft.theme_tags.len() > PUZZLE_MAX_TAG_COUNT
|
||||
{
|
||||
blockers.push(PuzzleResultPreviewBlocker {
|
||||
id: "invalid-tag-count".to_string(),
|
||||
code: "INVALID_TAG_COUNT".to_string(),
|
||||
@@ -927,7 +950,10 @@ pub fn swap_pieces(
|
||||
normalize_required_string(first_piece_id).ok_or(PuzzleFieldError::MissingPieceId)?;
|
||||
let second_piece_id =
|
||||
normalize_required_string(second_piece_id).ok_or(PuzzleFieldError::MissingPieceId)?;
|
||||
let current_level = run.current_level.clone().ok_or(PuzzleFieldError::InvalidOperation)?;
|
||||
let current_level = run
|
||||
.current_level
|
||||
.clone()
|
||||
.ok_or(PuzzleFieldError::InvalidOperation)?;
|
||||
if current_level.status == PuzzleRuntimeLevelStatus::Cleared {
|
||||
return Err(PuzzleFieldError::InvalidOperation);
|
||||
}
|
||||
@@ -941,9 +967,14 @@ pub fn swap_pieces(
|
||||
.position(|piece| piece.piece_id == second_piece_id)
|
||||
.ok_or(PuzzleFieldError::MissingPieceId)?;
|
||||
|
||||
let (first_row, first_col) = (pieces[first_index].current_row, pieces[first_index].current_col);
|
||||
let (second_row, second_col) =
|
||||
(pieces[second_index].current_row, pieces[second_index].current_col);
|
||||
let (first_row, first_col) = (
|
||||
pieces[first_index].current_row,
|
||||
pieces[first_index].current_col,
|
||||
);
|
||||
let (second_row, second_col) = (
|
||||
pieces[second_index].current_row,
|
||||
pieces[second_index].current_col,
|
||||
);
|
||||
pieces[first_index].current_row = second_row;
|
||||
pieces[first_index].current_col = second_col;
|
||||
pieces[second_index].current_row = first_row;
|
||||
@@ -960,7 +991,10 @@ pub fn drag_piece_or_group(
|
||||
target_col: u32,
|
||||
) -> Result<PuzzleRunSnapshot, PuzzleFieldError> {
|
||||
let piece_id = normalize_required_string(piece_id).ok_or(PuzzleFieldError::MissingPieceId)?;
|
||||
let current_level = run.current_level.clone().ok_or(PuzzleFieldError::InvalidOperation)?;
|
||||
let current_level = run
|
||||
.current_level
|
||||
.clone()
|
||||
.ok_or(PuzzleFieldError::InvalidOperation)?;
|
||||
if current_level.status == PuzzleRuntimeLevelStatus::Cleared {
|
||||
return Err(PuzzleFieldError::InvalidOperation);
|
||||
}
|
||||
@@ -989,7 +1023,10 @@ pub fn advance_next_level(
|
||||
run: &PuzzleRunSnapshot,
|
||||
next_profile: &PuzzleWorkProfile,
|
||||
) -> Result<PuzzleRunSnapshot, PuzzleFieldError> {
|
||||
let current_level = run.current_level.clone().ok_or(PuzzleFieldError::InvalidOperation)?;
|
||||
let current_level = run
|
||||
.current_level
|
||||
.clone()
|
||||
.ok_or(PuzzleFieldError::InvalidOperation)?;
|
||||
if current_level.status != PuzzleRuntimeLevelStatus::Cleared {
|
||||
return Err(PuzzleFieldError::InvalidOperation);
|
||||
}
|
||||
@@ -1057,7 +1094,10 @@ pub fn select_next_profile<'a>(
|
||||
.unwrap_or(std::cmp::Ordering::Equal)
|
||||
.then_with(|| {
|
||||
tag_similarity_score(¤t_profile.theme_tags, &left.theme_tags)
|
||||
.partial_cmp(&tag_similarity_score(¤t_profile.theme_tags, &right.theme_tags))
|
||||
.partial_cmp(&tag_similarity_score(
|
||||
¤t_profile.theme_tags,
|
||||
&right.theme_tags,
|
||||
))
|
||||
.unwrap_or(std::cmp::Ordering::Equal)
|
||||
})
|
||||
.then_with(|| right.play_count.cmp(&left.play_count))
|
||||
@@ -1065,7 +1105,10 @@ pub fn select_next_profile<'a>(
|
||||
})
|
||||
}
|
||||
|
||||
pub fn recommendation_score(current_profile: &PuzzleWorkProfile, candidate: &PuzzleWorkProfile) -> f32 {
|
||||
pub fn recommendation_score(
|
||||
current_profile: &PuzzleWorkProfile,
|
||||
candidate: &PuzzleWorkProfile,
|
||||
) -> f32 {
|
||||
let tag_similarity = tag_similarity_score(¤t_profile.theme_tags, &candidate.theme_tags);
|
||||
let same_author_score = if current_profile.owner_user_id == candidate.owner_user_id {
|
||||
1.0
|
||||
@@ -1076,8 +1119,12 @@ pub fn recommendation_score(current_profile: &PuzzleWorkProfile, candidate: &Puz
|
||||
}
|
||||
|
||||
pub fn tag_similarity_score(left_tags: &[String], right_tags: &[String]) -> f32 {
|
||||
let left_set = normalize_theme_tags(left_tags.to_vec()).into_iter().collect::<BTreeSet<_>>();
|
||||
let right_set = normalize_theme_tags(right_tags.to_vec()).into_iter().collect::<BTreeSet<_>>();
|
||||
let left_set = normalize_theme_tags(left_tags.to_vec())
|
||||
.into_iter()
|
||||
.collect::<BTreeSet<_>>();
|
||||
let right_set = normalize_theme_tags(right_tags.to_vec())
|
||||
.into_iter()
|
||||
.collect::<BTreeSet<_>>();
|
||||
if left_set.is_empty() && right_set.is_empty() {
|
||||
return 0.0;
|
||||
}
|
||||
@@ -1211,7 +1258,11 @@ fn rebuild_board_snapshot(
|
||||
let group_by_piece = merged_groups
|
||||
.iter()
|
||||
.flat_map(|group| {
|
||||
group.piece_ids.iter().cloned().map(|piece_id| (piece_id, group.group_id.clone()))
|
||||
group
|
||||
.piece_ids
|
||||
.iter()
|
||||
.cloned()
|
||||
.map(|piece_id| (piece_id, group.group_id.clone()))
|
||||
})
|
||||
.collect::<BTreeMap<_, _>>();
|
||||
|
||||
@@ -1263,7 +1314,9 @@ fn resolve_merged_groups(pieces: &[PuzzlePieceState]) -> Vec<PuzzleMergedGroupSt
|
||||
};
|
||||
collected_ids.push(current_piece_id.clone());
|
||||
|
||||
for (neighbor_row, neighbor_col) in neighbor_cells(current_piece.current_row, current_piece.current_col) {
|
||||
for (neighbor_row, neighbor_col) in
|
||||
neighbor_cells(current_piece.current_row, current_piece.current_col)
|
||||
{
|
||||
if let Some(neighbor_piece) = pieces_by_cell.get(&(neighbor_row, neighbor_col))
|
||||
&& are_correct_neighbors(current_piece, neighbor_piece)
|
||||
{
|
||||
@@ -1330,12 +1383,18 @@ fn drag_single_piece(
|
||||
.ok_or(PuzzleFieldError::InvalidTargetCell)?;
|
||||
|
||||
if let Some(target_group_id) = pieces[target_index].merged_group_id.clone() {
|
||||
for piece in pieces.iter_mut().filter(|piece| piece.merged_group_id.as_deref() == Some(target_group_id.as_str())) {
|
||||
for piece in pieces
|
||||
.iter_mut()
|
||||
.filter(|piece| piece.merged_group_id.as_deref() == Some(target_group_id.as_str()))
|
||||
{
|
||||
piece.merged_group_id = None;
|
||||
}
|
||||
}
|
||||
|
||||
let (source_row, source_col) = (pieces[piece_index].current_row, pieces[piece_index].current_col);
|
||||
let (source_row, source_col) = (
|
||||
pieces[piece_index].current_row,
|
||||
pieces[piece_index].current_col,
|
||||
);
|
||||
pieces[piece_index].current_row = target_row;
|
||||
pieces[piece_index].current_col = target_col;
|
||||
if target_index != piece_index {
|
||||
@@ -1355,7 +1414,9 @@ fn drag_group(
|
||||
let group_indices = pieces
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter_map(|(index, piece)| (piece.merged_group_id.as_deref() == Some(group_id)).then_some(index))
|
||||
.filter_map(|(index, piece)| {
|
||||
(piece.merged_group_id.as_deref() == Some(group_id)).then_some(index)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
if group_indices.is_empty() {
|
||||
return Err(PuzzleFieldError::InvalidOperation);
|
||||
@@ -1368,7 +1429,11 @@ fn drag_group(
|
||||
for &index in &group_indices {
|
||||
let next_row = pieces[index].current_row as i32 + row_offset;
|
||||
let next_col = pieces[index].current_col as i32 + col_offset;
|
||||
if next_row < 0 || next_col < 0 || next_row >= grid_size as i32 || next_col >= grid_size as i32 {
|
||||
if next_row < 0
|
||||
|| next_col < 0
|
||||
|| next_row >= grid_size as i32
|
||||
|| next_col >= grid_size as i32
|
||||
{
|
||||
return Err(PuzzleFieldError::InvalidTargetCell);
|
||||
}
|
||||
target_positions.push((index, next_row as u32, next_col as u32));
|
||||
@@ -1433,7 +1498,11 @@ fn with_next_board(run: &PuzzleRunSnapshot, next_board: PuzzleBoardSnapshot) ->
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
fn build_published_profile(profile_id: &str, owner_user_id: &str, tags: Vec<&str>) -> PuzzleWorkProfile {
|
||||
fn build_published_profile(
|
||||
profile_id: &str,
|
||||
owner_user_id: &str,
|
||||
tags: Vec<&str>,
|
||||
) -> PuzzleWorkProfile {
|
||||
PuzzleWorkProfile {
|
||||
work_id: format!("work-{profile_id}"),
|
||||
profile_id: profile_id.to_string(),
|
||||
@@ -1490,8 +1559,8 @@ mod tests {
|
||||
build_published_profile("b", "owner-a", vec!["蒸汽城市", "雨夜"]),
|
||||
build_published_profile("c", "owner-c", vec!["猫咪", "森林"]),
|
||||
];
|
||||
let selected = select_next_profile(¤t, &["a".to_string()], &candidates)
|
||||
.expect("should select");
|
||||
let selected =
|
||||
select_next_profile(¤t, &["a".to_string()], &candidates).expect("should select");
|
||||
assert_eq!(selected.profile_id, "b");
|
||||
}
|
||||
|
||||
@@ -1502,8 +1571,18 @@ mod tests {
|
||||
let current_level = run.current_level.clone().expect("level");
|
||||
let first_piece = current_level.board.pieces[0].clone();
|
||||
let second_piece = current_level.board.pieces[1].clone();
|
||||
let swapped = swap_pieces(&run, &first_piece.piece_id, &second_piece.piece_id).expect("swap");
|
||||
assert_eq!(swapped.current_level.as_ref().expect("level").board.pieces.len(), 9);
|
||||
let swapped =
|
||||
swap_pieces(&run, &first_piece.piece_id, &second_piece.piece_id).expect("swap");
|
||||
assert_eq!(
|
||||
swapped
|
||||
.current_level
|
||||
.as_ref()
|
||||
.expect("level")
|
||||
.board
|
||||
.pieces
|
||||
.len(),
|
||||
9
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -1539,13 +1618,9 @@ mod tests {
|
||||
fn apply_publish_overrides_rejects_invalid_tag_count() {
|
||||
let anchor_pack = infer_anchor_pack("蒸汽城市", Some("蒸汽城市"));
|
||||
let draft = compile_result_draft(&anchor_pack, &[]);
|
||||
let error = apply_publish_overrides_to_draft(
|
||||
&draft,
|
||||
None,
|
||||
None,
|
||||
Some(vec!["蒸汽".to_string()]),
|
||||
)
|
||||
.expect_err("invalid tag count should fail");
|
||||
let error =
|
||||
apply_publish_overrides_to_draft(&draft, None, None, Some(vec!["蒸汽".to_string()]))
|
||||
.expect_err("invalid tag count should fail");
|
||||
|
||||
assert_eq!(error, PuzzleFieldError::InvalidTagCount);
|
||||
}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
use serde_json::{Map, Value, json};
|
||||
|
||||
use shared_contracts::runtime_story::{
|
||||
RuntimeBattlePresentation, RuntimeStoryActionRequest, RuntimeStoryOptionView,
|
||||
RuntimeStoryPatch,
|
||||
RuntimeBattlePresentation, RuntimeStoryActionRequest, RuntimeStoryOptionView, RuntimeStoryPatch,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
@@ -471,7 +470,10 @@ fn read_player_inventory_items(game_state: &Value) -> Vec<BattleInventoryItemVie
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn find_player_inventory_item(game_state: &Value, item_id: &str) -> Option<BattleInventoryItemView> {
|
||||
fn find_player_inventory_item(
|
||||
game_state: &Value,
|
||||
item_id: &str,
|
||||
) -> Option<BattleInventoryItemView> {
|
||||
read_player_inventory_items(game_state)
|
||||
.into_iter()
|
||||
.find(|item| item.id == item_id)
|
||||
@@ -798,10 +800,7 @@ fn build_inventory_use_battle_action_plan(
|
||||
})
|
||||
}
|
||||
|
||||
fn battle_action_toast(
|
||||
function_id: &str,
|
||||
request: &RuntimeStoryActionRequest,
|
||||
) -> Option<String> {
|
||||
fn battle_action_toast(function_id: &str, request: &RuntimeStoryActionRequest) -> Option<String> {
|
||||
if function_id != "inventory_use" {
|
||||
return None;
|
||||
}
|
||||
|
||||
@@ -115,7 +115,10 @@ pub fn resolve_forge_dismantle_action(
|
||||
next_inventory = remove_inventory_item_from_list(next_inventory, item_id.as_str(), 1);
|
||||
next_inventory = add_inventory_items_to_list(next_inventory, outputs.clone());
|
||||
write_player_inventory_values(game_state, next_inventory);
|
||||
let output_names = outputs.iter().map(read_inventory_item_name).collect::<Vec<_>>();
|
||||
let output_names = outputs
|
||||
.iter()
|
||||
.map(read_inventory_item_name)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
Ok(StoryResolution {
|
||||
action_text: resolve_action_text(
|
||||
|
||||
@@ -13,6 +13,9 @@ pub mod npc_support;
|
||||
pub mod options;
|
||||
pub mod view_model;
|
||||
|
||||
pub use battle::{
|
||||
build_battle_runtime_story_options, resolve_battle_action, restore_player_resource,
|
||||
};
|
||||
pub use core::{
|
||||
MAX_PLAYER_LEVEL, add_player_currency, add_player_inventory_items, append_active_build_buffs,
|
||||
append_story_history, clear_encounter_only, clear_encounter_state, cumulative_xp_required,
|
||||
@@ -25,27 +28,26 @@ pub use core::{
|
||||
write_first_hostile_npc_i32_field, write_i32_field, write_null_field, write_string_field,
|
||||
write_u32_field, xp_to_next_level_for,
|
||||
};
|
||||
pub use forge::{build_runtime_equipment_item, build_runtime_material_item, format_currency_text};
|
||||
pub use forge_actions::{
|
||||
resolve_forge_craft_action, resolve_forge_dismantle_action, resolve_forge_reforge_action,
|
||||
};
|
||||
pub use game_state::{
|
||||
add_inventory_items_to_list, apply_equipment_loadout_to_state, battle_mode_text,
|
||||
build_current_build_toast, clone_inventory_item_with_quantity, current_encounter_id,
|
||||
current_encounter_name, current_encounter_name_from_battle, ensure_inventory_action_available,
|
||||
equipment_bonus_fallbacks, equipment_item_bonuses, equipment_slot_label,
|
||||
find_player_inventory_entry, has_giftable_player_inventory, item_rarity_key,
|
||||
normalize_equipped_item, normalize_equipment_slot_id, read_equipment_total_bonuses,
|
||||
normalize_equipment_slot_id, normalize_equipped_item, read_equipment_total_bonuses,
|
||||
read_inventory_item_name, read_player_equipment_item, read_player_inventory_values,
|
||||
read_runtime_equipment_bonus_cache, remove_inventory_item_from_list,
|
||||
resolve_equipment_slot_for_item, write_player_equipment_item, write_player_inventory_values,
|
||||
write_runtime_equipment_bonus_cache,
|
||||
};
|
||||
pub use forge::{build_runtime_equipment_item, build_runtime_material_item, format_currency_text};
|
||||
pub use forge_actions::{
|
||||
resolve_forge_craft_action, resolve_forge_dismantle_action, resolve_forge_reforge_action,
|
||||
};
|
||||
pub use npc_support::{
|
||||
build_npc_gift_result_text, npc_buyback_price, npc_purchase_price, recruit_companion_to_party,
|
||||
resolve_npc_gift_affinity_gain, trade_quantity_suffix,
|
||||
};
|
||||
pub use battle::{build_battle_runtime_story_options, resolve_battle_action, restore_player_resource};
|
||||
pub use options::{
|
||||
build_disabled_runtime_story_option, build_runtime_story_option_from_story_option,
|
||||
build_runtime_story_option_interaction, build_runtime_story_option_with_payload,
|
||||
|
||||
@@ -1,12 +1,8 @@
|
||||
use serde_json::Value;
|
||||
|
||||
use shared_contracts::runtime_story::{
|
||||
RuntimeStoryOptionInteraction, RuntimeStoryOptionView,
|
||||
};
|
||||
use shared_contracts::runtime_story::{RuntimeStoryOptionInteraction, RuntimeStoryOptionView};
|
||||
|
||||
use crate::{
|
||||
read_bool_field, read_field, read_optional_string_field, read_required_string_field,
|
||||
};
|
||||
use crate::{read_bool_field, read_field, read_optional_string_field, read_required_string_field};
|
||||
|
||||
/// 这批 helper 只负责 runtime story option 的纯 DTO 编译,不触碰 HTTP / AppState。
|
||||
pub fn infer_option_scope(function_id: &str) -> &'static str {
|
||||
|
||||
@@ -38,9 +38,7 @@ pub fn build_runtime_story_view_model(
|
||||
}
|
||||
}
|
||||
|
||||
pub fn build_runtime_story_companions(
|
||||
game_state: &Value,
|
||||
) -> Vec<RuntimeStoryCompanionViewModel> {
|
||||
pub fn build_runtime_story_companions(game_state: &Value) -> Vec<RuntimeStoryCompanionViewModel> {
|
||||
read_array_field(game_state, "companions")
|
||||
.into_iter()
|
||||
.filter_map(|entry| {
|
||||
@@ -54,9 +52,7 @@ pub fn build_runtime_story_companions(
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn build_runtime_story_encounter(
|
||||
game_state: &Value,
|
||||
) -> Option<RuntimeStoryEncounterViewModel> {
|
||||
pub fn build_runtime_story_encounter(game_state: &Value) -> Option<RuntimeStoryEncounterViewModel> {
|
||||
let encounter = read_object_field(game_state, "currentEncounter")?;
|
||||
let npc_name = read_required_string_field(encounter, "npcName")
|
||||
.or_else(|| read_required_string_field(encounter, "name"))
|
||||
|
||||
@@ -652,10 +652,10 @@ pub fn build_runtime_snapshot_upsert_input(
|
||||
updated_at_micros: i64,
|
||||
) -> Result<RuntimeSnapshotUpsertInput, RuntimeProfileFieldError> {
|
||||
let user_id = normalize_runtime_profile_user_id(user_id)?;
|
||||
let bottom_tab = normalize_bottom_tab(bottom_tab)
|
||||
.ok_or(RuntimeProfileFieldError::MissingBottomTab)?;
|
||||
let game_state_json =
|
||||
serde_json::to_string(&game_state).map_err(|_| RuntimeProfileFieldError::InvalidGameStateJson)?;
|
||||
let bottom_tab =
|
||||
normalize_bottom_tab(bottom_tab).ok_or(RuntimeProfileFieldError::MissingBottomTab)?;
|
||||
let game_state_json = serde_json::to_string(&game_state)
|
||||
.map_err(|_| RuntimeProfileFieldError::InvalidGameStateJson)?;
|
||||
let current_story_json = normalize_current_story_json(current_story)?;
|
||||
|
||||
Ok(RuntimeSnapshotUpsertInput {
|
||||
@@ -1012,7 +1012,9 @@ impl std::fmt::Display for RuntimeProfileFieldError {
|
||||
Self::MissingUserId => f.write_str("profile.user_id 不能为空"),
|
||||
Self::MissingWorldKey => f.write_str("profile.world_key 不能为空"),
|
||||
Self::MissingBottomTab => f.write_str("runtime_snapshot.bottom_tab 不能为空"),
|
||||
Self::InvalidGameStateJson => f.write_str("runtime_snapshot.game_state 必须是合法 JSON"),
|
||||
Self::InvalidGameStateJson => {
|
||||
f.write_str("runtime_snapshot.game_state 必须是合法 JSON")
|
||||
}
|
||||
Self::InvalidCurrentStoryJson => {
|
||||
f.write_str("runtime_snapshot.current_story 必须是合法 JSON object 或 null")
|
||||
}
|
||||
|
||||
@@ -504,10 +504,7 @@ impl SmsAuthProvider {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn verify_code(
|
||||
&self,
|
||||
request: SmsVerifyCodeRequest,
|
||||
) -> Result<(), SmsProviderError> {
|
||||
pub async fn verify_code(&self, request: SmsVerifyCodeRequest) -> Result<(), SmsProviderError> {
|
||||
match self {
|
||||
Self::Mock(provider) => provider.verify_code(request).await,
|
||||
Self::Aliyun(provider) => provider.verify_code(request).await,
|
||||
@@ -520,7 +517,8 @@ impl MockSmsAuthProvider {
|
||||
&self,
|
||||
request: SmsSendCodeRequest,
|
||||
) -> Result<SmsSendCodeResult, SmsProviderError> {
|
||||
let provider_out_id = build_sms_provider_out_id(&request.scene, &request.national_phone_number);
|
||||
let provider_out_id =
|
||||
build_sms_provider_out_id(&request.scene, &request.national_phone_number);
|
||||
|
||||
Ok(SmsSendCodeResult {
|
||||
cooldown_seconds: self.config.interval_seconds,
|
||||
@@ -530,10 +528,7 @@ impl MockSmsAuthProvider {
|
||||
})
|
||||
}
|
||||
|
||||
async fn verify_code(
|
||||
&self,
|
||||
request: SmsVerifyCodeRequest,
|
||||
) -> Result<(), SmsProviderError> {
|
||||
async fn verify_code(&self, request: SmsVerifyCodeRequest) -> Result<(), SmsProviderError> {
|
||||
if request.verify_code.trim() != self.config.mock_verify_code {
|
||||
return Err(SmsProviderError::InvalidVerifyCode);
|
||||
}
|
||||
@@ -546,7 +541,8 @@ impl AliyunSmsAuthProvider {
|
||||
&self,
|
||||
request: SmsSendCodeRequest,
|
||||
) -> Result<SmsSendCodeResult, SmsProviderError> {
|
||||
let provider_out_id = build_sms_provider_out_id(&request.scene, &request.national_phone_number);
|
||||
let provider_out_id =
|
||||
build_sms_provider_out_id(&request.scene, &request.national_phone_number);
|
||||
let phone_masked = mask_phone_number(&request.national_phone_number);
|
||||
let template_param = serde_json::json!({
|
||||
self.config.template_param_key.clone(): "##code##",
|
||||
@@ -577,26 +573,23 @@ impl AliyunSmsAuthProvider {
|
||||
query.insert("SignatureVersion".to_string(), "1.0".to_string());
|
||||
query.insert(
|
||||
"AccessKeyId".to_string(),
|
||||
self.config
|
||||
.access_key_id
|
||||
.clone()
|
||||
.unwrap_or_default(),
|
||||
self.config.access_key_id.clone().unwrap_or_default(),
|
||||
);
|
||||
query.insert(
|
||||
"PhoneNumber".to_string(),
|
||||
request.national_phone_number.trim().to_string(),
|
||||
);
|
||||
query.insert(
|
||||
"CountryCode".to_string(),
|
||||
self.config.country_code.clone(),
|
||||
);
|
||||
query.insert("CountryCode".to_string(), self.config.country_code.clone());
|
||||
query.insert("SignName".to_string(), self.config.sign_name.clone());
|
||||
query.insert(
|
||||
"TemplateCode".to_string(),
|
||||
self.config.template_code.clone(),
|
||||
);
|
||||
query.insert("TemplateParam".to_string(), template_param);
|
||||
query.insert("CodeLength".to_string(), self.config.code_length.to_string());
|
||||
query.insert(
|
||||
"CodeLength".to_string(),
|
||||
self.config.code_length.to_string(),
|
||||
);
|
||||
query.insert("CodeType".to_string(), self.config.code_type.to_string());
|
||||
query.insert(
|
||||
"ValidTime".to_string(),
|
||||
@@ -640,7 +633,10 @@ impl AliyunSmsAuthProvider {
|
||||
provider_request_id = body
|
||||
.request_id
|
||||
.as_deref()
|
||||
.or_else(|| body.model.as_ref().and_then(|model| model.request_id.as_deref()))
|
||||
.or_else(|| body
|
||||
.model
|
||||
.as_ref()
|
||||
.and_then(|model| model.request_id.as_deref()))
|
||||
.unwrap_or("unknown"),
|
||||
provider_out_id = body
|
||||
.model
|
||||
@@ -661,7 +657,10 @@ impl AliyunSmsAuthProvider {
|
||||
provider_request_id = body
|
||||
.request_id
|
||||
.as_deref()
|
||||
.or_else(|| body.model.as_ref().and_then(|model| model.request_id.as_deref()))
|
||||
.or_else(|| body
|
||||
.model
|
||||
.as_ref()
|
||||
.and_then(|model| model.request_id.as_deref()))
|
||||
.unwrap_or("unknown"),
|
||||
provider_out_id = body
|
||||
.model
|
||||
@@ -680,17 +679,16 @@ impl AliyunSmsAuthProvider {
|
||||
Ok(SmsSendCodeResult {
|
||||
cooldown_seconds: self.config.interval_seconds,
|
||||
expires_in_seconds: self.config.valid_time_seconds,
|
||||
provider_request_id: body
|
||||
.request_id
|
||||
.or_else(|| body.model.as_ref().and_then(|model| model.request_id.clone())),
|
||||
provider_request_id: body.request_id.or_else(|| {
|
||||
body.model
|
||||
.as_ref()
|
||||
.and_then(|model| model.request_id.clone())
|
||||
}),
|
||||
provider_out_id: body.model.and_then(|model| model.out_id),
|
||||
})
|
||||
}
|
||||
|
||||
async fn verify_code(
|
||||
&self,
|
||||
request: SmsVerifyCodeRequest,
|
||||
) -> Result<(), SmsProviderError> {
|
||||
async fn verify_code(&self, request: SmsVerifyCodeRequest) -> Result<(), SmsProviderError> {
|
||||
let mut query = BTreeMap::new();
|
||||
query.insert("Action".to_string(), "CheckSmsVerifyCode".to_string());
|
||||
query.insert("Format".to_string(), "json".to_string());
|
||||
@@ -701,19 +699,13 @@ impl AliyunSmsAuthProvider {
|
||||
query.insert("SignatureVersion".to_string(), "1.0".to_string());
|
||||
query.insert(
|
||||
"AccessKeyId".to_string(),
|
||||
self.config
|
||||
.access_key_id
|
||||
.clone()
|
||||
.unwrap_or_default(),
|
||||
self.config.access_key_id.clone().unwrap_or_default(),
|
||||
);
|
||||
query.insert(
|
||||
"PhoneNumber".to_string(),
|
||||
request.national_phone_number.trim().to_string(),
|
||||
);
|
||||
query.insert(
|
||||
"CountryCode".to_string(),
|
||||
self.config.country_code.clone(),
|
||||
);
|
||||
query.insert("CountryCode".to_string(), self.config.country_code.clone());
|
||||
query.insert(
|
||||
"VerifyCode".to_string(),
|
||||
request.verify_code.trim().to_string(),
|
||||
@@ -746,12 +738,7 @@ impl AliyunSmsAuthProvider {
|
||||
body.code,
|
||||
));
|
||||
}
|
||||
if body
|
||||
.model
|
||||
.and_then(|model| model.verify_result)
|
||||
.as_deref()
|
||||
!= Some("PASS")
|
||||
{
|
||||
if body.model.and_then(|model| model.verify_result).as_deref() != Some("PASS") {
|
||||
return Err(SmsProviderError::InvalidVerifyCode);
|
||||
}
|
||||
|
||||
@@ -759,11 +746,9 @@ impl AliyunSmsAuthProvider {
|
||||
}
|
||||
|
||||
fn sign_query(&self, query: &mut BTreeMap<String, String>) -> Result<(), SmsProviderError> {
|
||||
let access_key_secret = self
|
||||
.config
|
||||
.access_key_secret
|
||||
.as_deref()
|
||||
.ok_or_else(|| SmsProviderError::InvalidConfig("阿里云短信 AccessKeySecret 未配置".to_string()))?;
|
||||
let access_key_secret = self.config.access_key_secret.as_deref().ok_or_else(|| {
|
||||
SmsProviderError::InvalidConfig("阿里云短信 AccessKeySecret 未配置".to_string())
|
||||
})?;
|
||||
let canonicalized = canonicalize_aliyun_rpc_params(query);
|
||||
let string_to_sign = format!(
|
||||
"POST&{}&{}",
|
||||
@@ -771,7 +756,9 @@ impl AliyunSmsAuthProvider {
|
||||
aliyun_percent_encode(&canonicalized)
|
||||
);
|
||||
let mut signer = HmacSha1::new_from_slice(format!("{access_key_secret}&").as_bytes())
|
||||
.map_err(|error| SmsProviderError::InvalidConfig(format!("初始化短信签名器失败:{error}")))?;
|
||||
.map_err(|error| {
|
||||
SmsProviderError::InvalidConfig(format!("初始化短信签名器失败:{error}"))
|
||||
})?;
|
||||
signer.update(string_to_sign.as_bytes());
|
||||
let signature = BASE64_STANDARD.encode(signer.finalize().into_bytes());
|
||||
query.insert("Signature".to_string(), signature);
|
||||
@@ -1138,9 +1125,10 @@ async fn parse_aliyun_json_response(
|
||||
.text()
|
||||
.await
|
||||
.map_err(|error| SmsProviderError::Upstream(format!("{fallback_message}:{error}")))?;
|
||||
let payload = serde_json::from_str::<AliyunSendSmsVerifyCodeResponse>(&body).map_err(|error| {
|
||||
SmsProviderError::Upstream(format!("{fallback_message}:响应解析失败:{error}"))
|
||||
})?;
|
||||
let payload =
|
||||
serde_json::from_str::<AliyunSendSmsVerifyCodeResponse>(&body).map_err(|error| {
|
||||
SmsProviderError::Upstream(format!("{fallback_message}:响应解析失败:{error}"))
|
||||
})?;
|
||||
if status.is_client_error() || status.is_server_error() {
|
||||
return Err(map_http_status_to_sms_provider_error(
|
||||
fallback_message,
|
||||
@@ -1160,8 +1148,10 @@ async fn parse_aliyun_json_response_for_verify(
|
||||
.text()
|
||||
.await
|
||||
.map_err(|error| SmsProviderError::Upstream(format!("验证码校验失败:{error}")))?;
|
||||
let payload = serde_json::from_str::<AliyunCheckSmsVerifyCodeResponse>(&body)
|
||||
.map_err(|error| SmsProviderError::Upstream(format!("验证码校验失败:响应解析失败:{error}")))?;
|
||||
let payload =
|
||||
serde_json::from_str::<AliyunCheckSmsVerifyCodeResponse>(&body).map_err(|error| {
|
||||
SmsProviderError::Upstream(format!("验证码校验失败:响应解析失败:{error}"))
|
||||
})?;
|
||||
if status.is_client_error() || status.is_server_error() {
|
||||
return Err(map_http_status_to_sms_provider_error(
|
||||
"验证码校验失败",
|
||||
@@ -1461,7 +1451,10 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn sms_auth_provider_kind_parses_supported_values() {
|
||||
assert_eq!(SmsAuthProviderKind::parse("mock"), Some(SmsAuthProviderKind::Mock));
|
||||
assert_eq!(
|
||||
SmsAuthProviderKind::parse("mock"),
|
||||
Some(SmsAuthProviderKind::Mock)
|
||||
);
|
||||
assert_eq!(
|
||||
SmsAuthProviderKind::parse("aliyun"),
|
||||
Some(SmsAuthProviderKind::Aliyun)
|
||||
@@ -1482,7 +1475,10 @@ mod tests {
|
||||
.expect("send code should succeed");
|
||||
|
||||
assert_eq!(send_result.cooldown_seconds, DEFAULT_SMS_INTERVAL_SECONDS);
|
||||
assert_eq!(send_result.expires_in_seconds, DEFAULT_SMS_VALID_TIME_SECONDS);
|
||||
assert_eq!(
|
||||
send_result.expires_in_seconds,
|
||||
DEFAULT_SMS_VALID_TIME_SECONDS
|
||||
);
|
||||
assert_eq!(
|
||||
send_result.provider_request_id.as_deref(),
|
||||
Some("mock-request-id")
|
||||
@@ -1548,7 +1544,10 @@ mod tests {
|
||||
#[test]
|
||||
fn canonicalize_aliyun_rpc_params_keeps_sorted_percent_encoded_order() {
|
||||
let mut params = BTreeMap::new();
|
||||
params.insert("TemplateParam".to_string(), "{\"code\":\"##code##\"}".to_string());
|
||||
params.insert(
|
||||
"TemplateParam".to_string(),
|
||||
"{\"code\":\"##code##\"}".to_string(),
|
||||
);
|
||||
params.insert("Action".to_string(), "SendSmsVerifyCode".to_string());
|
||||
params.insert("PhoneNumber".to_string(), "13800138000".to_string());
|
||||
|
||||
@@ -1580,7 +1579,10 @@ mod tests {
|
||||
assert_eq!(payload.request_id.as_deref(), Some("req_123"));
|
||||
assert_eq!(payload.success, Some(true));
|
||||
assert_eq!(
|
||||
payload.model.as_ref().and_then(|model| model.out_id.as_deref()),
|
||||
payload
|
||||
.model
|
||||
.as_ref()
|
||||
.and_then(|model| model.out_id.as_deref()),
|
||||
Some("out_789")
|
||||
);
|
||||
assert_eq!(
|
||||
|
||||
@@ -458,11 +458,9 @@ impl LlmClient {
|
||||
}
|
||||
|
||||
if !undecoded_chunk_bytes.is_empty() {
|
||||
let trailing_text = std_str::from_utf8(undecoded_chunk_bytes.as_slice())
|
||||
.map_err(|error| {
|
||||
LlmError::Deserialize(format!(
|
||||
"解析 LLM 流式 UTF-8 响应失败:{error}"
|
||||
))
|
||||
let trailing_text =
|
||||
std_str::from_utf8(undecoded_chunk_bytes.as_slice()).map_err(|error| {
|
||||
LlmError::Deserialize(format!("解析 LLM 流式 UTF-8 响应失败:{error}"))
|
||||
})?;
|
||||
if !trailing_text.is_empty() {
|
||||
for event in parser.push_chunk(trailing_text)? {
|
||||
@@ -761,9 +759,7 @@ fn decode_utf8_stream_chunk(bytes: &[u8]) -> Result<(String, Vec<u8>), LlmError>
|
||||
let valid_up_to = error.valid_up_to();
|
||||
let Some(_) = error.error_len() else {
|
||||
let decoded = std_str::from_utf8(&bytes[..valid_up_to]).map_err(|inner_error| {
|
||||
LlmError::Deserialize(format!(
|
||||
"解析 LLM 流式 UTF-8 响应失败:{inner_error}"
|
||||
))
|
||||
LlmError::Deserialize(format!("解析 LLM 流式 UTF-8 响应失败:{inner_error}"))
|
||||
})?;
|
||||
return Ok((decoded.to_string(), bytes[valid_up_to..].to_vec()));
|
||||
};
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::Value;
|
||||
|
||||
// 管理后台协议统一收口在 shared-contracts,避免页面脚本和 Rust handler 各自手拼字段。
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct AdminLoginRequest {
|
||||
@@ -8,6 +9,7 @@ pub struct AdminLoginRequest {
|
||||
pub password: String,
|
||||
}
|
||||
|
||||
// 登录成功后返回管理员访问令牌与基础会话信息。
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct AdminLoginResponse {
|
||||
@@ -15,6 +17,7 @@ pub struct AdminLoginResponse {
|
||||
pub admin: AdminSessionPayload,
|
||||
}
|
||||
|
||||
// 管理员会话只暴露页面展示和鉴权调试所需的最小字段。
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct AdminSessionPayload {
|
||||
@@ -26,12 +29,14 @@ pub struct AdminSessionPayload {
|
||||
pub expires_at: String,
|
||||
}
|
||||
|
||||
// 页面恢复登录态时读取当前管理员会话。
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct AdminMeResponse {
|
||||
pub admin: AdminSessionPayload,
|
||||
}
|
||||
|
||||
// 后台概览统一返回服务信息与数据库信息两块,前端不再额外拼装。
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct AdminOverviewResponse {
|
||||
@@ -39,6 +44,7 @@ pub struct AdminOverviewResponse {
|
||||
pub database: AdminDatabaseOverviewPayload,
|
||||
}
|
||||
|
||||
// 服务概览描述当前 api-server 与 SpacetimeDB 连接配置。
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct AdminServiceOverviewPayload {
|
||||
@@ -50,6 +56,7 @@ pub struct AdminServiceOverviewPayload {
|
||||
pub spacetime_database: String,
|
||||
}
|
||||
|
||||
// 数据库概览返回真实数据库元信息、表清单与统计错误。
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct AdminDatabaseOverviewPayload {
|
||||
@@ -61,6 +68,7 @@ pub struct AdminDatabaseOverviewPayload {
|
||||
pub fetch_errors: Vec<String>,
|
||||
}
|
||||
|
||||
// 单表统计允许成功和失败并存,避免某张表失败导致整页概览不可用。
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct AdminDatabaseTableStatPayload {
|
||||
@@ -69,6 +77,7 @@ pub struct AdminDatabaseTableStatPayload {
|
||||
pub error_message: Option<String>,
|
||||
}
|
||||
|
||||
// 调试请求只允许同源路径、受控请求头和有限请求体。
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct AdminDebugHttpRequest {
|
||||
@@ -78,6 +87,7 @@ pub struct AdminDebugHttpRequest {
|
||||
pub body: Option<String>,
|
||||
}
|
||||
|
||||
// 调试请求头使用显式结构,避免页面直接塞任意对象。
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct AdminDebugHeaderInput {
|
||||
@@ -85,6 +95,7 @@ pub struct AdminDebugHeaderInput {
|
||||
pub value: String,
|
||||
}
|
||||
|
||||
// 调试响应回显状态、响应头与文本/JSON 预览,便于后台排查接口问题。
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct AdminDebugHttpResponse {
|
||||
|
||||
@@ -155,8 +155,7 @@ use crate::module_bindings::{
|
||||
BigFishSessionGetInput as BindingBigFishSessionGetInput,
|
||||
BigFishSessionProcedureResult as BindingBigFishSessionProcedureResult,
|
||||
BigFishSessionSnapshot as BindingBigFishSessionSnapshot,
|
||||
BigFishVector2 as BindingBigFishVector2,
|
||||
BigFishWorksListInput as BindingBigFishWorksListInput,
|
||||
BigFishVector2 as BindingBigFishVector2, BigFishWorksListInput as BindingBigFishWorksListInput,
|
||||
BigFishWorksProcedureResult as BindingBigFishWorksProcedureResult,
|
||||
CombatOutcome as BindingCombatOutcome,
|
||||
CustomWorldAgentActionExecuteInput as BindingCustomWorldAgentActionExecuteInput,
|
||||
@@ -323,10 +322,10 @@ use crate::module_bindings::{
|
||||
get_runtime_setting_or_default_procedure::get_runtime_setting_or_default as _,
|
||||
get_runtime_snapshot_procedure::get_runtime_snapshot as _,
|
||||
get_story_session_state_procedure::get_story_session_state as _,
|
||||
list_big_fish_works_procedure::list_big_fish_works as _,
|
||||
list_custom_world_gallery_entries_procedure::list_custom_world_gallery_entries as _,
|
||||
list_custom_world_profiles_procedure::list_custom_world_profiles as _,
|
||||
list_custom_world_works_procedure::list_custom_world_works as _,
|
||||
list_big_fish_works_procedure::list_big_fish_works as _,
|
||||
list_platform_browse_history_procedure::list_platform_browse_history as _,
|
||||
list_profile_save_archives_procedure::list_profile_save_archives as _,
|
||||
list_profile_wallet_ledger_procedure::list_profile_wallet_ledger as _,
|
||||
@@ -1534,15 +1533,14 @@ impl SpacetimeClient {
|
||||
let procedure_input = BindingBigFishWorksListInput { owner_user_id };
|
||||
|
||||
self.call_after_connect(move |connection, sender| {
|
||||
connection.procedures().list_big_fish_works_then(
|
||||
procedure_input,
|
||||
move |_, result| {
|
||||
connection
|
||||
.procedures()
|
||||
.list_big_fish_works_then(procedure_input, move |_, result| {
|
||||
let mapped = result
|
||||
.map_err(|error| SpacetimeClientError::Procedure(error.to_string()))
|
||||
.and_then(map_big_fish_works_procedure_result);
|
||||
send_once(&sender, mapped);
|
||||
},
|
||||
);
|
||||
});
|
||||
})
|
||||
.await
|
||||
}
|
||||
@@ -3582,8 +3580,9 @@ fn map_big_fish_works_procedure_result(
|
||||
"SpacetimeDB procedure 未返回 big fish works 快照".to_string(),
|
||||
)
|
||||
})?;
|
||||
serde_json::from_str::<Vec<BigFishWorkSummaryRecord>>(&items_json)
|
||||
.map_err(|error| SpacetimeClientError::Runtime(format!("big fish works items_json 非法: {error}")))
|
||||
serde_json::from_str::<Vec<BigFishWorkSummaryRecord>>(&items_json).map_err(|error| {
|
||||
SpacetimeClientError::Runtime(format!("big fish works items_json 非法: {error}"))
|
||||
})
|
||||
}
|
||||
|
||||
fn map_big_fish_run_procedure_result(
|
||||
|
||||
@@ -2,12 +2,7 @@
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
#![allow(unused, clippy::all)]
|
||||
use spacetimedb_sdk::__codegen::{
|
||||
self as __sdk,
|
||||
__lib,
|
||||
__sats,
|
||||
__ws,
|
||||
};
|
||||
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
||||
|
||||
use super::quest_record_input_type::QuestRecordInput;
|
||||
|
||||
@@ -19,10 +14,8 @@ pub(super) struct AcceptQuestArgs {
|
||||
|
||||
impl From<AcceptQuestArgs> for super::Reducer {
|
||||
fn from(args: AcceptQuestArgs) -> Self {
|
||||
Self::AcceptQuest {
|
||||
input: args.input,
|
||||
}
|
||||
}
|
||||
Self::AcceptQuest { input: args.input }
|
||||
}
|
||||
}
|
||||
|
||||
impl __sdk::InModule for AcceptQuestArgs {
|
||||
@@ -40,9 +33,8 @@ pub trait accept_quest {
|
||||
/// The reducer will run asynchronously in the future,
|
||||
/// and this method provides no way to listen for its completion status.
|
||||
/// /// Use [`accept_quest:accept_quest_then`] to run a callback after the reducer completes.
|
||||
fn accept_quest(&self, input: QuestRecordInput,
|
||||
) -> __sdk::Result<()> {
|
||||
self.accept_quest_then(input, |_, _| {})
|
||||
fn accept_quest(&self, input: QuestRecordInput) -> __sdk::Result<()> {
|
||||
self.accept_quest_then(input, |_, _| {})
|
||||
}
|
||||
|
||||
/// Request that the remote module invoke the reducer `accept_quest` to run as soon as possible,
|
||||
@@ -55,9 +47,11 @@ pub trait accept_quest {
|
||||
&self,
|
||||
input: QuestRecordInput,
|
||||
|
||||
callback: impl FnOnce(&super::ReducerEventContext, Result<Result<(), String>, __sdk::InternalError>)
|
||||
+ Send
|
||||
+ 'static,
|
||||
callback: impl FnOnce(
|
||||
&super::ReducerEventContext,
|
||||
Result<Result<(), String>, __sdk::InternalError>,
|
||||
) + Send
|
||||
+ 'static,
|
||||
) -> __sdk::Result<()>;
|
||||
}
|
||||
|
||||
@@ -66,11 +60,13 @@ impl accept_quest for super::RemoteReducers {
|
||||
&self,
|
||||
input: QuestRecordInput,
|
||||
|
||||
callback: impl FnOnce(&super::ReducerEventContext, Result<Result<(), String>, __sdk::InternalError>)
|
||||
+ Send
|
||||
+ 'static,
|
||||
callback: impl FnOnce(
|
||||
&super::ReducerEventContext,
|
||||
Result<Result<(), String>, __sdk::InternalError>,
|
||||
) + Send
|
||||
+ 'static,
|
||||
) -> __sdk::Result<()> {
|
||||
self.imp.invoke_reducer_with_callback(AcceptQuestArgs { input, }, callback)
|
||||
self.imp
|
||||
.invoke_reducer_with_callback(AcceptQuestArgs { input }, callback)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,12 +2,7 @@
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
#![allow(unused, clippy::all)]
|
||||
use spacetimedb_sdk::__codegen::{
|
||||
self as __sdk,
|
||||
__lib,
|
||||
__sats,
|
||||
__ws,
|
||||
};
|
||||
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
||||
|
||||
use super::quest_completion_ack_input_type::QuestCompletionAckInput;
|
||||
|
||||
@@ -19,10 +14,8 @@ pub(super) struct AcknowledgeQuestCompletionArgs {
|
||||
|
||||
impl From<AcknowledgeQuestCompletionArgs> for super::Reducer {
|
||||
fn from(args: AcknowledgeQuestCompletionArgs) -> Self {
|
||||
Self::AcknowledgeQuestCompletion {
|
||||
input: args.input,
|
||||
}
|
||||
}
|
||||
Self::AcknowledgeQuestCompletion { input: args.input }
|
||||
}
|
||||
}
|
||||
|
||||
impl __sdk::InModule for AcknowledgeQuestCompletionArgs {
|
||||
@@ -40,9 +33,8 @@ pub trait acknowledge_quest_completion {
|
||||
/// The reducer will run asynchronously in the future,
|
||||
/// and this method provides no way to listen for its completion status.
|
||||
/// /// Use [`acknowledge_quest_completion:acknowledge_quest_completion_then`] to run a callback after the reducer completes.
|
||||
fn acknowledge_quest_completion(&self, input: QuestCompletionAckInput,
|
||||
) -> __sdk::Result<()> {
|
||||
self.acknowledge_quest_completion_then(input, |_, _| {})
|
||||
fn acknowledge_quest_completion(&self, input: QuestCompletionAckInput) -> __sdk::Result<()> {
|
||||
self.acknowledge_quest_completion_then(input, |_, _| {})
|
||||
}
|
||||
|
||||
/// Request that the remote module invoke the reducer `acknowledge_quest_completion` to run as soon as possible,
|
||||
@@ -55,9 +47,11 @@ pub trait acknowledge_quest_completion {
|
||||
&self,
|
||||
input: QuestCompletionAckInput,
|
||||
|
||||
callback: impl FnOnce(&super::ReducerEventContext, Result<Result<(), String>, __sdk::InternalError>)
|
||||
+ Send
|
||||
+ 'static,
|
||||
callback: impl FnOnce(
|
||||
&super::ReducerEventContext,
|
||||
Result<Result<(), String>, __sdk::InternalError>,
|
||||
) + Send
|
||||
+ 'static,
|
||||
) -> __sdk::Result<()>;
|
||||
}
|
||||
|
||||
@@ -66,11 +60,13 @@ impl acknowledge_quest_completion for super::RemoteReducers {
|
||||
&self,
|
||||
input: QuestCompletionAckInput,
|
||||
|
||||
callback: impl FnOnce(&super::ReducerEventContext, Result<Result<(), String>, __sdk::InternalError>)
|
||||
+ Send
|
||||
+ 'static,
|
||||
callback: impl FnOnce(
|
||||
&super::ReducerEventContext,
|
||||
Result<Result<(), String>, __sdk::InternalError>,
|
||||
) + Send
|
||||
+ 'static,
|
||||
) -> __sdk::Result<()> {
|
||||
self.imp.invoke_reducer_with_callback(AcknowledgeQuestCompletionArgs { input, }, callback)
|
||||
self.imp
|
||||
.invoke_reducer_with_callback(AcknowledgeQuestCompletionArgs { input }, callback)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,23 +2,17 @@
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
#![allow(unused, clippy::all)]
|
||||
use spacetimedb_sdk::__codegen::{
|
||||
self as __sdk,
|
||||
__lib,
|
||||
__sats,
|
||||
__ws,
|
||||
};
|
||||
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
||||
|
||||
use super::puzzle_run_next_level_input_type::PuzzleRunNextLevelInput;
|
||||
use super::puzzle_run_procedure_result_type::PuzzleRunProcedureResult;
|
||||
|
||||
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
|
||||
#[sats(crate = __lib)]
|
||||
struct AdvancePuzzleNextLevelArgs {
|
||||
struct AdvancePuzzleNextLevelArgs {
|
||||
pub input: PuzzleRunNextLevelInput,
|
||||
}
|
||||
|
||||
|
||||
impl __sdk::InModule for AdvancePuzzleNextLevelArgs {
|
||||
type Module = super::RemoteModule;
|
||||
}
|
||||
@@ -28,16 +22,19 @@ impl __sdk::InModule for AdvancePuzzleNextLevelArgs {
|
||||
///
|
||||
/// Implemented for [`super::RemoteProcedures`].
|
||||
pub trait advance_puzzle_next_level {
|
||||
fn advance_puzzle_next_level(&self, input: PuzzleRunNextLevelInput,
|
||||
) {
|
||||
self.advance_puzzle_next_level_then(input, |_, _| {});
|
||||
fn advance_puzzle_next_level(&self, input: PuzzleRunNextLevelInput) {
|
||||
self.advance_puzzle_next_level_then(input, |_, _| {});
|
||||
}
|
||||
|
||||
fn advance_puzzle_next_level_then(
|
||||
&self,
|
||||
input: PuzzleRunNextLevelInput,
|
||||
|
||||
__callback: impl FnOnce(&super::ProcedureEventContext, Result<PuzzleRunProcedureResult, __sdk::InternalError>) + Send + 'static,
|
||||
__callback: impl FnOnce(
|
||||
&super::ProcedureEventContext,
|
||||
Result<PuzzleRunProcedureResult, __sdk::InternalError>,
|
||||
) + Send
|
||||
+ 'static,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -46,13 +43,17 @@ impl advance_puzzle_next_level for super::RemoteProcedures {
|
||||
&self,
|
||||
input: PuzzleRunNextLevelInput,
|
||||
|
||||
__callback: impl FnOnce(&super::ProcedureEventContext, Result<PuzzleRunProcedureResult, __sdk::InternalError>) + Send + 'static,
|
||||
__callback: impl FnOnce(
|
||||
&super::ProcedureEventContext,
|
||||
Result<PuzzleRunProcedureResult, __sdk::InternalError>,
|
||||
) + Send
|
||||
+ 'static,
|
||||
) {
|
||||
self.imp.invoke_procedure_with_callback::<_, PuzzleRunProcedureResult>(
|
||||
"advance_puzzle_next_level",
|
||||
AdvancePuzzleNextLevelArgs { input, },
|
||||
__callback,
|
||||
);
|
||||
self.imp
|
||||
.invoke_procedure_with_callback::<_, PuzzleRunProcedureResult>(
|
||||
"advance_puzzle_next_level",
|
||||
AdvancePuzzleNextLevelArgs { input },
|
||||
__callback,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,12 +2,7 @@
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
#![allow(unused, clippy::all)]
|
||||
use spacetimedb_sdk::__codegen::{
|
||||
self as __sdk,
|
||||
__lib,
|
||||
__sats,
|
||||
__ws,
|
||||
};
|
||||
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
||||
|
||||
use super::ai_result_reference_kind_type::AiResultReferenceKind;
|
||||
|
||||
@@ -17,12 +12,10 @@ pub struct AiResultReferenceInput {
|
||||
pub task_id: String,
|
||||
pub reference_kind: AiResultReferenceKind,
|
||||
pub reference_id: String,
|
||||
pub label: Option::<String>,
|
||||
pub label: Option<String>,
|
||||
pub created_at_micros: i64,
|
||||
}
|
||||
|
||||
|
||||
impl __sdk::InModule for AiResultReferenceInput {
|
||||
type Module = super::RemoteModule;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,12 +2,7 @@
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
#![allow(unused, clippy::all)]
|
||||
use spacetimedb_sdk::__codegen::{
|
||||
self as __sdk,
|
||||
__lib,
|
||||
__sats,
|
||||
__ws,
|
||||
};
|
||||
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
||||
|
||||
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
|
||||
#[sats(crate = __lib)]
|
||||
@@ -24,12 +19,8 @@ pub enum AiResultReferenceKind {
|
||||
RuntimeItemRecord,
|
||||
|
||||
AssetObject,
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
impl __sdk::InModule for AiResultReferenceKind {
|
||||
type Module = super::RemoteModule;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,12 +2,7 @@
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
#![allow(unused, clippy::all)]
|
||||
use spacetimedb_sdk::__codegen::{
|
||||
self as __sdk,
|
||||
__lib,
|
||||
__sats,
|
||||
__ws,
|
||||
};
|
||||
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
||||
|
||||
use super::ai_result_reference_kind_type::AiResultReferenceKind;
|
||||
|
||||
@@ -18,12 +13,10 @@ pub struct AiResultReferenceSnapshot {
|
||||
pub task_id: String,
|
||||
pub reference_kind: AiResultReferenceKind,
|
||||
pub reference_id: String,
|
||||
pub label: Option::<String>,
|
||||
pub label: Option<String>,
|
||||
pub created_at_micros: i64,
|
||||
}
|
||||
|
||||
|
||||
impl __sdk::InModule for AiResultReferenceSnapshot {
|
||||
type Module = super::RemoteModule;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,14 +2,9 @@
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
#![allow(unused, clippy::all)]
|
||||
use spacetimedb_sdk::__codegen::{
|
||||
self as __sdk,
|
||||
__lib,
|
||||
__sats,
|
||||
__ws,
|
||||
};
|
||||
use super::ai_result_reference_type::AiResultReference;
|
||||
use super::ai_result_reference_kind_type::AiResultReferenceKind;
|
||||
use super::ai_result_reference_type::AiResultReference;
|
||||
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
||||
|
||||
/// Table handle for the table `ai_result_reference`.
|
||||
///
|
||||
@@ -37,7 +32,9 @@ pub trait AiResultReferenceTableAccess {
|
||||
impl AiResultReferenceTableAccess for super::RemoteTables {
|
||||
fn ai_result_reference(&self) -> AiResultReferenceTableHandle<'_> {
|
||||
AiResultReferenceTableHandle {
|
||||
imp: self.imp.get_table::<AiResultReference>("ai_result_reference"),
|
||||
imp: self
|
||||
.imp
|
||||
.get_table::<AiResultReference>("ai_result_reference"),
|
||||
ctx: std::marker::PhantomData,
|
||||
}
|
||||
}
|
||||
@@ -50,8 +47,12 @@ impl<'ctx> __sdk::Table for AiResultReferenceTableHandle<'ctx> {
|
||||
type Row = AiResultReference;
|
||||
type EventContext = super::EventContext;
|
||||
|
||||
fn count(&self) -> u64 { self.imp.count() }
|
||||
fn iter(&self) -> impl Iterator<Item = AiResultReference> + '_ { self.imp.iter() }
|
||||
fn count(&self) -> u64 {
|
||||
self.imp.count()
|
||||
}
|
||||
fn iter(&self) -> impl Iterator<Item = AiResultReference> + '_ {
|
||||
self.imp.iter()
|
||||
}
|
||||
|
||||
type InsertCallbackId = AiResultReferenceInsertCallbackId;
|
||||
|
||||
@@ -97,41 +98,44 @@ impl<'ctx> __sdk::TableWithPrimaryKey for AiResultReferenceTableHandle<'ctx> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Access to the `result_reference_row_id` unique index on the table `ai_result_reference`,
|
||||
/// which allows point queries on the field of the same name
|
||||
/// via the [`AiResultReferenceResultReferenceRowIdUnique::find`] method.
|
||||
///
|
||||
/// Users are encouraged not to explicitly reference this type,
|
||||
/// but to directly chain method calls,
|
||||
/// like `ctx.db.ai_result_reference().result_reference_row_id().find(...)`.
|
||||
pub struct AiResultReferenceResultReferenceRowIdUnique<'ctx> {
|
||||
imp: __sdk::UniqueConstraintHandle<AiResultReference, String>,
|
||||
phantom: std::marker::PhantomData<&'ctx super::RemoteTables>,
|
||||
}
|
||||
/// Access to the `result_reference_row_id` unique index on the table `ai_result_reference`,
|
||||
/// which allows point queries on the field of the same name
|
||||
/// via the [`AiResultReferenceResultReferenceRowIdUnique::find`] method.
|
||||
///
|
||||
/// Users are encouraged not to explicitly reference this type,
|
||||
/// but to directly chain method calls,
|
||||
/// like `ctx.db.ai_result_reference().result_reference_row_id().find(...)`.
|
||||
pub struct AiResultReferenceResultReferenceRowIdUnique<'ctx> {
|
||||
imp: __sdk::UniqueConstraintHandle<AiResultReference, String>,
|
||||
phantom: std::marker::PhantomData<&'ctx super::RemoteTables>,
|
||||
}
|
||||
|
||||
impl<'ctx> AiResultReferenceTableHandle<'ctx> {
|
||||
/// Get a handle on the `result_reference_row_id` unique index on the table `ai_result_reference`.
|
||||
pub fn result_reference_row_id(&self) -> AiResultReferenceResultReferenceRowIdUnique<'ctx> {
|
||||
AiResultReferenceResultReferenceRowIdUnique {
|
||||
imp: self.imp.get_unique_constraint::<String>("result_reference_row_id"),
|
||||
phantom: std::marker::PhantomData,
|
||||
}
|
||||
}
|
||||
impl<'ctx> AiResultReferenceTableHandle<'ctx> {
|
||||
/// Get a handle on the `result_reference_row_id` unique index on the table `ai_result_reference`.
|
||||
pub fn result_reference_row_id(&self) -> AiResultReferenceResultReferenceRowIdUnique<'ctx> {
|
||||
AiResultReferenceResultReferenceRowIdUnique {
|
||||
imp: self
|
||||
.imp
|
||||
.get_unique_constraint::<String>("result_reference_row_id"),
|
||||
phantom: std::marker::PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ctx> AiResultReferenceResultReferenceRowIdUnique<'ctx> {
|
||||
/// Find the subscribed row whose `result_reference_row_id` column value is equal to `col_val`,
|
||||
/// if such a row is present in the client cache.
|
||||
pub fn find(&self, col_val: &String) -> Option<AiResultReference> {
|
||||
self.imp.find(col_val)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ctx> AiResultReferenceResultReferenceRowIdUnique<'ctx> {
|
||||
/// Find the subscribed row whose `result_reference_row_id` column value is equal to `col_val`,
|
||||
/// if such a row is present in the client cache.
|
||||
pub fn find(&self, col_val: &String) -> Option<AiResultReference> {
|
||||
self.imp.find(col_val)
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub(super) fn register_table(client_cache: &mut __sdk::ClientCache<super::RemoteModule>) {
|
||||
|
||||
let _table = client_cache.get_or_make_table::<AiResultReference>("ai_result_reference");
|
||||
_table.add_unique_constraint::<String>("result_reference_row_id", |row| &row.result_reference_row_id);
|
||||
_table.add_unique_constraint::<String>("result_reference_row_id", |row| {
|
||||
&row.result_reference_row_id
|
||||
});
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
@@ -139,26 +143,24 @@ pub(super) fn parse_table_update(
|
||||
raw_updates: __ws::v2::TableUpdate,
|
||||
) -> __sdk::Result<__sdk::TableUpdate<AiResultReference>> {
|
||||
__sdk::TableUpdate::parse_table_update(raw_updates).map_err(|e| {
|
||||
__sdk::InternalError::failed_parse(
|
||||
"TableUpdate<AiResultReference>",
|
||||
"TableUpdate",
|
||||
).with_cause(e).into()
|
||||
__sdk::InternalError::failed_parse("TableUpdate<AiResultReference>", "TableUpdate")
|
||||
.with_cause(e)
|
||||
.into()
|
||||
})
|
||||
}
|
||||
|
||||
#[allow(non_camel_case_types)]
|
||||
/// Extension trait for query builder access to the table `AiResultReference`.
|
||||
///
|
||||
/// Implemented for [`__sdk::QueryTableAccessor`].
|
||||
pub trait ai_result_referenceQueryTableAccess {
|
||||
#[allow(non_snake_case)]
|
||||
/// Get a query builder for the table `AiResultReference`.
|
||||
fn ai_result_reference(&self) -> __sdk::__query_builder::Table<AiResultReference>;
|
||||
}
|
||||
|
||||
impl ai_result_referenceQueryTableAccess for __sdk::QueryTableAccessor {
|
||||
fn ai_result_reference(&self) -> __sdk::__query_builder::Table<AiResultReference> {
|
||||
__sdk::__query_builder::Table::new("ai_result_reference")
|
||||
}
|
||||
}
|
||||
#[allow(non_camel_case_types)]
|
||||
/// Extension trait for query builder access to the table `AiResultReference`.
|
||||
///
|
||||
/// Implemented for [`__sdk::QueryTableAccessor`].
|
||||
pub trait ai_result_referenceQueryTableAccess {
|
||||
#[allow(non_snake_case)]
|
||||
/// Get a query builder for the table `AiResultReference`.
|
||||
fn ai_result_reference(&self) -> __sdk::__query_builder::Table<AiResultReference>;
|
||||
}
|
||||
|
||||
impl ai_result_referenceQueryTableAccess for __sdk::QueryTableAccessor {
|
||||
fn ai_result_reference(&self) -> __sdk::__query_builder::Table<AiResultReference> {
|
||||
__sdk::__query_builder::Table::new("ai_result_reference")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,12 +2,7 @@
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
#![allow(unused, clippy::all)]
|
||||
use spacetimedb_sdk::__codegen::{
|
||||
self as __sdk,
|
||||
__lib,
|
||||
__sats,
|
||||
__ws,
|
||||
};
|
||||
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
||||
|
||||
use super::ai_result_reference_kind_type::AiResultReferenceKind;
|
||||
|
||||
@@ -19,16 +14,14 @@ pub struct AiResultReference {
|
||||
pub task_id: String,
|
||||
pub reference_kind: AiResultReferenceKind,
|
||||
pub reference_id: String,
|
||||
pub label: Option::<String>,
|
||||
pub label: Option<String>,
|
||||
pub created_at: __sdk::Timestamp,
|
||||
}
|
||||
|
||||
|
||||
impl __sdk::InModule for AiResultReference {
|
||||
type Module = super::RemoteModule;
|
||||
}
|
||||
|
||||
|
||||
/// Column accessor struct for the table `AiResultReference`.
|
||||
///
|
||||
/// Provides typed access to columns for query building.
|
||||
@@ -38,7 +31,7 @@ pub struct AiResultReferenceCols {
|
||||
pub task_id: __sdk::__query_builder::Col<AiResultReference, String>,
|
||||
pub reference_kind: __sdk::__query_builder::Col<AiResultReference, AiResultReferenceKind>,
|
||||
pub reference_id: __sdk::__query_builder::Col<AiResultReference, String>,
|
||||
pub label: __sdk::__query_builder::Col<AiResultReference, Option::<String>>,
|
||||
pub label: __sdk::__query_builder::Col<AiResultReference, Option<String>>,
|
||||
pub created_at: __sdk::__query_builder::Col<AiResultReference, __sdk::Timestamp>,
|
||||
}
|
||||
|
||||
@@ -46,14 +39,16 @@ impl __sdk::__query_builder::HasCols for AiResultReference {
|
||||
type Cols = AiResultReferenceCols;
|
||||
fn cols(table_name: &'static str) -> Self::Cols {
|
||||
AiResultReferenceCols {
|
||||
result_reference_row_id: __sdk::__query_builder::Col::new(table_name, "result_reference_row_id"),
|
||||
result_reference_row_id: __sdk::__query_builder::Col::new(
|
||||
table_name,
|
||||
"result_reference_row_id",
|
||||
),
|
||||
result_ref_id: __sdk::__query_builder::Col::new(table_name, "result_ref_id"),
|
||||
task_id: __sdk::__query_builder::Col::new(table_name, "task_id"),
|
||||
reference_kind: __sdk::__query_builder::Col::new(table_name, "reference_kind"),
|
||||
reference_id: __sdk::__query_builder::Col::new(table_name, "reference_id"),
|
||||
label: __sdk::__query_builder::Col::new(table_name, "label"),
|
||||
created_at: __sdk::__query_builder::Col::new(table_name, "created_at"),
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -70,12 +65,13 @@ impl __sdk::__query_builder::HasIxCols for AiResultReference {
|
||||
type IxCols = AiResultReferenceIxCols;
|
||||
fn ix_cols(table_name: &'static str) -> Self::IxCols {
|
||||
AiResultReferenceIxCols {
|
||||
result_reference_row_id: __sdk::__query_builder::IxCol::new(table_name, "result_reference_row_id"),
|
||||
result_reference_row_id: __sdk::__query_builder::IxCol::new(
|
||||
table_name,
|
||||
"result_reference_row_id",
|
||||
),
|
||||
task_id: __sdk::__query_builder::IxCol::new(table_name, "task_id"),
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl __sdk::__query_builder::CanBeLookupTable for AiResultReference {}
|
||||
|
||||
|
||||
@@ -2,12 +2,7 @@
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
#![allow(unused, clippy::all)]
|
||||
use spacetimedb_sdk::__codegen::{
|
||||
self as __sdk,
|
||||
__lib,
|
||||
__sats,
|
||||
__ws,
|
||||
};
|
||||
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
||||
|
||||
use super::ai_task_stage_kind_type::AiTaskStageKind;
|
||||
|
||||
@@ -16,14 +11,12 @@ use super::ai_task_stage_kind_type::AiTaskStageKind;
|
||||
pub struct AiStageCompletionInput {
|
||||
pub task_id: String,
|
||||
pub stage_kind: AiTaskStageKind,
|
||||
pub text_output: Option::<String>,
|
||||
pub structured_payload_json: Option::<String>,
|
||||
pub warning_messages: Vec::<String>,
|
||||
pub text_output: Option<String>,
|
||||
pub structured_payload_json: Option<String>,
|
||||
pub warning_messages: Vec<String>,
|
||||
pub completed_at_micros: i64,
|
||||
}
|
||||
|
||||
|
||||
impl __sdk::InModule for AiStageCompletionInput {
|
||||
type Module = super::RemoteModule;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,13 +2,7 @@
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
#![allow(unused, clippy::all)]
|
||||
use spacetimedb_sdk::__codegen::{
|
||||
self as __sdk,
|
||||
__lib,
|
||||
__sats,
|
||||
__ws,
|
||||
};
|
||||
|
||||
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
||||
|
||||
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
|
||||
#[sats(crate = __lib)]
|
||||
@@ -17,8 +11,6 @@ pub struct AiTaskCancelInput {
|
||||
pub completed_at_micros: i64,
|
||||
}
|
||||
|
||||
|
||||
impl __sdk::InModule for AiTaskCancelInput {
|
||||
type Module = super::RemoteModule;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,12 +2,7 @@
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
#![allow(unused, clippy::all)]
|
||||
use spacetimedb_sdk::__codegen::{
|
||||
self as __sdk,
|
||||
__lib,
|
||||
__sats,
|
||||
__ws,
|
||||
};
|
||||
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
||||
|
||||
use super::ai_task_kind_type::AiTaskKind;
|
||||
use super::ai_task_stage_blueprint_type::AiTaskStageBlueprint;
|
||||
@@ -20,14 +15,12 @@ pub struct AiTaskCreateInput {
|
||||
pub owner_user_id: String,
|
||||
pub request_label: String,
|
||||
pub source_module: String,
|
||||
pub source_entity_id: Option::<String>,
|
||||
pub request_payload_json: Option::<String>,
|
||||
pub stages: Vec::<AiTaskStageBlueprint>,
|
||||
pub source_entity_id: Option<String>,
|
||||
pub request_payload_json: Option<String>,
|
||||
pub stages: Vec<AiTaskStageBlueprint>,
|
||||
pub created_at_micros: i64,
|
||||
}
|
||||
|
||||
|
||||
impl __sdk::InModule for AiTaskCreateInput {
|
||||
type Module = super::RemoteModule;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,13 +2,7 @@
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
#![allow(unused, clippy::all)]
|
||||
use spacetimedb_sdk::__codegen::{
|
||||
self as __sdk,
|
||||
__lib,
|
||||
__sats,
|
||||
__ws,
|
||||
};
|
||||
|
||||
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
||||
|
||||
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
|
||||
#[sats(crate = __lib)]
|
||||
@@ -18,8 +12,6 @@ pub struct AiTaskFailureInput {
|
||||
pub completed_at_micros: i64,
|
||||
}
|
||||
|
||||
|
||||
impl __sdk::InModule for AiTaskFailureInput {
|
||||
type Module = super::RemoteModule;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,13 +2,7 @@
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
#![allow(unused, clippy::all)]
|
||||
use spacetimedb_sdk::__codegen::{
|
||||
self as __sdk,
|
||||
__lib,
|
||||
__sats,
|
||||
__ws,
|
||||
};
|
||||
|
||||
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
||||
|
||||
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
|
||||
#[sats(crate = __lib)]
|
||||
@@ -17,8 +11,6 @@ pub struct AiTaskFinishInput {
|
||||
pub completed_at_micros: i64,
|
||||
}
|
||||
|
||||
|
||||
impl __sdk::InModule for AiTaskFinishInput {
|
||||
type Module = super::RemoteModule;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,12 +2,7 @@
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
#![allow(unused, clippy::all)]
|
||||
use spacetimedb_sdk::__codegen::{
|
||||
self as __sdk,
|
||||
__lib,
|
||||
__sats,
|
||||
__ws,
|
||||
};
|
||||
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
||||
|
||||
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
|
||||
#[sats(crate = __lib)]
|
||||
@@ -24,12 +19,8 @@ pub enum AiTaskKind {
|
||||
QuestIntent,
|
||||
|
||||
RuntimeItemIntent,
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
impl __sdk::InModule for AiTaskKind {
|
||||
type Module = super::RemoteModule;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,12 +2,7 @@
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
#![allow(unused, clippy::all)]
|
||||
use spacetimedb_sdk::__codegen::{
|
||||
self as __sdk,
|
||||
__lib,
|
||||
__sats,
|
||||
__ws,
|
||||
};
|
||||
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
||||
|
||||
use super::ai_task_snapshot_type::AiTaskSnapshot;
|
||||
use super::ai_text_chunk_snapshot_type::AiTextChunkSnapshot;
|
||||
@@ -16,13 +11,11 @@ use super::ai_text_chunk_snapshot_type::AiTextChunkSnapshot;
|
||||
#[sats(crate = __lib)]
|
||||
pub struct AiTaskProcedureResult {
|
||||
pub ok: bool,
|
||||
pub task: Option::<AiTaskSnapshot>,
|
||||
pub text_chunk: Option::<AiTextChunkSnapshot>,
|
||||
pub error_message: Option::<String>,
|
||||
pub task: Option<AiTaskSnapshot>,
|
||||
pub text_chunk: Option<AiTextChunkSnapshot>,
|
||||
pub error_message: Option<String>,
|
||||
}
|
||||
|
||||
|
||||
impl __sdk::InModule for AiTaskProcedureResult {
|
||||
type Module = super::RemoteModule;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,17 +2,12 @@
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
#![allow(unused, clippy::all)]
|
||||
use spacetimedb_sdk::__codegen::{
|
||||
self as __sdk,
|
||||
__lib,
|
||||
__sats,
|
||||
__ws,
|
||||
};
|
||||
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
||||
|
||||
use super::ai_task_kind_type::AiTaskKind;
|
||||
use super::ai_task_status_type::AiTaskStatus;
|
||||
use super::ai_task_stage_snapshot_type::AiTaskStageSnapshot;
|
||||
use super::ai_result_reference_snapshot_type::AiResultReferenceSnapshot;
|
||||
use super::ai_task_kind_type::AiTaskKind;
|
||||
use super::ai_task_stage_snapshot_type::AiTaskStageSnapshot;
|
||||
use super::ai_task_status_type::AiTaskStatus;
|
||||
|
||||
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
|
||||
#[sats(crate = __lib)]
|
||||
@@ -22,23 +17,21 @@ pub struct AiTaskSnapshot {
|
||||
pub owner_user_id: String,
|
||||
pub request_label: String,
|
||||
pub source_module: String,
|
||||
pub source_entity_id: Option::<String>,
|
||||
pub request_payload_json: Option::<String>,
|
||||
pub source_entity_id: Option<String>,
|
||||
pub request_payload_json: Option<String>,
|
||||
pub status: AiTaskStatus,
|
||||
pub failure_message: Option::<String>,
|
||||
pub stages: Vec::<AiTaskStageSnapshot>,
|
||||
pub result_references: Vec::<AiResultReferenceSnapshot>,
|
||||
pub latest_text_output: Option::<String>,
|
||||
pub latest_structured_payload_json: Option::<String>,
|
||||
pub failure_message: Option<String>,
|
||||
pub stages: Vec<AiTaskStageSnapshot>,
|
||||
pub result_references: Vec<AiResultReferenceSnapshot>,
|
||||
pub latest_text_output: Option<String>,
|
||||
pub latest_structured_payload_json: Option<String>,
|
||||
pub version: u32,
|
||||
pub created_at_micros: i64,
|
||||
pub started_at_micros: Option::<i64>,
|
||||
pub completed_at_micros: Option::<i64>,
|
||||
pub started_at_micros: Option<i64>,
|
||||
pub completed_at_micros: Option<i64>,
|
||||
pub updated_at_micros: i64,
|
||||
}
|
||||
|
||||
|
||||
impl __sdk::InModule for AiTaskSnapshot {
|
||||
type Module = super::RemoteModule;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,12 +2,7 @@
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
#![allow(unused, clippy::all)]
|
||||
use spacetimedb_sdk::__codegen::{
|
||||
self as __sdk,
|
||||
__lib,
|
||||
__sats,
|
||||
__ws,
|
||||
};
|
||||
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
||||
|
||||
use super::ai_task_stage_kind_type::AiTaskStageKind;
|
||||
|
||||
@@ -20,8 +15,6 @@ pub struct AiTaskStageBlueprint {
|
||||
pub order: u32,
|
||||
}
|
||||
|
||||
|
||||
impl __sdk::InModule for AiTaskStageBlueprint {
|
||||
type Module = super::RemoteModule;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,12 +2,7 @@
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
#![allow(unused, clippy::all)]
|
||||
use spacetimedb_sdk::__codegen::{
|
||||
self as __sdk,
|
||||
__lib,
|
||||
__sats,
|
||||
__ws,
|
||||
};
|
||||
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
||||
|
||||
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
|
||||
#[sats(crate = __lib)]
|
||||
@@ -22,12 +17,8 @@ pub enum AiTaskStageKind {
|
||||
NormalizeResult,
|
||||
|
||||
PersistResult,
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
impl __sdk::InModule for AiTaskStageKind {
|
||||
type Module = super::RemoteModule;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,12 +2,7 @@
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
#![allow(unused, clippy::all)]
|
||||
use spacetimedb_sdk::__codegen::{
|
||||
self as __sdk,
|
||||
__lib,
|
||||
__sats,
|
||||
__ws,
|
||||
};
|
||||
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
||||
|
||||
use super::ai_task_stage_kind_type::AiTaskStageKind;
|
||||
use super::ai_task_stage_status_type::AiTaskStageStatus;
|
||||
@@ -20,15 +15,13 @@ pub struct AiTaskStageSnapshot {
|
||||
pub detail: String,
|
||||
pub order: u32,
|
||||
pub status: AiTaskStageStatus,
|
||||
pub text_output: Option::<String>,
|
||||
pub structured_payload_json: Option::<String>,
|
||||
pub warning_messages: Vec::<String>,
|
||||
pub started_at_micros: Option::<i64>,
|
||||
pub completed_at_micros: Option::<i64>,
|
||||
pub text_output: Option<String>,
|
||||
pub structured_payload_json: Option<String>,
|
||||
pub warning_messages: Vec<String>,
|
||||
pub started_at_micros: Option<i64>,
|
||||
pub completed_at_micros: Option<i64>,
|
||||
}
|
||||
|
||||
|
||||
impl __sdk::InModule for AiTaskStageSnapshot {
|
||||
type Module = super::RemoteModule;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,12 +2,7 @@
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
#![allow(unused, clippy::all)]
|
||||
use spacetimedb_sdk::__codegen::{
|
||||
self as __sdk,
|
||||
__lib,
|
||||
__sats,
|
||||
__ws,
|
||||
};
|
||||
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
||||
|
||||
use super::ai_task_stage_kind_type::AiTaskStageKind;
|
||||
|
||||
@@ -19,8 +14,6 @@ pub struct AiTaskStageStartInput {
|
||||
pub started_at_micros: i64,
|
||||
}
|
||||
|
||||
|
||||
impl __sdk::InModule for AiTaskStageStartInput {
|
||||
type Module = super::RemoteModule;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,12 +2,7 @@
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
#![allow(unused, clippy::all)]
|
||||
use spacetimedb_sdk::__codegen::{
|
||||
self as __sdk,
|
||||
__lib,
|
||||
__sats,
|
||||
__ws,
|
||||
};
|
||||
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
||||
|
||||
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
|
||||
#[sats(crate = __lib)]
|
||||
@@ -20,12 +15,8 @@ pub enum AiTaskStageStatus {
|
||||
Completed,
|
||||
|
||||
Skipped,
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
impl __sdk::InModule for AiTaskStageStatus {
|
||||
type Module = super::RemoteModule;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,15 +2,10 @@
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
#![allow(unused, clippy::all)]
|
||||
use spacetimedb_sdk::__codegen::{
|
||||
self as __sdk,
|
||||
__lib,
|
||||
__sats,
|
||||
__ws,
|
||||
};
|
||||
use super::ai_task_stage_type::AiTaskStage;
|
||||
use super::ai_task_stage_kind_type::AiTaskStageKind;
|
||||
use super::ai_task_stage_status_type::AiTaskStageStatus;
|
||||
use super::ai_task_stage_type::AiTaskStage;
|
||||
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
||||
|
||||
/// Table handle for the table `ai_task_stage`.
|
||||
///
|
||||
@@ -51,8 +46,12 @@ impl<'ctx> __sdk::Table for AiTaskStageTableHandle<'ctx> {
|
||||
type Row = AiTaskStage;
|
||||
type EventContext = super::EventContext;
|
||||
|
||||
fn count(&self) -> u64 { self.imp.count() }
|
||||
fn iter(&self) -> impl Iterator<Item = AiTaskStage> + '_ { self.imp.iter() }
|
||||
fn count(&self) -> u64 {
|
||||
self.imp.count()
|
||||
}
|
||||
fn iter(&self) -> impl Iterator<Item = AiTaskStage> + '_ {
|
||||
self.imp.iter()
|
||||
}
|
||||
|
||||
type InsertCallbackId = AiTaskStageInsertCallbackId;
|
||||
|
||||
@@ -98,39 +97,38 @@ impl<'ctx> __sdk::TableWithPrimaryKey for AiTaskStageTableHandle<'ctx> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Access to the `task_stage_id` unique index on the table `ai_task_stage`,
|
||||
/// which allows point queries on the field of the same name
|
||||
/// via the [`AiTaskStageTaskStageIdUnique::find`] method.
|
||||
///
|
||||
/// Users are encouraged not to explicitly reference this type,
|
||||
/// but to directly chain method calls,
|
||||
/// like `ctx.db.ai_task_stage().task_stage_id().find(...)`.
|
||||
pub struct AiTaskStageTaskStageIdUnique<'ctx> {
|
||||
imp: __sdk::UniqueConstraintHandle<AiTaskStage, String>,
|
||||
phantom: std::marker::PhantomData<&'ctx super::RemoteTables>,
|
||||
}
|
||||
/// Access to the `task_stage_id` unique index on the table `ai_task_stage`,
|
||||
/// which allows point queries on the field of the same name
|
||||
/// via the [`AiTaskStageTaskStageIdUnique::find`] method.
|
||||
///
|
||||
/// Users are encouraged not to explicitly reference this type,
|
||||
/// but to directly chain method calls,
|
||||
/// like `ctx.db.ai_task_stage().task_stage_id().find(...)`.
|
||||
pub struct AiTaskStageTaskStageIdUnique<'ctx> {
|
||||
imp: __sdk::UniqueConstraintHandle<AiTaskStage, String>,
|
||||
phantom: std::marker::PhantomData<&'ctx super::RemoteTables>,
|
||||
}
|
||||
|
||||
impl<'ctx> AiTaskStageTableHandle<'ctx> {
|
||||
/// Get a handle on the `task_stage_id` unique index on the table `ai_task_stage`.
|
||||
pub fn task_stage_id(&self) -> AiTaskStageTaskStageIdUnique<'ctx> {
|
||||
AiTaskStageTaskStageIdUnique {
|
||||
imp: self.imp.get_unique_constraint::<String>("task_stage_id"),
|
||||
phantom: std::marker::PhantomData,
|
||||
}
|
||||
}
|
||||
impl<'ctx> AiTaskStageTableHandle<'ctx> {
|
||||
/// Get a handle on the `task_stage_id` unique index on the table `ai_task_stage`.
|
||||
pub fn task_stage_id(&self) -> AiTaskStageTaskStageIdUnique<'ctx> {
|
||||
AiTaskStageTaskStageIdUnique {
|
||||
imp: self.imp.get_unique_constraint::<String>("task_stage_id"),
|
||||
phantom: std::marker::PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ctx> AiTaskStageTaskStageIdUnique<'ctx> {
|
||||
/// Find the subscribed row whose `task_stage_id` column value is equal to `col_val`,
|
||||
/// if such a row is present in the client cache.
|
||||
pub fn find(&self, col_val: &String) -> Option<AiTaskStage> {
|
||||
self.imp.find(col_val)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ctx> AiTaskStageTaskStageIdUnique<'ctx> {
|
||||
/// Find the subscribed row whose `task_stage_id` column value is equal to `col_val`,
|
||||
/// if such a row is present in the client cache.
|
||||
pub fn find(&self, col_val: &String) -> Option<AiTaskStage> {
|
||||
self.imp.find(col_val)
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub(super) fn register_table(client_cache: &mut __sdk::ClientCache<super::RemoteModule>) {
|
||||
|
||||
let _table = client_cache.get_or_make_table::<AiTaskStage>("ai_task_stage");
|
||||
_table.add_unique_constraint::<String>("task_stage_id", |row| &row.task_stage_id);
|
||||
}
|
||||
@@ -140,26 +138,24 @@ pub(super) fn parse_table_update(
|
||||
raw_updates: __ws::v2::TableUpdate,
|
||||
) -> __sdk::Result<__sdk::TableUpdate<AiTaskStage>> {
|
||||
__sdk::TableUpdate::parse_table_update(raw_updates).map_err(|e| {
|
||||
__sdk::InternalError::failed_parse(
|
||||
"TableUpdate<AiTaskStage>",
|
||||
"TableUpdate",
|
||||
).with_cause(e).into()
|
||||
__sdk::InternalError::failed_parse("TableUpdate<AiTaskStage>", "TableUpdate")
|
||||
.with_cause(e)
|
||||
.into()
|
||||
})
|
||||
}
|
||||
|
||||
#[allow(non_camel_case_types)]
|
||||
/// Extension trait for query builder access to the table `AiTaskStage`.
|
||||
///
|
||||
/// Implemented for [`__sdk::QueryTableAccessor`].
|
||||
pub trait ai_task_stageQueryTableAccess {
|
||||
#[allow(non_snake_case)]
|
||||
/// Get a query builder for the table `AiTaskStage`.
|
||||
fn ai_task_stage(&self) -> __sdk::__query_builder::Table<AiTaskStage>;
|
||||
}
|
||||
|
||||
impl ai_task_stageQueryTableAccess for __sdk::QueryTableAccessor {
|
||||
fn ai_task_stage(&self) -> __sdk::__query_builder::Table<AiTaskStage> {
|
||||
__sdk::__query_builder::Table::new("ai_task_stage")
|
||||
}
|
||||
}
|
||||
#[allow(non_camel_case_types)]
|
||||
/// Extension trait for query builder access to the table `AiTaskStage`.
|
||||
///
|
||||
/// Implemented for [`__sdk::QueryTableAccessor`].
|
||||
pub trait ai_task_stageQueryTableAccess {
|
||||
#[allow(non_snake_case)]
|
||||
/// Get a query builder for the table `AiTaskStage`.
|
||||
fn ai_task_stage(&self) -> __sdk::__query_builder::Table<AiTaskStage>;
|
||||
}
|
||||
|
||||
impl ai_task_stageQueryTableAccess for __sdk::QueryTableAccessor {
|
||||
fn ai_task_stage(&self) -> __sdk::__query_builder::Table<AiTaskStage> {
|
||||
__sdk::__query_builder::Table::new("ai_task_stage")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,12 +2,7 @@
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
#![allow(unused, clippy::all)]
|
||||
use spacetimedb_sdk::__codegen::{
|
||||
self as __sdk,
|
||||
__lib,
|
||||
__sats,
|
||||
__ws,
|
||||
};
|
||||
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
||||
|
||||
use super::ai_task_stage_kind_type::AiTaskStageKind;
|
||||
use super::ai_task_stage_status_type::AiTaskStageStatus;
|
||||
@@ -22,19 +17,17 @@ pub struct AiTaskStage {
|
||||
pub detail: String,
|
||||
pub stage_order: u32,
|
||||
pub status: AiTaskStageStatus,
|
||||
pub text_output: Option::<String>,
|
||||
pub structured_payload_json: Option::<String>,
|
||||
pub warning_messages: Vec::<String>,
|
||||
pub started_at: Option::<__sdk::Timestamp>,
|
||||
pub completed_at: Option::<__sdk::Timestamp>,
|
||||
pub text_output: Option<String>,
|
||||
pub structured_payload_json: Option<String>,
|
||||
pub warning_messages: Vec<String>,
|
||||
pub started_at: Option<__sdk::Timestamp>,
|
||||
pub completed_at: Option<__sdk::Timestamp>,
|
||||
}
|
||||
|
||||
|
||||
impl __sdk::InModule for AiTaskStage {
|
||||
type Module = super::RemoteModule;
|
||||
}
|
||||
|
||||
|
||||
/// Column accessor struct for the table `AiTaskStage`.
|
||||
///
|
||||
/// Provides typed access to columns for query building.
|
||||
@@ -46,11 +39,11 @@ pub struct AiTaskStageCols {
|
||||
pub detail: __sdk::__query_builder::Col<AiTaskStage, String>,
|
||||
pub stage_order: __sdk::__query_builder::Col<AiTaskStage, u32>,
|
||||
pub status: __sdk::__query_builder::Col<AiTaskStage, AiTaskStageStatus>,
|
||||
pub text_output: __sdk::__query_builder::Col<AiTaskStage, Option::<String>>,
|
||||
pub structured_payload_json: __sdk::__query_builder::Col<AiTaskStage, Option::<String>>,
|
||||
pub warning_messages: __sdk::__query_builder::Col<AiTaskStage, Vec::<String>>,
|
||||
pub started_at: __sdk::__query_builder::Col<AiTaskStage, Option::<__sdk::Timestamp>>,
|
||||
pub completed_at: __sdk::__query_builder::Col<AiTaskStage, Option::<__sdk::Timestamp>>,
|
||||
pub text_output: __sdk::__query_builder::Col<AiTaskStage, Option<String>>,
|
||||
pub structured_payload_json: __sdk::__query_builder::Col<AiTaskStage, Option<String>>,
|
||||
pub warning_messages: __sdk::__query_builder::Col<AiTaskStage, Vec<String>>,
|
||||
pub started_at: __sdk::__query_builder::Col<AiTaskStage, Option<__sdk::Timestamp>>,
|
||||
pub completed_at: __sdk::__query_builder::Col<AiTaskStage, Option<__sdk::Timestamp>>,
|
||||
}
|
||||
|
||||
impl __sdk::__query_builder::HasCols for AiTaskStage {
|
||||
@@ -65,11 +58,13 @@ impl __sdk::__query_builder::HasCols for AiTaskStage {
|
||||
stage_order: __sdk::__query_builder::Col::new(table_name, "stage_order"),
|
||||
status: __sdk::__query_builder::Col::new(table_name, "status"),
|
||||
text_output: __sdk::__query_builder::Col::new(table_name, "text_output"),
|
||||
structured_payload_json: __sdk::__query_builder::Col::new(table_name, "structured_payload_json"),
|
||||
structured_payload_json: __sdk::__query_builder::Col::new(
|
||||
table_name,
|
||||
"structured_payload_json",
|
||||
),
|
||||
warning_messages: __sdk::__query_builder::Col::new(table_name, "warning_messages"),
|
||||
started_at: __sdk::__query_builder::Col::new(table_name, "started_at"),
|
||||
completed_at: __sdk::__query_builder::Col::new(table_name, "completed_at"),
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -88,10 +83,8 @@ impl __sdk::__query_builder::HasIxCols for AiTaskStage {
|
||||
AiTaskStageIxCols {
|
||||
task_id: __sdk::__query_builder::IxCol::new(table_name, "task_id"),
|
||||
task_stage_id: __sdk::__query_builder::IxCol::new(table_name, "task_stage_id"),
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl __sdk::__query_builder::CanBeLookupTable for AiTaskStage {}
|
||||
|
||||
|
||||
@@ -2,13 +2,7 @@
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
#![allow(unused, clippy::all)]
|
||||
use spacetimedb_sdk::__codegen::{
|
||||
self as __sdk,
|
||||
__lib,
|
||||
__sats,
|
||||
__ws,
|
||||
};
|
||||
|
||||
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
||||
|
||||
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
|
||||
#[sats(crate = __lib)]
|
||||
@@ -17,8 +11,6 @@ pub struct AiTaskStartInput {
|
||||
pub started_at_micros: i64,
|
||||
}
|
||||
|
||||
|
||||
impl __sdk::InModule for AiTaskStartInput {
|
||||
type Module = super::RemoteModule;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,12 +2,7 @@
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
#![allow(unused, clippy::all)]
|
||||
use spacetimedb_sdk::__codegen::{
|
||||
self as __sdk,
|
||||
__lib,
|
||||
__sats,
|
||||
__ws,
|
||||
};
|
||||
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
||||
|
||||
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
|
||||
#[sats(crate = __lib)]
|
||||
@@ -22,12 +17,8 @@ pub enum AiTaskStatus {
|
||||
Failed,
|
||||
|
||||
Cancelled,
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
impl __sdk::InModule for AiTaskStatus {
|
||||
type Module = super::RemoteModule;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,15 +2,10 @@
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
#![allow(unused, clippy::all)]
|
||||
use spacetimedb_sdk::__codegen::{
|
||||
self as __sdk,
|
||||
__lib,
|
||||
__sats,
|
||||
__ws,
|
||||
};
|
||||
use super::ai_task_type::AiTask;
|
||||
use super::ai_task_kind_type::AiTaskKind;
|
||||
use super::ai_task_status_type::AiTaskStatus;
|
||||
use super::ai_task_type::AiTask;
|
||||
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
||||
|
||||
/// Table handle for the table `ai_task`.
|
||||
///
|
||||
@@ -51,8 +46,12 @@ impl<'ctx> __sdk::Table for AiTaskTableHandle<'ctx> {
|
||||
type Row = AiTask;
|
||||
type EventContext = super::EventContext;
|
||||
|
||||
fn count(&self) -> u64 { self.imp.count() }
|
||||
fn iter(&self) -> impl Iterator<Item = AiTask> + '_ { self.imp.iter() }
|
||||
fn count(&self) -> u64 {
|
||||
self.imp.count()
|
||||
}
|
||||
fn iter(&self) -> impl Iterator<Item = AiTask> + '_ {
|
||||
self.imp.iter()
|
||||
}
|
||||
|
||||
type InsertCallbackId = AiTaskInsertCallbackId;
|
||||
|
||||
@@ -98,39 +97,38 @@ impl<'ctx> __sdk::TableWithPrimaryKey for AiTaskTableHandle<'ctx> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Access to the `task_id` unique index on the table `ai_task`,
|
||||
/// which allows point queries on the field of the same name
|
||||
/// via the [`AiTaskTaskIdUnique::find`] method.
|
||||
///
|
||||
/// Users are encouraged not to explicitly reference this type,
|
||||
/// but to directly chain method calls,
|
||||
/// like `ctx.db.ai_task().task_id().find(...)`.
|
||||
pub struct AiTaskTaskIdUnique<'ctx> {
|
||||
imp: __sdk::UniqueConstraintHandle<AiTask, String>,
|
||||
phantom: std::marker::PhantomData<&'ctx super::RemoteTables>,
|
||||
}
|
||||
/// Access to the `task_id` unique index on the table `ai_task`,
|
||||
/// which allows point queries on the field of the same name
|
||||
/// via the [`AiTaskTaskIdUnique::find`] method.
|
||||
///
|
||||
/// Users are encouraged not to explicitly reference this type,
|
||||
/// but to directly chain method calls,
|
||||
/// like `ctx.db.ai_task().task_id().find(...)`.
|
||||
pub struct AiTaskTaskIdUnique<'ctx> {
|
||||
imp: __sdk::UniqueConstraintHandle<AiTask, String>,
|
||||
phantom: std::marker::PhantomData<&'ctx super::RemoteTables>,
|
||||
}
|
||||
|
||||
impl<'ctx> AiTaskTableHandle<'ctx> {
|
||||
/// Get a handle on the `task_id` unique index on the table `ai_task`.
|
||||
pub fn task_id(&self) -> AiTaskTaskIdUnique<'ctx> {
|
||||
AiTaskTaskIdUnique {
|
||||
imp: self.imp.get_unique_constraint::<String>("task_id"),
|
||||
phantom: std::marker::PhantomData,
|
||||
}
|
||||
}
|
||||
impl<'ctx> AiTaskTableHandle<'ctx> {
|
||||
/// Get a handle on the `task_id` unique index on the table `ai_task`.
|
||||
pub fn task_id(&self) -> AiTaskTaskIdUnique<'ctx> {
|
||||
AiTaskTaskIdUnique {
|
||||
imp: self.imp.get_unique_constraint::<String>("task_id"),
|
||||
phantom: std::marker::PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ctx> AiTaskTaskIdUnique<'ctx> {
|
||||
/// Find the subscribed row whose `task_id` column value is equal to `col_val`,
|
||||
/// if such a row is present in the client cache.
|
||||
pub fn find(&self, col_val: &String) -> Option<AiTask> {
|
||||
self.imp.find(col_val)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ctx> AiTaskTaskIdUnique<'ctx> {
|
||||
/// Find the subscribed row whose `task_id` column value is equal to `col_val`,
|
||||
/// if such a row is present in the client cache.
|
||||
pub fn find(&self, col_val: &String) -> Option<AiTask> {
|
||||
self.imp.find(col_val)
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub(super) fn register_table(client_cache: &mut __sdk::ClientCache<super::RemoteModule>) {
|
||||
|
||||
let _table = client_cache.get_or_make_table::<AiTask>("ai_task");
|
||||
_table.add_unique_constraint::<String>("task_id", |row| &row.task_id);
|
||||
}
|
||||
@@ -140,26 +138,24 @@ pub(super) fn parse_table_update(
|
||||
raw_updates: __ws::v2::TableUpdate,
|
||||
) -> __sdk::Result<__sdk::TableUpdate<AiTask>> {
|
||||
__sdk::TableUpdate::parse_table_update(raw_updates).map_err(|e| {
|
||||
__sdk::InternalError::failed_parse(
|
||||
"TableUpdate<AiTask>",
|
||||
"TableUpdate",
|
||||
).with_cause(e).into()
|
||||
__sdk::InternalError::failed_parse("TableUpdate<AiTask>", "TableUpdate")
|
||||
.with_cause(e)
|
||||
.into()
|
||||
})
|
||||
}
|
||||
|
||||
#[allow(non_camel_case_types)]
|
||||
/// Extension trait for query builder access to the table `AiTask`.
|
||||
///
|
||||
/// Implemented for [`__sdk::QueryTableAccessor`].
|
||||
pub trait ai_taskQueryTableAccess {
|
||||
#[allow(non_snake_case)]
|
||||
/// Get a query builder for the table `AiTask`.
|
||||
fn ai_task(&self) -> __sdk::__query_builder::Table<AiTask>;
|
||||
}
|
||||
|
||||
impl ai_taskQueryTableAccess for __sdk::QueryTableAccessor {
|
||||
fn ai_task(&self) -> __sdk::__query_builder::Table<AiTask> {
|
||||
__sdk::__query_builder::Table::new("ai_task")
|
||||
}
|
||||
}
|
||||
#[allow(non_camel_case_types)]
|
||||
/// Extension trait for query builder access to the table `AiTask`.
|
||||
///
|
||||
/// Implemented for [`__sdk::QueryTableAccessor`].
|
||||
pub trait ai_taskQueryTableAccess {
|
||||
#[allow(non_snake_case)]
|
||||
/// Get a query builder for the table `AiTask`.
|
||||
fn ai_task(&self) -> __sdk::__query_builder::Table<AiTask>;
|
||||
}
|
||||
|
||||
impl ai_taskQueryTableAccess for __sdk::QueryTableAccessor {
|
||||
fn ai_task(&self) -> __sdk::__query_builder::Table<AiTask> {
|
||||
__sdk::__query_builder::Table::new("ai_task")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,12 +2,7 @@
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
#![allow(unused, clippy::all)]
|
||||
use spacetimedb_sdk::__codegen::{
|
||||
self as __sdk,
|
||||
__lib,
|
||||
__sats,
|
||||
__ws,
|
||||
};
|
||||
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
||||
|
||||
use super::ai_task_kind_type::AiTaskKind;
|
||||
use super::ai_task_status_type::AiTaskStatus;
|
||||
@@ -20,25 +15,23 @@ pub struct AiTask {
|
||||
pub owner_user_id: String,
|
||||
pub request_label: String,
|
||||
pub source_module: String,
|
||||
pub source_entity_id: Option::<String>,
|
||||
pub request_payload_json: Option::<String>,
|
||||
pub source_entity_id: Option<String>,
|
||||
pub request_payload_json: Option<String>,
|
||||
pub status: AiTaskStatus,
|
||||
pub failure_message: Option::<String>,
|
||||
pub latest_text_output: Option::<String>,
|
||||
pub latest_structured_payload_json: Option::<String>,
|
||||
pub failure_message: Option<String>,
|
||||
pub latest_text_output: Option<String>,
|
||||
pub latest_structured_payload_json: Option<String>,
|
||||
pub version: u32,
|
||||
pub created_at: __sdk::Timestamp,
|
||||
pub started_at: Option::<__sdk::Timestamp>,
|
||||
pub completed_at: Option::<__sdk::Timestamp>,
|
||||
pub started_at: Option<__sdk::Timestamp>,
|
||||
pub completed_at: Option<__sdk::Timestamp>,
|
||||
pub updated_at: __sdk::Timestamp,
|
||||
}
|
||||
|
||||
|
||||
impl __sdk::InModule for AiTask {
|
||||
type Module = super::RemoteModule;
|
||||
}
|
||||
|
||||
|
||||
/// Column accessor struct for the table `AiTask`.
|
||||
///
|
||||
/// Provides typed access to columns for query building.
|
||||
@@ -48,16 +41,16 @@ pub struct AiTaskCols {
|
||||
pub owner_user_id: __sdk::__query_builder::Col<AiTask, String>,
|
||||
pub request_label: __sdk::__query_builder::Col<AiTask, String>,
|
||||
pub source_module: __sdk::__query_builder::Col<AiTask, String>,
|
||||
pub source_entity_id: __sdk::__query_builder::Col<AiTask, Option::<String>>,
|
||||
pub request_payload_json: __sdk::__query_builder::Col<AiTask, Option::<String>>,
|
||||
pub source_entity_id: __sdk::__query_builder::Col<AiTask, Option<String>>,
|
||||
pub request_payload_json: __sdk::__query_builder::Col<AiTask, Option<String>>,
|
||||
pub status: __sdk::__query_builder::Col<AiTask, AiTaskStatus>,
|
||||
pub failure_message: __sdk::__query_builder::Col<AiTask, Option::<String>>,
|
||||
pub latest_text_output: __sdk::__query_builder::Col<AiTask, Option::<String>>,
|
||||
pub latest_structured_payload_json: __sdk::__query_builder::Col<AiTask, Option::<String>>,
|
||||
pub failure_message: __sdk::__query_builder::Col<AiTask, Option<String>>,
|
||||
pub latest_text_output: __sdk::__query_builder::Col<AiTask, Option<String>>,
|
||||
pub latest_structured_payload_json: __sdk::__query_builder::Col<AiTask, Option<String>>,
|
||||
pub version: __sdk::__query_builder::Col<AiTask, u32>,
|
||||
pub created_at: __sdk::__query_builder::Col<AiTask, __sdk::Timestamp>,
|
||||
pub started_at: __sdk::__query_builder::Col<AiTask, Option::<__sdk::Timestamp>>,
|
||||
pub completed_at: __sdk::__query_builder::Col<AiTask, Option::<__sdk::Timestamp>>,
|
||||
pub started_at: __sdk::__query_builder::Col<AiTask, Option<__sdk::Timestamp>>,
|
||||
pub completed_at: __sdk::__query_builder::Col<AiTask, Option<__sdk::Timestamp>>,
|
||||
pub updated_at: __sdk::__query_builder::Col<AiTask, __sdk::Timestamp>,
|
||||
}
|
||||
|
||||
@@ -71,17 +64,22 @@ impl __sdk::__query_builder::HasCols for AiTask {
|
||||
request_label: __sdk::__query_builder::Col::new(table_name, "request_label"),
|
||||
source_module: __sdk::__query_builder::Col::new(table_name, "source_module"),
|
||||
source_entity_id: __sdk::__query_builder::Col::new(table_name, "source_entity_id"),
|
||||
request_payload_json: __sdk::__query_builder::Col::new(table_name, "request_payload_json"),
|
||||
request_payload_json: __sdk::__query_builder::Col::new(
|
||||
table_name,
|
||||
"request_payload_json",
|
||||
),
|
||||
status: __sdk::__query_builder::Col::new(table_name, "status"),
|
||||
failure_message: __sdk::__query_builder::Col::new(table_name, "failure_message"),
|
||||
latest_text_output: __sdk::__query_builder::Col::new(table_name, "latest_text_output"),
|
||||
latest_structured_payload_json: __sdk::__query_builder::Col::new(table_name, "latest_structured_payload_json"),
|
||||
latest_structured_payload_json: __sdk::__query_builder::Col::new(
|
||||
table_name,
|
||||
"latest_structured_payload_json",
|
||||
),
|
||||
version: __sdk::__query_builder::Col::new(table_name, "version"),
|
||||
created_at: __sdk::__query_builder::Col::new(table_name, "created_at"),
|
||||
started_at: __sdk::__query_builder::Col::new(table_name, "started_at"),
|
||||
completed_at: __sdk::__query_builder::Col::new(table_name, "completed_at"),
|
||||
updated_at: __sdk::__query_builder::Col::new(table_name, "updated_at"),
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -104,10 +102,8 @@ impl __sdk::__query_builder::HasIxCols for AiTask {
|
||||
status: __sdk::__query_builder::IxCol::new(table_name, "status"),
|
||||
task_id: __sdk::__query_builder::IxCol::new(table_name, "task_id"),
|
||||
task_kind: __sdk::__query_builder::IxCol::new(table_name, "task_kind"),
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl __sdk::__query_builder::CanBeLookupTable for AiTask {}
|
||||
|
||||
|
||||
@@ -2,12 +2,7 @@
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
#![allow(unused, clippy::all)]
|
||||
use spacetimedb_sdk::__codegen::{
|
||||
self as __sdk,
|
||||
__lib,
|
||||
__sats,
|
||||
__ws,
|
||||
};
|
||||
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
||||
|
||||
use super::ai_task_stage_kind_type::AiTaskStageKind;
|
||||
|
||||
@@ -21,8 +16,6 @@ pub struct AiTextChunkAppendInput {
|
||||
pub created_at_micros: i64,
|
||||
}
|
||||
|
||||
|
||||
impl __sdk::InModule for AiTextChunkAppendInput {
|
||||
type Module = super::RemoteModule;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,12 +2,7 @@
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
#![allow(unused, clippy::all)]
|
||||
use spacetimedb_sdk::__codegen::{
|
||||
self as __sdk,
|
||||
__lib,
|
||||
__sats,
|
||||
__ws,
|
||||
};
|
||||
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
||||
|
||||
use super::ai_task_stage_kind_type::AiTaskStageKind;
|
||||
|
||||
@@ -22,8 +17,6 @@ pub struct AiTextChunkSnapshot {
|
||||
pub created_at_micros: i64,
|
||||
}
|
||||
|
||||
|
||||
impl __sdk::InModule for AiTextChunkSnapshot {
|
||||
type Module = super::RemoteModule;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,14 +2,9 @@
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
#![allow(unused, clippy::all)]
|
||||
use spacetimedb_sdk::__codegen::{
|
||||
self as __sdk,
|
||||
__lib,
|
||||
__sats,
|
||||
__ws,
|
||||
};
|
||||
use super::ai_text_chunk_type::AiTextChunk;
|
||||
use super::ai_task_stage_kind_type::AiTaskStageKind;
|
||||
use super::ai_text_chunk_type::AiTextChunk;
|
||||
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
||||
|
||||
/// Table handle for the table `ai_text_chunk`.
|
||||
///
|
||||
@@ -50,8 +45,12 @@ impl<'ctx> __sdk::Table for AiTextChunkTableHandle<'ctx> {
|
||||
type Row = AiTextChunk;
|
||||
type EventContext = super::EventContext;
|
||||
|
||||
fn count(&self) -> u64 { self.imp.count() }
|
||||
fn iter(&self) -> impl Iterator<Item = AiTextChunk> + '_ { self.imp.iter() }
|
||||
fn count(&self) -> u64 {
|
||||
self.imp.count()
|
||||
}
|
||||
fn iter(&self) -> impl Iterator<Item = AiTextChunk> + '_ {
|
||||
self.imp.iter()
|
||||
}
|
||||
|
||||
type InsertCallbackId = AiTextChunkInsertCallbackId;
|
||||
|
||||
@@ -97,39 +96,40 @@ impl<'ctx> __sdk::TableWithPrimaryKey for AiTextChunkTableHandle<'ctx> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Access to the `text_chunk_row_id` unique index on the table `ai_text_chunk`,
|
||||
/// which allows point queries on the field of the same name
|
||||
/// via the [`AiTextChunkTextChunkRowIdUnique::find`] method.
|
||||
///
|
||||
/// Users are encouraged not to explicitly reference this type,
|
||||
/// but to directly chain method calls,
|
||||
/// like `ctx.db.ai_text_chunk().text_chunk_row_id().find(...)`.
|
||||
pub struct AiTextChunkTextChunkRowIdUnique<'ctx> {
|
||||
imp: __sdk::UniqueConstraintHandle<AiTextChunk, String>,
|
||||
phantom: std::marker::PhantomData<&'ctx super::RemoteTables>,
|
||||
}
|
||||
/// Access to the `text_chunk_row_id` unique index on the table `ai_text_chunk`,
|
||||
/// which allows point queries on the field of the same name
|
||||
/// via the [`AiTextChunkTextChunkRowIdUnique::find`] method.
|
||||
///
|
||||
/// Users are encouraged not to explicitly reference this type,
|
||||
/// but to directly chain method calls,
|
||||
/// like `ctx.db.ai_text_chunk().text_chunk_row_id().find(...)`.
|
||||
pub struct AiTextChunkTextChunkRowIdUnique<'ctx> {
|
||||
imp: __sdk::UniqueConstraintHandle<AiTextChunk, String>,
|
||||
phantom: std::marker::PhantomData<&'ctx super::RemoteTables>,
|
||||
}
|
||||
|
||||
impl<'ctx> AiTextChunkTableHandle<'ctx> {
|
||||
/// Get a handle on the `text_chunk_row_id` unique index on the table `ai_text_chunk`.
|
||||
pub fn text_chunk_row_id(&self) -> AiTextChunkTextChunkRowIdUnique<'ctx> {
|
||||
AiTextChunkTextChunkRowIdUnique {
|
||||
imp: self.imp.get_unique_constraint::<String>("text_chunk_row_id"),
|
||||
phantom: std::marker::PhantomData,
|
||||
}
|
||||
}
|
||||
impl<'ctx> AiTextChunkTableHandle<'ctx> {
|
||||
/// Get a handle on the `text_chunk_row_id` unique index on the table `ai_text_chunk`.
|
||||
pub fn text_chunk_row_id(&self) -> AiTextChunkTextChunkRowIdUnique<'ctx> {
|
||||
AiTextChunkTextChunkRowIdUnique {
|
||||
imp: self
|
||||
.imp
|
||||
.get_unique_constraint::<String>("text_chunk_row_id"),
|
||||
phantom: std::marker::PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ctx> AiTextChunkTextChunkRowIdUnique<'ctx> {
|
||||
/// Find the subscribed row whose `text_chunk_row_id` column value is equal to `col_val`,
|
||||
/// if such a row is present in the client cache.
|
||||
pub fn find(&self, col_val: &String) -> Option<AiTextChunk> {
|
||||
self.imp.find(col_val)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ctx> AiTextChunkTextChunkRowIdUnique<'ctx> {
|
||||
/// Find the subscribed row whose `text_chunk_row_id` column value is equal to `col_val`,
|
||||
/// if such a row is present in the client cache.
|
||||
pub fn find(&self, col_val: &String) -> Option<AiTextChunk> {
|
||||
self.imp.find(col_val)
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub(super) fn register_table(client_cache: &mut __sdk::ClientCache<super::RemoteModule>) {
|
||||
|
||||
let _table = client_cache.get_or_make_table::<AiTextChunk>("ai_text_chunk");
|
||||
_table.add_unique_constraint::<String>("text_chunk_row_id", |row| &row.text_chunk_row_id);
|
||||
}
|
||||
@@ -139,26 +139,24 @@ pub(super) fn parse_table_update(
|
||||
raw_updates: __ws::v2::TableUpdate,
|
||||
) -> __sdk::Result<__sdk::TableUpdate<AiTextChunk>> {
|
||||
__sdk::TableUpdate::parse_table_update(raw_updates).map_err(|e| {
|
||||
__sdk::InternalError::failed_parse(
|
||||
"TableUpdate<AiTextChunk>",
|
||||
"TableUpdate",
|
||||
).with_cause(e).into()
|
||||
__sdk::InternalError::failed_parse("TableUpdate<AiTextChunk>", "TableUpdate")
|
||||
.with_cause(e)
|
||||
.into()
|
||||
})
|
||||
}
|
||||
|
||||
#[allow(non_camel_case_types)]
|
||||
/// Extension trait for query builder access to the table `AiTextChunk`.
|
||||
///
|
||||
/// Implemented for [`__sdk::QueryTableAccessor`].
|
||||
pub trait ai_text_chunkQueryTableAccess {
|
||||
#[allow(non_snake_case)]
|
||||
/// Get a query builder for the table `AiTextChunk`.
|
||||
fn ai_text_chunk(&self) -> __sdk::__query_builder::Table<AiTextChunk>;
|
||||
}
|
||||
|
||||
impl ai_text_chunkQueryTableAccess for __sdk::QueryTableAccessor {
|
||||
fn ai_text_chunk(&self) -> __sdk::__query_builder::Table<AiTextChunk> {
|
||||
__sdk::__query_builder::Table::new("ai_text_chunk")
|
||||
}
|
||||
}
|
||||
#[allow(non_camel_case_types)]
|
||||
/// Extension trait for query builder access to the table `AiTextChunk`.
|
||||
///
|
||||
/// Implemented for [`__sdk::QueryTableAccessor`].
|
||||
pub trait ai_text_chunkQueryTableAccess {
|
||||
#[allow(non_snake_case)]
|
||||
/// Get a query builder for the table `AiTextChunk`.
|
||||
fn ai_text_chunk(&self) -> __sdk::__query_builder::Table<AiTextChunk>;
|
||||
}
|
||||
|
||||
impl ai_text_chunkQueryTableAccess for __sdk::QueryTableAccessor {
|
||||
fn ai_text_chunk(&self) -> __sdk::__query_builder::Table<AiTextChunk> {
|
||||
__sdk::__query_builder::Table::new("ai_text_chunk")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,12 +2,7 @@
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
#![allow(unused, clippy::all)]
|
||||
use spacetimedb_sdk::__codegen::{
|
||||
self as __sdk,
|
||||
__lib,
|
||||
__sats,
|
||||
__ws,
|
||||
};
|
||||
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
||||
|
||||
use super::ai_task_stage_kind_type::AiTaskStageKind;
|
||||
|
||||
@@ -23,12 +18,10 @@ pub struct AiTextChunk {
|
||||
pub created_at: __sdk::Timestamp,
|
||||
}
|
||||
|
||||
|
||||
impl __sdk::InModule for AiTextChunk {
|
||||
type Module = super::RemoteModule;
|
||||
}
|
||||
|
||||
|
||||
/// Column accessor struct for the table `AiTextChunk`.
|
||||
///
|
||||
/// Provides typed access to columns for query building.
|
||||
@@ -53,7 +46,6 @@ impl __sdk::__query_builder::HasCols for AiTextChunk {
|
||||
sequence: __sdk::__query_builder::Col::new(table_name, "sequence"),
|
||||
delta_text: __sdk::__query_builder::Col::new(table_name, "delta_text"),
|
||||
created_at: __sdk::__query_builder::Col::new(table_name, "created_at"),
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -72,10 +64,8 @@ impl __sdk::__query_builder::HasIxCols for AiTextChunk {
|
||||
AiTextChunkIxCols {
|
||||
task_id: __sdk::__query_builder::IxCol::new(table_name, "task_id"),
|
||||
text_chunk_row_id: __sdk::__query_builder::IxCol::new(table_name, "text_chunk_row_id"),
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl __sdk::__query_builder::CanBeLookupTable for AiTextChunk {}
|
||||
|
||||
|
||||
@@ -2,23 +2,17 @@
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
#![allow(unused, clippy::all)]
|
||||
use spacetimedb_sdk::__codegen::{
|
||||
self as __sdk,
|
||||
__lib,
|
||||
__sats,
|
||||
__ws,
|
||||
};
|
||||
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
||||
|
||||
use super::ai_text_chunk_append_input_type::AiTextChunkAppendInput;
|
||||
use super::ai_task_procedure_result_type::AiTaskProcedureResult;
|
||||
use super::ai_text_chunk_append_input_type::AiTextChunkAppendInput;
|
||||
|
||||
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
|
||||
#[sats(crate = __lib)]
|
||||
struct AppendAiTextChunkAndReturnArgs {
|
||||
struct AppendAiTextChunkAndReturnArgs {
|
||||
pub input: AiTextChunkAppendInput,
|
||||
}
|
||||
|
||||
|
||||
impl __sdk::InModule for AppendAiTextChunkAndReturnArgs {
|
||||
type Module = super::RemoteModule;
|
||||
}
|
||||
@@ -28,16 +22,19 @@ impl __sdk::InModule for AppendAiTextChunkAndReturnArgs {
|
||||
///
|
||||
/// Implemented for [`super::RemoteProcedures`].
|
||||
pub trait append_ai_text_chunk_and_return {
|
||||
fn append_ai_text_chunk_and_return(&self, input: AiTextChunkAppendInput,
|
||||
) {
|
||||
self.append_ai_text_chunk_and_return_then(input, |_, _| {});
|
||||
fn append_ai_text_chunk_and_return(&self, input: AiTextChunkAppendInput) {
|
||||
self.append_ai_text_chunk_and_return_then(input, |_, _| {});
|
||||
}
|
||||
|
||||
fn append_ai_text_chunk_and_return_then(
|
||||
&self,
|
||||
input: AiTextChunkAppendInput,
|
||||
|
||||
__callback: impl FnOnce(&super::ProcedureEventContext, Result<AiTaskProcedureResult, __sdk::InternalError>) + Send + 'static,
|
||||
__callback: impl FnOnce(
|
||||
&super::ProcedureEventContext,
|
||||
Result<AiTaskProcedureResult, __sdk::InternalError>,
|
||||
) + Send
|
||||
+ 'static,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -46,13 +43,17 @@ impl append_ai_text_chunk_and_return for super::RemoteProcedures {
|
||||
&self,
|
||||
input: AiTextChunkAppendInput,
|
||||
|
||||
__callback: impl FnOnce(&super::ProcedureEventContext, Result<AiTaskProcedureResult, __sdk::InternalError>) + Send + 'static,
|
||||
__callback: impl FnOnce(
|
||||
&super::ProcedureEventContext,
|
||||
Result<AiTaskProcedureResult, __sdk::InternalError>,
|
||||
) + Send
|
||||
+ 'static,
|
||||
) {
|
||||
self.imp.invoke_procedure_with_callback::<_, AiTaskProcedureResult>(
|
||||
"append_ai_text_chunk_and_return",
|
||||
AppendAiTextChunkAndReturnArgs { input, },
|
||||
__callback,
|
||||
);
|
||||
self.imp
|
||||
.invoke_procedure_with_callback::<_, AiTaskProcedureResult>(
|
||||
"append_ai_text_chunk_and_return",
|
||||
AppendAiTextChunkAndReturnArgs { input },
|
||||
__callback,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,23 +2,17 @@
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
#![allow(unused, clippy::all)]
|
||||
use spacetimedb_sdk::__codegen::{
|
||||
self as __sdk,
|
||||
__lib,
|
||||
__sats,
|
||||
__ws,
|
||||
};
|
||||
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
||||
|
||||
use super::chapter_progression_ledger_input_type::ChapterProgressionLedgerInput;
|
||||
use super::chapter_progression_procedure_result_type::ChapterProgressionProcedureResult;
|
||||
|
||||
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
|
||||
#[sats(crate = __lib)]
|
||||
struct ApplyChapterProgressionLedgerEntryAndReturnArgs {
|
||||
struct ApplyChapterProgressionLedgerEntryAndReturnArgs {
|
||||
pub input: ChapterProgressionLedgerInput,
|
||||
}
|
||||
|
||||
|
||||
impl __sdk::InModule for ApplyChapterProgressionLedgerEntryAndReturnArgs {
|
||||
type Module = super::RemoteModule;
|
||||
}
|
||||
@@ -28,16 +22,22 @@ impl __sdk::InModule for ApplyChapterProgressionLedgerEntryAndReturnArgs {
|
||||
///
|
||||
/// Implemented for [`super::RemoteProcedures`].
|
||||
pub trait apply_chapter_progression_ledger_entry_and_return {
|
||||
fn apply_chapter_progression_ledger_entry_and_return(&self, input: ChapterProgressionLedgerInput,
|
||||
) {
|
||||
self.apply_chapter_progression_ledger_entry_and_return_then(input, |_, _| {});
|
||||
fn apply_chapter_progression_ledger_entry_and_return(
|
||||
&self,
|
||||
input: ChapterProgressionLedgerInput,
|
||||
) {
|
||||
self.apply_chapter_progression_ledger_entry_and_return_then(input, |_, _| {});
|
||||
}
|
||||
|
||||
fn apply_chapter_progression_ledger_entry_and_return_then(
|
||||
&self,
|
||||
input: ChapterProgressionLedgerInput,
|
||||
|
||||
__callback: impl FnOnce(&super::ProcedureEventContext, Result<ChapterProgressionProcedureResult, __sdk::InternalError>) + Send + 'static,
|
||||
__callback: impl FnOnce(
|
||||
&super::ProcedureEventContext,
|
||||
Result<ChapterProgressionProcedureResult, __sdk::InternalError>,
|
||||
) + Send
|
||||
+ 'static,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -46,13 +46,17 @@ impl apply_chapter_progression_ledger_entry_and_return for super::RemoteProcedur
|
||||
&self,
|
||||
input: ChapterProgressionLedgerInput,
|
||||
|
||||
__callback: impl FnOnce(&super::ProcedureEventContext, Result<ChapterProgressionProcedureResult, __sdk::InternalError>) + Send + 'static,
|
||||
__callback: impl FnOnce(
|
||||
&super::ProcedureEventContext,
|
||||
Result<ChapterProgressionProcedureResult, __sdk::InternalError>,
|
||||
) + Send
|
||||
+ 'static,
|
||||
) {
|
||||
self.imp.invoke_procedure_with_callback::<_, ChapterProgressionProcedureResult>(
|
||||
"apply_chapter_progression_ledger_entry_and_return",
|
||||
ApplyChapterProgressionLedgerEntryAndReturnArgs { input, },
|
||||
__callback,
|
||||
);
|
||||
self.imp
|
||||
.invoke_procedure_with_callback::<_, ChapterProgressionProcedureResult>(
|
||||
"apply_chapter_progression_ledger_entry_and_return",
|
||||
ApplyChapterProgressionLedgerEntryAndReturnArgs { input },
|
||||
__callback,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,12 +2,7 @@
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
#![allow(unused, clippy::all)]
|
||||
use spacetimedb_sdk::__codegen::{
|
||||
self as __sdk,
|
||||
__lib,
|
||||
__sats,
|
||||
__ws,
|
||||
};
|
||||
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
||||
|
||||
use super::chapter_progression_ledger_input_type::ChapterProgressionLedgerInput;
|
||||
|
||||
@@ -19,10 +14,8 @@ pub(super) struct ApplyChapterProgressionLedgerEntryArgs {
|
||||
|
||||
impl From<ApplyChapterProgressionLedgerEntryArgs> for super::Reducer {
|
||||
fn from(args: ApplyChapterProgressionLedgerEntryArgs) -> Self {
|
||||
Self::ApplyChapterProgressionLedgerEntry {
|
||||
input: args.input,
|
||||
}
|
||||
}
|
||||
Self::ApplyChapterProgressionLedgerEntry { input: args.input }
|
||||
}
|
||||
}
|
||||
|
||||
impl __sdk::InModule for ApplyChapterProgressionLedgerEntryArgs {
|
||||
@@ -40,9 +33,11 @@ pub trait apply_chapter_progression_ledger_entry {
|
||||
/// The reducer will run asynchronously in the future,
|
||||
/// and this method provides no way to listen for its completion status.
|
||||
/// /// Use [`apply_chapter_progression_ledger_entry:apply_chapter_progression_ledger_entry_then`] to run a callback after the reducer completes.
|
||||
fn apply_chapter_progression_ledger_entry(&self, input: ChapterProgressionLedgerInput,
|
||||
) -> __sdk::Result<()> {
|
||||
self.apply_chapter_progression_ledger_entry_then(input, |_, _| {})
|
||||
fn apply_chapter_progression_ledger_entry(
|
||||
&self,
|
||||
input: ChapterProgressionLedgerInput,
|
||||
) -> __sdk::Result<()> {
|
||||
self.apply_chapter_progression_ledger_entry_then(input, |_, _| {})
|
||||
}
|
||||
|
||||
/// Request that the remote module invoke the reducer `apply_chapter_progression_ledger_entry` to run as soon as possible,
|
||||
@@ -55,9 +50,11 @@ pub trait apply_chapter_progression_ledger_entry {
|
||||
&self,
|
||||
input: ChapterProgressionLedgerInput,
|
||||
|
||||
callback: impl FnOnce(&super::ReducerEventContext, Result<Result<(), String>, __sdk::InternalError>)
|
||||
+ Send
|
||||
+ 'static,
|
||||
callback: impl FnOnce(
|
||||
&super::ReducerEventContext,
|
||||
Result<Result<(), String>, __sdk::InternalError>,
|
||||
) + Send
|
||||
+ 'static,
|
||||
) -> __sdk::Result<()>;
|
||||
}
|
||||
|
||||
@@ -66,11 +63,15 @@ impl apply_chapter_progression_ledger_entry for super::RemoteReducers {
|
||||
&self,
|
||||
input: ChapterProgressionLedgerInput,
|
||||
|
||||
callback: impl FnOnce(&super::ReducerEventContext, Result<Result<(), String>, __sdk::InternalError>)
|
||||
+ Send
|
||||
+ 'static,
|
||||
callback: impl FnOnce(
|
||||
&super::ReducerEventContext,
|
||||
Result<Result<(), String>, __sdk::InternalError>,
|
||||
) + Send
|
||||
+ 'static,
|
||||
) -> __sdk::Result<()> {
|
||||
self.imp.invoke_reducer_with_callback(ApplyChapterProgressionLedgerEntryArgs { input, }, callback)
|
||||
self.imp.invoke_reducer_with_callback(
|
||||
ApplyChapterProgressionLedgerEntryArgs { input },
|
||||
callback,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,12 +2,7 @@
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
#![allow(unused, clippy::all)]
|
||||
use spacetimedb_sdk::__codegen::{
|
||||
self as __sdk,
|
||||
__lib,
|
||||
__sats,
|
||||
__ws,
|
||||
};
|
||||
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
||||
|
||||
use super::inventory_mutation_input_type::InventoryMutationInput;
|
||||
|
||||
@@ -19,10 +14,8 @@ pub(super) struct ApplyInventoryMutationArgs {
|
||||
|
||||
impl From<ApplyInventoryMutationArgs> for super::Reducer {
|
||||
fn from(args: ApplyInventoryMutationArgs) -> Self {
|
||||
Self::ApplyInventoryMutation {
|
||||
input: args.input,
|
||||
}
|
||||
}
|
||||
Self::ApplyInventoryMutation { input: args.input }
|
||||
}
|
||||
}
|
||||
|
||||
impl __sdk::InModule for ApplyInventoryMutationArgs {
|
||||
@@ -40,9 +33,8 @@ pub trait apply_inventory_mutation {
|
||||
/// The reducer will run asynchronously in the future,
|
||||
/// and this method provides no way to listen for its completion status.
|
||||
/// /// Use [`apply_inventory_mutation:apply_inventory_mutation_then`] to run a callback after the reducer completes.
|
||||
fn apply_inventory_mutation(&self, input: InventoryMutationInput,
|
||||
) -> __sdk::Result<()> {
|
||||
self.apply_inventory_mutation_then(input, |_, _| {})
|
||||
fn apply_inventory_mutation(&self, input: InventoryMutationInput) -> __sdk::Result<()> {
|
||||
self.apply_inventory_mutation_then(input, |_, _| {})
|
||||
}
|
||||
|
||||
/// Request that the remote module invoke the reducer `apply_inventory_mutation` to run as soon as possible,
|
||||
@@ -55,9 +47,11 @@ pub trait apply_inventory_mutation {
|
||||
&self,
|
||||
input: InventoryMutationInput,
|
||||
|
||||
callback: impl FnOnce(&super::ReducerEventContext, Result<Result<(), String>, __sdk::InternalError>)
|
||||
+ Send
|
||||
+ 'static,
|
||||
callback: impl FnOnce(
|
||||
&super::ReducerEventContext,
|
||||
Result<Result<(), String>, __sdk::InternalError>,
|
||||
) + Send
|
||||
+ 'static,
|
||||
) -> __sdk::Result<()>;
|
||||
}
|
||||
|
||||
@@ -66,11 +60,13 @@ impl apply_inventory_mutation for super::RemoteReducers {
|
||||
&self,
|
||||
input: InventoryMutationInput,
|
||||
|
||||
callback: impl FnOnce(&super::ReducerEventContext, Result<Result<(), String>, __sdk::InternalError>)
|
||||
+ Send
|
||||
+ 'static,
|
||||
callback: impl FnOnce(
|
||||
&super::ReducerEventContext,
|
||||
Result<Result<(), String>, __sdk::InternalError>,
|
||||
) + Send
|
||||
+ 'static,
|
||||
) -> __sdk::Result<()> {
|
||||
self.imp.invoke_reducer_with_callback(ApplyInventoryMutationArgs { input, }, callback)
|
||||
self.imp
|
||||
.invoke_reducer_with_callback(ApplyInventoryMutationArgs { input }, callback)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,12 +2,7 @@
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
#![allow(unused, clippy::all)]
|
||||
use spacetimedb_sdk::__codegen::{
|
||||
self as __sdk,
|
||||
__lib,
|
||||
__sats,
|
||||
__ws,
|
||||
};
|
||||
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
||||
|
||||
use super::quest_signal_apply_input_type::QuestSignalApplyInput;
|
||||
|
||||
@@ -19,10 +14,8 @@ pub(super) struct ApplyQuestSignalArgs {
|
||||
|
||||
impl From<ApplyQuestSignalArgs> for super::Reducer {
|
||||
fn from(args: ApplyQuestSignalArgs) -> Self {
|
||||
Self::ApplyQuestSignal {
|
||||
input: args.input,
|
||||
}
|
||||
}
|
||||
Self::ApplyQuestSignal { input: args.input }
|
||||
}
|
||||
}
|
||||
|
||||
impl __sdk::InModule for ApplyQuestSignalArgs {
|
||||
@@ -40,9 +33,8 @@ pub trait apply_quest_signal {
|
||||
/// The reducer will run asynchronously in the future,
|
||||
/// and this method provides no way to listen for its completion status.
|
||||
/// /// Use [`apply_quest_signal:apply_quest_signal_then`] to run a callback after the reducer completes.
|
||||
fn apply_quest_signal(&self, input: QuestSignalApplyInput,
|
||||
) -> __sdk::Result<()> {
|
||||
self.apply_quest_signal_then(input, |_, _| {})
|
||||
fn apply_quest_signal(&self, input: QuestSignalApplyInput) -> __sdk::Result<()> {
|
||||
self.apply_quest_signal_then(input, |_, _| {})
|
||||
}
|
||||
|
||||
/// Request that the remote module invoke the reducer `apply_quest_signal` to run as soon as possible,
|
||||
@@ -55,9 +47,11 @@ pub trait apply_quest_signal {
|
||||
&self,
|
||||
input: QuestSignalApplyInput,
|
||||
|
||||
callback: impl FnOnce(&super::ReducerEventContext, Result<Result<(), String>, __sdk::InternalError>)
|
||||
+ Send
|
||||
+ 'static,
|
||||
callback: impl FnOnce(
|
||||
&super::ReducerEventContext,
|
||||
Result<Result<(), String>, __sdk::InternalError>,
|
||||
) + Send
|
||||
+ 'static,
|
||||
) -> __sdk::Result<()>;
|
||||
}
|
||||
|
||||
@@ -66,11 +60,13 @@ impl apply_quest_signal for super::RemoteReducers {
|
||||
&self,
|
||||
input: QuestSignalApplyInput,
|
||||
|
||||
callback: impl FnOnce(&super::ReducerEventContext, Result<Result<(), String>, __sdk::InternalError>)
|
||||
+ Send
|
||||
+ 'static,
|
||||
callback: impl FnOnce(
|
||||
&super::ReducerEventContext,
|
||||
Result<Result<(), String>, __sdk::InternalError>,
|
||||
) + Send
|
||||
+ 'static,
|
||||
) -> __sdk::Result<()> {
|
||||
self.imp.invoke_reducer_with_callback(ApplyQuestSignalArgs { input, }, callback)
|
||||
self.imp
|
||||
.invoke_reducer_with_callback(ApplyQuestSignalArgs { input }, callback)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,13 +2,7 @@
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
#![allow(unused, clippy::all)]
|
||||
use spacetimedb_sdk::__codegen::{
|
||||
self as __sdk,
|
||||
__lib,
|
||||
__sats,
|
||||
__ws,
|
||||
};
|
||||
|
||||
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
||||
|
||||
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
|
||||
#[sats(crate = __lib)]
|
||||
@@ -19,13 +13,11 @@ pub struct AssetEntityBindingInput {
|
||||
pub entity_id: String,
|
||||
pub slot: String,
|
||||
pub asset_kind: String,
|
||||
pub owner_user_id: Option::<String>,
|
||||
pub profile_id: Option::<String>,
|
||||
pub owner_user_id: Option<String>,
|
||||
pub profile_id: Option<String>,
|
||||
pub updated_at_micros: i64,
|
||||
}
|
||||
|
||||
|
||||
impl __sdk::InModule for AssetEntityBindingInput {
|
||||
type Module = super::RemoteModule;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,12 +2,7 @@
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
#![allow(unused, clippy::all)]
|
||||
use spacetimedb_sdk::__codegen::{
|
||||
self as __sdk,
|
||||
__lib,
|
||||
__sats,
|
||||
__ws,
|
||||
};
|
||||
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
||||
|
||||
use super::asset_entity_binding_snapshot_type::AssetEntityBindingSnapshot;
|
||||
|
||||
@@ -15,12 +10,10 @@ use super::asset_entity_binding_snapshot_type::AssetEntityBindingSnapshot;
|
||||
#[sats(crate = __lib)]
|
||||
pub struct AssetEntityBindingProcedureResult {
|
||||
pub ok: bool,
|
||||
pub record: Option::<AssetEntityBindingSnapshot>,
|
||||
pub error_message: Option::<String>,
|
||||
pub record: Option<AssetEntityBindingSnapshot>,
|
||||
pub error_message: Option<String>,
|
||||
}
|
||||
|
||||
|
||||
impl __sdk::InModule for AssetEntityBindingProcedureResult {
|
||||
type Module = super::RemoteModule;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,13 +2,7 @@
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
#![allow(unused, clippy::all)]
|
||||
use spacetimedb_sdk::__codegen::{
|
||||
self as __sdk,
|
||||
__lib,
|
||||
__sats,
|
||||
__ws,
|
||||
};
|
||||
|
||||
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
||||
|
||||
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
|
||||
#[sats(crate = __lib)]
|
||||
@@ -19,14 +13,12 @@ pub struct AssetEntityBindingSnapshot {
|
||||
pub entity_id: String,
|
||||
pub slot: String,
|
||||
pub asset_kind: String,
|
||||
pub owner_user_id: Option::<String>,
|
||||
pub profile_id: Option::<String>,
|
||||
pub owner_user_id: Option<String>,
|
||||
pub profile_id: Option<String>,
|
||||
pub created_at_micros: i64,
|
||||
pub updated_at_micros: i64,
|
||||
}
|
||||
|
||||
|
||||
impl __sdk::InModule for AssetEntityBindingSnapshot {
|
||||
type Module = super::RemoteModule;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,13 +2,8 @@
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
#![allow(unused, clippy::all)]
|
||||
use spacetimedb_sdk::__codegen::{
|
||||
self as __sdk,
|
||||
__lib,
|
||||
__sats,
|
||||
__ws,
|
||||
};
|
||||
use super::asset_entity_binding_type::AssetEntityBinding;
|
||||
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
||||
|
||||
/// Table handle for the table `asset_entity_binding`.
|
||||
///
|
||||
@@ -36,7 +31,9 @@ pub trait AssetEntityBindingTableAccess {
|
||||
impl AssetEntityBindingTableAccess for super::RemoteTables {
|
||||
fn asset_entity_binding(&self) -> AssetEntityBindingTableHandle<'_> {
|
||||
AssetEntityBindingTableHandle {
|
||||
imp: self.imp.get_table::<AssetEntityBinding>("asset_entity_binding"),
|
||||
imp: self
|
||||
.imp
|
||||
.get_table::<AssetEntityBinding>("asset_entity_binding"),
|
||||
ctx: std::marker::PhantomData,
|
||||
}
|
||||
}
|
||||
@@ -49,8 +46,12 @@ impl<'ctx> __sdk::Table for AssetEntityBindingTableHandle<'ctx> {
|
||||
type Row = AssetEntityBinding;
|
||||
type EventContext = super::EventContext;
|
||||
|
||||
fn count(&self) -> u64 { self.imp.count() }
|
||||
fn iter(&self) -> impl Iterator<Item = AssetEntityBinding> + '_ { self.imp.iter() }
|
||||
fn count(&self) -> u64 {
|
||||
self.imp.count()
|
||||
}
|
||||
fn iter(&self) -> impl Iterator<Item = AssetEntityBinding> + '_ {
|
||||
self.imp.iter()
|
||||
}
|
||||
|
||||
type InsertCallbackId = AssetEntityBindingInsertCallbackId;
|
||||
|
||||
@@ -96,39 +97,38 @@ impl<'ctx> __sdk::TableWithPrimaryKey for AssetEntityBindingTableHandle<'ctx> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Access to the `binding_id` unique index on the table `asset_entity_binding`,
|
||||
/// which allows point queries on the field of the same name
|
||||
/// via the [`AssetEntityBindingBindingIdUnique::find`] method.
|
||||
///
|
||||
/// Users are encouraged not to explicitly reference this type,
|
||||
/// but to directly chain method calls,
|
||||
/// like `ctx.db.asset_entity_binding().binding_id().find(...)`.
|
||||
pub struct AssetEntityBindingBindingIdUnique<'ctx> {
|
||||
imp: __sdk::UniqueConstraintHandle<AssetEntityBinding, String>,
|
||||
phantom: std::marker::PhantomData<&'ctx super::RemoteTables>,
|
||||
}
|
||||
/// Access to the `binding_id` unique index on the table `asset_entity_binding`,
|
||||
/// which allows point queries on the field of the same name
|
||||
/// via the [`AssetEntityBindingBindingIdUnique::find`] method.
|
||||
///
|
||||
/// Users are encouraged not to explicitly reference this type,
|
||||
/// but to directly chain method calls,
|
||||
/// like `ctx.db.asset_entity_binding().binding_id().find(...)`.
|
||||
pub struct AssetEntityBindingBindingIdUnique<'ctx> {
|
||||
imp: __sdk::UniqueConstraintHandle<AssetEntityBinding, String>,
|
||||
phantom: std::marker::PhantomData<&'ctx super::RemoteTables>,
|
||||
}
|
||||
|
||||
impl<'ctx> AssetEntityBindingTableHandle<'ctx> {
|
||||
/// Get a handle on the `binding_id` unique index on the table `asset_entity_binding`.
|
||||
pub fn binding_id(&self) -> AssetEntityBindingBindingIdUnique<'ctx> {
|
||||
AssetEntityBindingBindingIdUnique {
|
||||
imp: self.imp.get_unique_constraint::<String>("binding_id"),
|
||||
phantom: std::marker::PhantomData,
|
||||
}
|
||||
}
|
||||
impl<'ctx> AssetEntityBindingTableHandle<'ctx> {
|
||||
/// Get a handle on the `binding_id` unique index on the table `asset_entity_binding`.
|
||||
pub fn binding_id(&self) -> AssetEntityBindingBindingIdUnique<'ctx> {
|
||||
AssetEntityBindingBindingIdUnique {
|
||||
imp: self.imp.get_unique_constraint::<String>("binding_id"),
|
||||
phantom: std::marker::PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ctx> AssetEntityBindingBindingIdUnique<'ctx> {
|
||||
/// Find the subscribed row whose `binding_id` column value is equal to `col_val`,
|
||||
/// if such a row is present in the client cache.
|
||||
pub fn find(&self, col_val: &String) -> Option<AssetEntityBinding> {
|
||||
self.imp.find(col_val)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ctx> AssetEntityBindingBindingIdUnique<'ctx> {
|
||||
/// Find the subscribed row whose `binding_id` column value is equal to `col_val`,
|
||||
/// if such a row is present in the client cache.
|
||||
pub fn find(&self, col_val: &String) -> Option<AssetEntityBinding> {
|
||||
self.imp.find(col_val)
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub(super) fn register_table(client_cache: &mut __sdk::ClientCache<super::RemoteModule>) {
|
||||
|
||||
let _table = client_cache.get_or_make_table::<AssetEntityBinding>("asset_entity_binding");
|
||||
_table.add_unique_constraint::<String>("binding_id", |row| &row.binding_id);
|
||||
}
|
||||
@@ -138,26 +138,24 @@ pub(super) fn parse_table_update(
|
||||
raw_updates: __ws::v2::TableUpdate,
|
||||
) -> __sdk::Result<__sdk::TableUpdate<AssetEntityBinding>> {
|
||||
__sdk::TableUpdate::parse_table_update(raw_updates).map_err(|e| {
|
||||
__sdk::InternalError::failed_parse(
|
||||
"TableUpdate<AssetEntityBinding>",
|
||||
"TableUpdate",
|
||||
).with_cause(e).into()
|
||||
__sdk::InternalError::failed_parse("TableUpdate<AssetEntityBinding>", "TableUpdate")
|
||||
.with_cause(e)
|
||||
.into()
|
||||
})
|
||||
}
|
||||
|
||||
#[allow(non_camel_case_types)]
|
||||
/// Extension trait for query builder access to the table `AssetEntityBinding`.
|
||||
///
|
||||
/// Implemented for [`__sdk::QueryTableAccessor`].
|
||||
pub trait asset_entity_bindingQueryTableAccess {
|
||||
#[allow(non_snake_case)]
|
||||
/// Get a query builder for the table `AssetEntityBinding`.
|
||||
fn asset_entity_binding(&self) -> __sdk::__query_builder::Table<AssetEntityBinding>;
|
||||
}
|
||||
|
||||
impl asset_entity_bindingQueryTableAccess for __sdk::QueryTableAccessor {
|
||||
fn asset_entity_binding(&self) -> __sdk::__query_builder::Table<AssetEntityBinding> {
|
||||
__sdk::__query_builder::Table::new("asset_entity_binding")
|
||||
}
|
||||
}
|
||||
#[allow(non_camel_case_types)]
|
||||
/// Extension trait for query builder access to the table `AssetEntityBinding`.
|
||||
///
|
||||
/// Implemented for [`__sdk::QueryTableAccessor`].
|
||||
pub trait asset_entity_bindingQueryTableAccess {
|
||||
#[allow(non_snake_case)]
|
||||
/// Get a query builder for the table `AssetEntityBinding`.
|
||||
fn asset_entity_binding(&self) -> __sdk::__query_builder::Table<AssetEntityBinding>;
|
||||
}
|
||||
|
||||
impl asset_entity_bindingQueryTableAccess for __sdk::QueryTableAccessor {
|
||||
fn asset_entity_binding(&self) -> __sdk::__query_builder::Table<AssetEntityBinding> {
|
||||
__sdk::__query_builder::Table::new("asset_entity_binding")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,13 +2,7 @@
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
#![allow(unused, clippy::all)]
|
||||
use spacetimedb_sdk::__codegen::{
|
||||
self as __sdk,
|
||||
__lib,
|
||||
__sats,
|
||||
__ws,
|
||||
};
|
||||
|
||||
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
||||
|
||||
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
|
||||
#[sats(crate = __lib)]
|
||||
@@ -19,18 +13,16 @@ pub struct AssetEntityBinding {
|
||||
pub entity_id: String,
|
||||
pub slot: String,
|
||||
pub asset_kind: String,
|
||||
pub owner_user_id: Option::<String>,
|
||||
pub profile_id: Option::<String>,
|
||||
pub owner_user_id: Option<String>,
|
||||
pub profile_id: Option<String>,
|
||||
pub created_at: __sdk::Timestamp,
|
||||
pub updated_at: __sdk::Timestamp,
|
||||
}
|
||||
|
||||
|
||||
impl __sdk::InModule for AssetEntityBinding {
|
||||
type Module = super::RemoteModule;
|
||||
}
|
||||
|
||||
|
||||
/// Column accessor struct for the table `AssetEntityBinding`.
|
||||
///
|
||||
/// Provides typed access to columns for query building.
|
||||
@@ -41,8 +33,8 @@ pub struct AssetEntityBindingCols {
|
||||
pub entity_id: __sdk::__query_builder::Col<AssetEntityBinding, String>,
|
||||
pub slot: __sdk::__query_builder::Col<AssetEntityBinding, String>,
|
||||
pub asset_kind: __sdk::__query_builder::Col<AssetEntityBinding, String>,
|
||||
pub owner_user_id: __sdk::__query_builder::Col<AssetEntityBinding, Option::<String>>,
|
||||
pub profile_id: __sdk::__query_builder::Col<AssetEntityBinding, Option::<String>>,
|
||||
pub owner_user_id: __sdk::__query_builder::Col<AssetEntityBinding, Option<String>>,
|
||||
pub profile_id: __sdk::__query_builder::Col<AssetEntityBinding, Option<String>>,
|
||||
pub created_at: __sdk::__query_builder::Col<AssetEntityBinding, __sdk::Timestamp>,
|
||||
pub updated_at: __sdk::__query_builder::Col<AssetEntityBinding, __sdk::Timestamp>,
|
||||
}
|
||||
@@ -61,7 +53,6 @@ impl __sdk::__query_builder::HasCols for AssetEntityBinding {
|
||||
profile_id: __sdk::__query_builder::Col::new(table_name, "profile_id"),
|
||||
created_at: __sdk::__query_builder::Col::new(table_name, "created_at"),
|
||||
updated_at: __sdk::__query_builder::Col::new(table_name, "updated_at"),
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -80,10 +71,8 @@ impl __sdk::__query_builder::HasIxCols for AssetEntityBinding {
|
||||
AssetEntityBindingIxCols {
|
||||
asset_object_id: __sdk::__query_builder::IxCol::new(table_name, "asset_object_id"),
|
||||
binding_id: __sdk::__query_builder::IxCol::new(table_name, "binding_id"),
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl __sdk::__query_builder::CanBeLookupTable for AssetEntityBinding {}
|
||||
|
||||
|
||||
@@ -2,12 +2,7 @@
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
#![allow(unused, clippy::all)]
|
||||
use spacetimedb_sdk::__codegen::{
|
||||
self as __sdk,
|
||||
__lib,
|
||||
__sats,
|
||||
__ws,
|
||||
};
|
||||
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
||||
|
||||
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
|
||||
#[sats(crate = __lib)]
|
||||
@@ -16,12 +11,8 @@ pub enum AssetObjectAccessPolicy {
|
||||
Private,
|
||||
|
||||
PublicRead,
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
impl __sdk::InModule for AssetObjectAccessPolicy {
|
||||
type Module = super::RemoteModule;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,12 +2,7 @@
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
#![allow(unused, clippy::all)]
|
||||
use spacetimedb_sdk::__codegen::{
|
||||
self as __sdk,
|
||||
__lib,
|
||||
__sats,
|
||||
__ws,
|
||||
};
|
||||
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
||||
|
||||
use super::asset_object_upsert_snapshot_type::AssetObjectUpsertSnapshot;
|
||||
|
||||
@@ -15,12 +10,10 @@ use super::asset_object_upsert_snapshot_type::AssetObjectUpsertSnapshot;
|
||||
#[sats(crate = __lib)]
|
||||
pub struct AssetObjectProcedureResult {
|
||||
pub ok: bool,
|
||||
pub record: Option::<AssetObjectUpsertSnapshot>,
|
||||
pub error_message: Option::<String>,
|
||||
pub record: Option<AssetObjectUpsertSnapshot>,
|
||||
pub error_message: Option<String>,
|
||||
}
|
||||
|
||||
|
||||
impl __sdk::InModule for AssetObjectProcedureResult {
|
||||
type Module = super::RemoteModule;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,14 +2,9 @@
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
#![allow(unused, clippy::all)]
|
||||
use spacetimedb_sdk::__codegen::{
|
||||
self as __sdk,
|
||||
__lib,
|
||||
__sats,
|
||||
__ws,
|
||||
};
|
||||
use super::asset_object_type::AssetObject;
|
||||
use super::asset_object_access_policy_type::AssetObjectAccessPolicy;
|
||||
use super::asset_object_type::AssetObject;
|
||||
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
||||
|
||||
/// Table handle for the table `asset_object`.
|
||||
///
|
||||
@@ -50,8 +45,12 @@ impl<'ctx> __sdk::Table for AssetObjectTableHandle<'ctx> {
|
||||
type Row = AssetObject;
|
||||
type EventContext = super::EventContext;
|
||||
|
||||
fn count(&self) -> u64 { self.imp.count() }
|
||||
fn iter(&self) -> impl Iterator<Item = AssetObject> + '_ { self.imp.iter() }
|
||||
fn count(&self) -> u64 {
|
||||
self.imp.count()
|
||||
}
|
||||
fn iter(&self) -> impl Iterator<Item = AssetObject> + '_ {
|
||||
self.imp.iter()
|
||||
}
|
||||
|
||||
type InsertCallbackId = AssetObjectInsertCallbackId;
|
||||
|
||||
@@ -97,39 +96,38 @@ impl<'ctx> __sdk::TableWithPrimaryKey for AssetObjectTableHandle<'ctx> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Access to the `asset_object_id` unique index on the table `asset_object`,
|
||||
/// which allows point queries on the field of the same name
|
||||
/// via the [`AssetObjectAssetObjectIdUnique::find`] method.
|
||||
///
|
||||
/// Users are encouraged not to explicitly reference this type,
|
||||
/// but to directly chain method calls,
|
||||
/// like `ctx.db.asset_object().asset_object_id().find(...)`.
|
||||
pub struct AssetObjectAssetObjectIdUnique<'ctx> {
|
||||
imp: __sdk::UniqueConstraintHandle<AssetObject, String>,
|
||||
phantom: std::marker::PhantomData<&'ctx super::RemoteTables>,
|
||||
}
|
||||
/// Access to the `asset_object_id` unique index on the table `asset_object`,
|
||||
/// which allows point queries on the field of the same name
|
||||
/// via the [`AssetObjectAssetObjectIdUnique::find`] method.
|
||||
///
|
||||
/// Users are encouraged not to explicitly reference this type,
|
||||
/// but to directly chain method calls,
|
||||
/// like `ctx.db.asset_object().asset_object_id().find(...)`.
|
||||
pub struct AssetObjectAssetObjectIdUnique<'ctx> {
|
||||
imp: __sdk::UniqueConstraintHandle<AssetObject, String>,
|
||||
phantom: std::marker::PhantomData<&'ctx super::RemoteTables>,
|
||||
}
|
||||
|
||||
impl<'ctx> AssetObjectTableHandle<'ctx> {
|
||||
/// Get a handle on the `asset_object_id` unique index on the table `asset_object`.
|
||||
pub fn asset_object_id(&self) -> AssetObjectAssetObjectIdUnique<'ctx> {
|
||||
AssetObjectAssetObjectIdUnique {
|
||||
imp: self.imp.get_unique_constraint::<String>("asset_object_id"),
|
||||
phantom: std::marker::PhantomData,
|
||||
}
|
||||
}
|
||||
impl<'ctx> AssetObjectTableHandle<'ctx> {
|
||||
/// Get a handle on the `asset_object_id` unique index on the table `asset_object`.
|
||||
pub fn asset_object_id(&self) -> AssetObjectAssetObjectIdUnique<'ctx> {
|
||||
AssetObjectAssetObjectIdUnique {
|
||||
imp: self.imp.get_unique_constraint::<String>("asset_object_id"),
|
||||
phantom: std::marker::PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ctx> AssetObjectAssetObjectIdUnique<'ctx> {
|
||||
/// Find the subscribed row whose `asset_object_id` column value is equal to `col_val`,
|
||||
/// if such a row is present in the client cache.
|
||||
pub fn find(&self, col_val: &String) -> Option<AssetObject> {
|
||||
self.imp.find(col_val)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ctx> AssetObjectAssetObjectIdUnique<'ctx> {
|
||||
/// Find the subscribed row whose `asset_object_id` column value is equal to `col_val`,
|
||||
/// if such a row is present in the client cache.
|
||||
pub fn find(&self, col_val: &String) -> Option<AssetObject> {
|
||||
self.imp.find(col_val)
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub(super) fn register_table(client_cache: &mut __sdk::ClientCache<super::RemoteModule>) {
|
||||
|
||||
let _table = client_cache.get_or_make_table::<AssetObject>("asset_object");
|
||||
_table.add_unique_constraint::<String>("asset_object_id", |row| &row.asset_object_id);
|
||||
}
|
||||
@@ -139,26 +137,24 @@ pub(super) fn parse_table_update(
|
||||
raw_updates: __ws::v2::TableUpdate,
|
||||
) -> __sdk::Result<__sdk::TableUpdate<AssetObject>> {
|
||||
__sdk::TableUpdate::parse_table_update(raw_updates).map_err(|e| {
|
||||
__sdk::InternalError::failed_parse(
|
||||
"TableUpdate<AssetObject>",
|
||||
"TableUpdate",
|
||||
).with_cause(e).into()
|
||||
__sdk::InternalError::failed_parse("TableUpdate<AssetObject>", "TableUpdate")
|
||||
.with_cause(e)
|
||||
.into()
|
||||
})
|
||||
}
|
||||
|
||||
#[allow(non_camel_case_types)]
|
||||
/// Extension trait for query builder access to the table `AssetObject`.
|
||||
///
|
||||
/// Implemented for [`__sdk::QueryTableAccessor`].
|
||||
pub trait asset_objectQueryTableAccess {
|
||||
#[allow(non_snake_case)]
|
||||
/// Get a query builder for the table `AssetObject`.
|
||||
fn asset_object(&self) -> __sdk::__query_builder::Table<AssetObject>;
|
||||
}
|
||||
|
||||
impl asset_objectQueryTableAccess for __sdk::QueryTableAccessor {
|
||||
fn asset_object(&self) -> __sdk::__query_builder::Table<AssetObject> {
|
||||
__sdk::__query_builder::Table::new("asset_object")
|
||||
}
|
||||
}
|
||||
#[allow(non_camel_case_types)]
|
||||
/// Extension trait for query builder access to the table `AssetObject`.
|
||||
///
|
||||
/// Implemented for [`__sdk::QueryTableAccessor`].
|
||||
pub trait asset_objectQueryTableAccess {
|
||||
#[allow(non_snake_case)]
|
||||
/// Get a query builder for the table `AssetObject`.
|
||||
fn asset_object(&self) -> __sdk::__query_builder::Table<AssetObject>;
|
||||
}
|
||||
|
||||
impl asset_objectQueryTableAccess for __sdk::QueryTableAccessor {
|
||||
fn asset_object(&self) -> __sdk::__query_builder::Table<AssetObject> {
|
||||
__sdk::__query_builder::Table::new("asset_object")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,12 +2,7 @@
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
#![allow(unused, clippy::all)]
|
||||
use spacetimedb_sdk::__codegen::{
|
||||
self as __sdk,
|
||||
__lib,
|
||||
__sats,
|
||||
__ws,
|
||||
};
|
||||
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
||||
|
||||
use super::asset_object_access_policy_type::AssetObjectAccessPolicy;
|
||||
|
||||
@@ -18,25 +13,23 @@ pub struct AssetObject {
|
||||
pub bucket: String,
|
||||
pub object_key: String,
|
||||
pub access_policy: AssetObjectAccessPolicy,
|
||||
pub content_type: Option::<String>,
|
||||
pub content_type: Option<String>,
|
||||
pub content_length: u64,
|
||||
pub content_hash: Option::<String>,
|
||||
pub content_hash: Option<String>,
|
||||
pub version: u32,
|
||||
pub source_job_id: Option::<String>,
|
||||
pub owner_user_id: Option::<String>,
|
||||
pub profile_id: Option::<String>,
|
||||
pub entity_id: Option::<String>,
|
||||
pub source_job_id: Option<String>,
|
||||
pub owner_user_id: Option<String>,
|
||||
pub profile_id: Option<String>,
|
||||
pub entity_id: Option<String>,
|
||||
pub asset_kind: String,
|
||||
pub created_at: __sdk::Timestamp,
|
||||
pub updated_at: __sdk::Timestamp,
|
||||
}
|
||||
|
||||
|
||||
impl __sdk::InModule for AssetObject {
|
||||
type Module = super::RemoteModule;
|
||||
}
|
||||
|
||||
|
||||
/// Column accessor struct for the table `AssetObject`.
|
||||
///
|
||||
/// Provides typed access to columns for query building.
|
||||
@@ -45,14 +38,14 @@ pub struct AssetObjectCols {
|
||||
pub bucket: __sdk::__query_builder::Col<AssetObject, String>,
|
||||
pub object_key: __sdk::__query_builder::Col<AssetObject, String>,
|
||||
pub access_policy: __sdk::__query_builder::Col<AssetObject, AssetObjectAccessPolicy>,
|
||||
pub content_type: __sdk::__query_builder::Col<AssetObject, Option::<String>>,
|
||||
pub content_type: __sdk::__query_builder::Col<AssetObject, Option<String>>,
|
||||
pub content_length: __sdk::__query_builder::Col<AssetObject, u64>,
|
||||
pub content_hash: __sdk::__query_builder::Col<AssetObject, Option::<String>>,
|
||||
pub content_hash: __sdk::__query_builder::Col<AssetObject, Option<String>>,
|
||||
pub version: __sdk::__query_builder::Col<AssetObject, u32>,
|
||||
pub source_job_id: __sdk::__query_builder::Col<AssetObject, Option::<String>>,
|
||||
pub owner_user_id: __sdk::__query_builder::Col<AssetObject, Option::<String>>,
|
||||
pub profile_id: __sdk::__query_builder::Col<AssetObject, Option::<String>>,
|
||||
pub entity_id: __sdk::__query_builder::Col<AssetObject, Option::<String>>,
|
||||
pub source_job_id: __sdk::__query_builder::Col<AssetObject, Option<String>>,
|
||||
pub owner_user_id: __sdk::__query_builder::Col<AssetObject, Option<String>>,
|
||||
pub profile_id: __sdk::__query_builder::Col<AssetObject, Option<String>>,
|
||||
pub entity_id: __sdk::__query_builder::Col<AssetObject, Option<String>>,
|
||||
pub asset_kind: __sdk::__query_builder::Col<AssetObject, String>,
|
||||
pub created_at: __sdk::__query_builder::Col<AssetObject, __sdk::Timestamp>,
|
||||
pub updated_at: __sdk::__query_builder::Col<AssetObject, __sdk::Timestamp>,
|
||||
@@ -77,7 +70,6 @@ impl __sdk::__query_builder::HasCols for AssetObject {
|
||||
asset_kind: __sdk::__query_builder::Col::new(table_name, "asset_kind"),
|
||||
created_at: __sdk::__query_builder::Col::new(table_name, "created_at"),
|
||||
updated_at: __sdk::__query_builder::Col::new(table_name, "updated_at"),
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -96,10 +88,8 @@ impl __sdk::__query_builder::HasIxCols for AssetObject {
|
||||
AssetObjectIxCols {
|
||||
asset_kind: __sdk::__query_builder::IxCol::new(table_name, "asset_kind"),
|
||||
asset_object_id: __sdk::__query_builder::IxCol::new(table_name, "asset_object_id"),
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl __sdk::__query_builder::CanBeLookupTable for AssetObject {}
|
||||
|
||||
|
||||
@@ -2,12 +2,7 @@
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
#![allow(unused, clippy::all)]
|
||||
use spacetimedb_sdk::__codegen::{
|
||||
self as __sdk,
|
||||
__lib,
|
||||
__sats,
|
||||
__ws,
|
||||
};
|
||||
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
||||
|
||||
use super::asset_object_access_policy_type::AssetObjectAccessPolicy;
|
||||
|
||||
@@ -18,20 +13,18 @@ pub struct AssetObjectUpsertInput {
|
||||
pub bucket: String,
|
||||
pub object_key: String,
|
||||
pub access_policy: AssetObjectAccessPolicy,
|
||||
pub content_type: Option::<String>,
|
||||
pub content_type: Option<String>,
|
||||
pub content_length: u64,
|
||||
pub content_hash: Option::<String>,
|
||||
pub content_hash: Option<String>,
|
||||
pub version: u32,
|
||||
pub source_job_id: Option::<String>,
|
||||
pub owner_user_id: Option::<String>,
|
||||
pub profile_id: Option::<String>,
|
||||
pub entity_id: Option::<String>,
|
||||
pub source_job_id: Option<String>,
|
||||
pub owner_user_id: Option<String>,
|
||||
pub profile_id: Option<String>,
|
||||
pub entity_id: Option<String>,
|
||||
pub asset_kind: String,
|
||||
pub updated_at_micros: i64,
|
||||
}
|
||||
|
||||
|
||||
impl __sdk::InModule for AssetObjectUpsertInput {
|
||||
type Module = super::RemoteModule;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,12 +2,7 @@
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
#![allow(unused, clippy::all)]
|
||||
use spacetimedb_sdk::__codegen::{
|
||||
self as __sdk,
|
||||
__lib,
|
||||
__sats,
|
||||
__ws,
|
||||
};
|
||||
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
||||
|
||||
use super::asset_object_access_policy_type::AssetObjectAccessPolicy;
|
||||
|
||||
@@ -18,21 +13,19 @@ pub struct AssetObjectUpsertSnapshot {
|
||||
pub bucket: String,
|
||||
pub object_key: String,
|
||||
pub access_policy: AssetObjectAccessPolicy,
|
||||
pub content_type: Option::<String>,
|
||||
pub content_type: Option<String>,
|
||||
pub content_length: u64,
|
||||
pub content_hash: Option::<String>,
|
||||
pub content_hash: Option<String>,
|
||||
pub version: u32,
|
||||
pub source_job_id: Option::<String>,
|
||||
pub owner_user_id: Option::<String>,
|
||||
pub profile_id: Option::<String>,
|
||||
pub entity_id: Option::<String>,
|
||||
pub source_job_id: Option<String>,
|
||||
pub owner_user_id: Option<String>,
|
||||
pub profile_id: Option<String>,
|
||||
pub entity_id: Option<String>,
|
||||
pub asset_kind: String,
|
||||
pub created_at_micros: i64,
|
||||
pub updated_at_micros: i64,
|
||||
}
|
||||
|
||||
|
||||
impl __sdk::InModule for AssetObjectUpsertSnapshot {
|
||||
type Module = super::RemoteModule;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,23 +2,17 @@
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
#![allow(unused, clippy::all)]
|
||||
use spacetimedb_sdk::__codegen::{
|
||||
self as __sdk,
|
||||
__lib,
|
||||
__sats,
|
||||
__ws,
|
||||
};
|
||||
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
||||
|
||||
use super::ai_task_procedure_result_type::AiTaskProcedureResult;
|
||||
use super::ai_result_reference_input_type::AiResultReferenceInput;
|
||||
use super::ai_task_procedure_result_type::AiTaskProcedureResult;
|
||||
|
||||
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
|
||||
#[sats(crate = __lib)]
|
||||
struct AttachAiResultReferenceAndReturnArgs {
|
||||
struct AttachAiResultReferenceAndReturnArgs {
|
||||
pub input: AiResultReferenceInput,
|
||||
}
|
||||
|
||||
|
||||
impl __sdk::InModule for AttachAiResultReferenceAndReturnArgs {
|
||||
type Module = super::RemoteModule;
|
||||
}
|
||||
@@ -28,16 +22,19 @@ impl __sdk::InModule for AttachAiResultReferenceAndReturnArgs {
|
||||
///
|
||||
/// Implemented for [`super::RemoteProcedures`].
|
||||
pub trait attach_ai_result_reference_and_return {
|
||||
fn attach_ai_result_reference_and_return(&self, input: AiResultReferenceInput,
|
||||
) {
|
||||
self.attach_ai_result_reference_and_return_then(input, |_, _| {});
|
||||
fn attach_ai_result_reference_and_return(&self, input: AiResultReferenceInput) {
|
||||
self.attach_ai_result_reference_and_return_then(input, |_, _| {});
|
||||
}
|
||||
|
||||
fn attach_ai_result_reference_and_return_then(
|
||||
&self,
|
||||
input: AiResultReferenceInput,
|
||||
|
||||
__callback: impl FnOnce(&super::ProcedureEventContext, Result<AiTaskProcedureResult, __sdk::InternalError>) + Send + 'static,
|
||||
__callback: impl FnOnce(
|
||||
&super::ProcedureEventContext,
|
||||
Result<AiTaskProcedureResult, __sdk::InternalError>,
|
||||
) + Send
|
||||
+ 'static,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -46,13 +43,17 @@ impl attach_ai_result_reference_and_return for super::RemoteProcedures {
|
||||
&self,
|
||||
input: AiResultReferenceInput,
|
||||
|
||||
__callback: impl FnOnce(&super::ProcedureEventContext, Result<AiTaskProcedureResult, __sdk::InternalError>) + Send + 'static,
|
||||
__callback: impl FnOnce(
|
||||
&super::ProcedureEventContext,
|
||||
Result<AiTaskProcedureResult, __sdk::InternalError>,
|
||||
) + Send
|
||||
+ 'static,
|
||||
) {
|
||||
self.imp.invoke_procedure_with_callback::<_, AiTaskProcedureResult>(
|
||||
"attach_ai_result_reference_and_return",
|
||||
AttachAiResultReferenceAndReturnArgs { input, },
|
||||
__callback,
|
||||
);
|
||||
self.imp
|
||||
.invoke_procedure_with_callback::<_, AiTaskProcedureResult>(
|
||||
"attach_ai_result_reference_and_return",
|
||||
AttachAiResultReferenceAndReturnArgs { input },
|
||||
__callback,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,12 +2,7 @@
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
#![allow(unused, clippy::all)]
|
||||
use spacetimedb_sdk::__codegen::{
|
||||
self as __sdk,
|
||||
__lib,
|
||||
__sats,
|
||||
__ws,
|
||||
};
|
||||
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
||||
|
||||
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
|
||||
#[sats(crate = __lib)]
|
||||
@@ -16,12 +11,8 @@ pub enum BattleMode {
|
||||
Fight,
|
||||
|
||||
Spar,
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
impl __sdk::InModule for BattleMode {
|
||||
type Module = super::RemoteModule;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,12 +2,7 @@
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
#![allow(unused, clippy::all)]
|
||||
use spacetimedb_sdk::__codegen::{
|
||||
self as __sdk,
|
||||
__lib,
|
||||
__sats,
|
||||
__ws,
|
||||
};
|
||||
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
||||
|
||||
use super::battle_mode_type::BattleMode;
|
||||
use super::runtime_item_reward_item_snapshot_type::RuntimeItemRewardItemSnapshot;
|
||||
@@ -19,7 +14,7 @@ pub struct BattleStateInput {
|
||||
pub story_session_id: String,
|
||||
pub runtime_session_id: String,
|
||||
pub actor_user_id: String,
|
||||
pub chapter_id: Option::<String>,
|
||||
pub chapter_id: Option<String>,
|
||||
pub target_npc_id: String,
|
||||
pub target_name: String,
|
||||
pub battle_mode: BattleMode,
|
||||
@@ -30,12 +25,10 @@ pub struct BattleStateInput {
|
||||
pub target_hp: i32,
|
||||
pub target_max_hp: i32,
|
||||
pub experience_reward: u32,
|
||||
pub reward_items: Vec::<RuntimeItemRewardItemSnapshot>,
|
||||
pub reward_items: Vec<RuntimeItemRewardItemSnapshot>,
|
||||
pub created_at_micros: i64,
|
||||
}
|
||||
|
||||
|
||||
impl __sdk::InModule for BattleStateInput {
|
||||
type Module = super::RemoteModule;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,12 +2,7 @@
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
#![allow(unused, clippy::all)]
|
||||
use spacetimedb_sdk::__codegen::{
|
||||
self as __sdk,
|
||||
__lib,
|
||||
__sats,
|
||||
__ws,
|
||||
};
|
||||
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
||||
|
||||
use super::battle_state_snapshot_type::BattleStateSnapshot;
|
||||
|
||||
@@ -15,12 +10,10 @@ use super::battle_state_snapshot_type::BattleStateSnapshot;
|
||||
#[sats(crate = __lib)]
|
||||
pub struct BattleStateProcedureResult {
|
||||
pub ok: bool,
|
||||
pub snapshot: Option::<BattleStateSnapshot>,
|
||||
pub error_message: Option::<String>,
|
||||
pub snapshot: Option<BattleStateSnapshot>,
|
||||
pub error_message: Option<String>,
|
||||
}
|
||||
|
||||
|
||||
impl __sdk::InModule for BattleStateProcedureResult {
|
||||
type Module = super::RemoteModule;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,13 +2,7 @@
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
#![allow(unused, clippy::all)]
|
||||
use spacetimedb_sdk::__codegen::{
|
||||
self as __sdk,
|
||||
__lib,
|
||||
__sats,
|
||||
__ws,
|
||||
};
|
||||
|
||||
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
||||
|
||||
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
|
||||
#[sats(crate = __lib)]
|
||||
@@ -16,8 +10,6 @@ pub struct BattleStateQueryInput {
|
||||
pub battle_state_id: String,
|
||||
}
|
||||
|
||||
|
||||
impl __sdk::InModule for BattleStateQueryInput {
|
||||
type Module = super::RemoteModule;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,17 +2,12 @@
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
#![allow(unused, clippy::all)]
|
||||
use spacetimedb_sdk::__codegen::{
|
||||
self as __sdk,
|
||||
__lib,
|
||||
__sats,
|
||||
__ws,
|
||||
};
|
||||
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
||||
|
||||
use super::battle_mode_type::BattleMode;
|
||||
use super::battle_status_type::BattleStatus;
|
||||
use super::runtime_item_reward_item_snapshot_type::RuntimeItemRewardItemSnapshot;
|
||||
use super::combat_outcome_type::CombatOutcome;
|
||||
use super::runtime_item_reward_item_snapshot_type::RuntimeItemRewardItemSnapshot;
|
||||
|
||||
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
|
||||
#[sats(crate = __lib)]
|
||||
@@ -21,7 +16,7 @@ pub struct BattleStateSnapshot {
|
||||
pub story_session_id: String,
|
||||
pub runtime_session_id: String,
|
||||
pub actor_user_id: String,
|
||||
pub chapter_id: Option::<String>,
|
||||
pub chapter_id: Option<String>,
|
||||
pub target_npc_id: String,
|
||||
pub target_name: String,
|
||||
pub battle_mode: BattleMode,
|
||||
@@ -33,11 +28,11 @@ pub struct BattleStateSnapshot {
|
||||
pub target_hp: i32,
|
||||
pub target_max_hp: i32,
|
||||
pub experience_reward: u32,
|
||||
pub reward_items: Vec::<RuntimeItemRewardItemSnapshot>,
|
||||
pub reward_items: Vec<RuntimeItemRewardItemSnapshot>,
|
||||
pub turn_index: u32,
|
||||
pub last_action_function_id: Option::<String>,
|
||||
pub last_action_text: Option::<String>,
|
||||
pub last_result_text: Option::<String>,
|
||||
pub last_action_function_id: Option<String>,
|
||||
pub last_action_text: Option<String>,
|
||||
pub last_result_text: Option<String>,
|
||||
pub last_damage_dealt: i32,
|
||||
pub last_damage_taken: i32,
|
||||
pub last_outcome: CombatOutcome,
|
||||
@@ -46,8 +41,6 @@ pub struct BattleStateSnapshot {
|
||||
pub updated_at_micros: i64,
|
||||
}
|
||||
|
||||
|
||||
impl __sdk::InModule for BattleStateSnapshot {
|
||||
type Module = super::RemoteModule;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,17 +2,12 @@
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
#![allow(unused, clippy::all)]
|
||||
use spacetimedb_sdk::__codegen::{
|
||||
self as __sdk,
|
||||
__lib,
|
||||
__sats,
|
||||
__ws,
|
||||
};
|
||||
use super::battle_state_type::BattleState;
|
||||
use super::battle_mode_type::BattleMode;
|
||||
use super::battle_state_type::BattleState;
|
||||
use super::battle_status_type::BattleStatus;
|
||||
use super::runtime_item_reward_item_snapshot_type::RuntimeItemRewardItemSnapshot;
|
||||
use super::combat_outcome_type::CombatOutcome;
|
||||
use super::runtime_item_reward_item_snapshot_type::RuntimeItemRewardItemSnapshot;
|
||||
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
||||
|
||||
/// Table handle for the table `battle_state`.
|
||||
///
|
||||
@@ -53,8 +48,12 @@ impl<'ctx> __sdk::Table for BattleStateTableHandle<'ctx> {
|
||||
type Row = BattleState;
|
||||
type EventContext = super::EventContext;
|
||||
|
||||
fn count(&self) -> u64 { self.imp.count() }
|
||||
fn iter(&self) -> impl Iterator<Item = BattleState> + '_ { self.imp.iter() }
|
||||
fn count(&self) -> u64 {
|
||||
self.imp.count()
|
||||
}
|
||||
fn iter(&self) -> impl Iterator<Item = BattleState> + '_ {
|
||||
self.imp.iter()
|
||||
}
|
||||
|
||||
type InsertCallbackId = BattleStateInsertCallbackId;
|
||||
|
||||
@@ -100,39 +99,38 @@ impl<'ctx> __sdk::TableWithPrimaryKey for BattleStateTableHandle<'ctx> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Access to the `battle_state_id` unique index on the table `battle_state`,
|
||||
/// which allows point queries on the field of the same name
|
||||
/// via the [`BattleStateBattleStateIdUnique::find`] method.
|
||||
///
|
||||
/// Users are encouraged not to explicitly reference this type,
|
||||
/// but to directly chain method calls,
|
||||
/// like `ctx.db.battle_state().battle_state_id().find(...)`.
|
||||
pub struct BattleStateBattleStateIdUnique<'ctx> {
|
||||
imp: __sdk::UniqueConstraintHandle<BattleState, String>,
|
||||
phantom: std::marker::PhantomData<&'ctx super::RemoteTables>,
|
||||
}
|
||||
/// Access to the `battle_state_id` unique index on the table `battle_state`,
|
||||
/// which allows point queries on the field of the same name
|
||||
/// via the [`BattleStateBattleStateIdUnique::find`] method.
|
||||
///
|
||||
/// Users are encouraged not to explicitly reference this type,
|
||||
/// but to directly chain method calls,
|
||||
/// like `ctx.db.battle_state().battle_state_id().find(...)`.
|
||||
pub struct BattleStateBattleStateIdUnique<'ctx> {
|
||||
imp: __sdk::UniqueConstraintHandle<BattleState, String>,
|
||||
phantom: std::marker::PhantomData<&'ctx super::RemoteTables>,
|
||||
}
|
||||
|
||||
impl<'ctx> BattleStateTableHandle<'ctx> {
|
||||
/// Get a handle on the `battle_state_id` unique index on the table `battle_state`.
|
||||
pub fn battle_state_id(&self) -> BattleStateBattleStateIdUnique<'ctx> {
|
||||
BattleStateBattleStateIdUnique {
|
||||
imp: self.imp.get_unique_constraint::<String>("battle_state_id"),
|
||||
phantom: std::marker::PhantomData,
|
||||
}
|
||||
}
|
||||
impl<'ctx> BattleStateTableHandle<'ctx> {
|
||||
/// Get a handle on the `battle_state_id` unique index on the table `battle_state`.
|
||||
pub fn battle_state_id(&self) -> BattleStateBattleStateIdUnique<'ctx> {
|
||||
BattleStateBattleStateIdUnique {
|
||||
imp: self.imp.get_unique_constraint::<String>("battle_state_id"),
|
||||
phantom: std::marker::PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ctx> BattleStateBattleStateIdUnique<'ctx> {
|
||||
/// Find the subscribed row whose `battle_state_id` column value is equal to `col_val`,
|
||||
/// if such a row is present in the client cache.
|
||||
pub fn find(&self, col_val: &String) -> Option<BattleState> {
|
||||
self.imp.find(col_val)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ctx> BattleStateBattleStateIdUnique<'ctx> {
|
||||
/// Find the subscribed row whose `battle_state_id` column value is equal to `col_val`,
|
||||
/// if such a row is present in the client cache.
|
||||
pub fn find(&self, col_val: &String) -> Option<BattleState> {
|
||||
self.imp.find(col_val)
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub(super) fn register_table(client_cache: &mut __sdk::ClientCache<super::RemoteModule>) {
|
||||
|
||||
let _table = client_cache.get_or_make_table::<BattleState>("battle_state");
|
||||
_table.add_unique_constraint::<String>("battle_state_id", |row| &row.battle_state_id);
|
||||
}
|
||||
@@ -142,26 +140,24 @@ pub(super) fn parse_table_update(
|
||||
raw_updates: __ws::v2::TableUpdate,
|
||||
) -> __sdk::Result<__sdk::TableUpdate<BattleState>> {
|
||||
__sdk::TableUpdate::parse_table_update(raw_updates).map_err(|e| {
|
||||
__sdk::InternalError::failed_parse(
|
||||
"TableUpdate<BattleState>",
|
||||
"TableUpdate",
|
||||
).with_cause(e).into()
|
||||
__sdk::InternalError::failed_parse("TableUpdate<BattleState>", "TableUpdate")
|
||||
.with_cause(e)
|
||||
.into()
|
||||
})
|
||||
}
|
||||
|
||||
#[allow(non_camel_case_types)]
|
||||
/// Extension trait for query builder access to the table `BattleState`.
|
||||
///
|
||||
/// Implemented for [`__sdk::QueryTableAccessor`].
|
||||
pub trait battle_stateQueryTableAccess {
|
||||
#[allow(non_snake_case)]
|
||||
/// Get a query builder for the table `BattleState`.
|
||||
fn battle_state(&self) -> __sdk::__query_builder::Table<BattleState>;
|
||||
}
|
||||
|
||||
impl battle_stateQueryTableAccess for __sdk::QueryTableAccessor {
|
||||
fn battle_state(&self) -> __sdk::__query_builder::Table<BattleState> {
|
||||
__sdk::__query_builder::Table::new("battle_state")
|
||||
}
|
||||
}
|
||||
#[allow(non_camel_case_types)]
|
||||
/// Extension trait for query builder access to the table `BattleState`.
|
||||
///
|
||||
/// Implemented for [`__sdk::QueryTableAccessor`].
|
||||
pub trait battle_stateQueryTableAccess {
|
||||
#[allow(non_snake_case)]
|
||||
/// Get a query builder for the table `BattleState`.
|
||||
fn battle_state(&self) -> __sdk::__query_builder::Table<BattleState>;
|
||||
}
|
||||
|
||||
impl battle_stateQueryTableAccess for __sdk::QueryTableAccessor {
|
||||
fn battle_state(&self) -> __sdk::__query_builder::Table<BattleState> {
|
||||
__sdk::__query_builder::Table::new("battle_state")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,17 +2,12 @@
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
#![allow(unused, clippy::all)]
|
||||
use spacetimedb_sdk::__codegen::{
|
||||
self as __sdk,
|
||||
__lib,
|
||||
__sats,
|
||||
__ws,
|
||||
};
|
||||
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
||||
|
||||
use super::battle_mode_type::BattleMode;
|
||||
use super::battle_status_type::BattleStatus;
|
||||
use super::runtime_item_reward_item_snapshot_type::RuntimeItemRewardItemSnapshot;
|
||||
use super::combat_outcome_type::CombatOutcome;
|
||||
use super::runtime_item_reward_item_snapshot_type::RuntimeItemRewardItemSnapshot;
|
||||
|
||||
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
|
||||
#[sats(crate = __lib)]
|
||||
@@ -21,7 +16,7 @@ pub struct BattleState {
|
||||
pub story_session_id: String,
|
||||
pub runtime_session_id: String,
|
||||
pub actor_user_id: String,
|
||||
pub chapter_id: Option::<String>,
|
||||
pub chapter_id: Option<String>,
|
||||
pub target_npc_id: String,
|
||||
pub target_name: String,
|
||||
pub battle_mode: BattleMode,
|
||||
@@ -33,11 +28,11 @@ pub struct BattleState {
|
||||
pub target_hp: i32,
|
||||
pub target_max_hp: i32,
|
||||
pub experience_reward: u32,
|
||||
pub reward_items: Vec::<RuntimeItemRewardItemSnapshot>,
|
||||
pub reward_items: Vec<RuntimeItemRewardItemSnapshot>,
|
||||
pub turn_index: u32,
|
||||
pub last_action_function_id: Option::<String>,
|
||||
pub last_action_text: Option::<String>,
|
||||
pub last_result_text: Option::<String>,
|
||||
pub last_action_function_id: Option<String>,
|
||||
pub last_action_text: Option<String>,
|
||||
pub last_result_text: Option<String>,
|
||||
pub last_damage_dealt: i32,
|
||||
pub last_damage_taken: i32,
|
||||
pub last_outcome: CombatOutcome,
|
||||
@@ -46,12 +41,10 @@ pub struct BattleState {
|
||||
pub updated_at: __sdk::Timestamp,
|
||||
}
|
||||
|
||||
|
||||
impl __sdk::InModule for BattleState {
|
||||
type Module = super::RemoteModule;
|
||||
}
|
||||
|
||||
|
||||
/// Column accessor struct for the table `BattleState`.
|
||||
///
|
||||
/// Provides typed access to columns for query building.
|
||||
@@ -60,7 +53,7 @@ pub struct BattleStateCols {
|
||||
pub story_session_id: __sdk::__query_builder::Col<BattleState, String>,
|
||||
pub runtime_session_id: __sdk::__query_builder::Col<BattleState, String>,
|
||||
pub actor_user_id: __sdk::__query_builder::Col<BattleState, String>,
|
||||
pub chapter_id: __sdk::__query_builder::Col<BattleState, Option::<String>>,
|
||||
pub chapter_id: __sdk::__query_builder::Col<BattleState, Option<String>>,
|
||||
pub target_npc_id: __sdk::__query_builder::Col<BattleState, String>,
|
||||
pub target_name: __sdk::__query_builder::Col<BattleState, String>,
|
||||
pub battle_mode: __sdk::__query_builder::Col<BattleState, BattleMode>,
|
||||
@@ -72,11 +65,11 @@ pub struct BattleStateCols {
|
||||
pub target_hp: __sdk::__query_builder::Col<BattleState, i32>,
|
||||
pub target_max_hp: __sdk::__query_builder::Col<BattleState, i32>,
|
||||
pub experience_reward: __sdk::__query_builder::Col<BattleState, u32>,
|
||||
pub reward_items: __sdk::__query_builder::Col<BattleState, Vec::<RuntimeItemRewardItemSnapshot>>,
|
||||
pub reward_items: __sdk::__query_builder::Col<BattleState, Vec<RuntimeItemRewardItemSnapshot>>,
|
||||
pub turn_index: __sdk::__query_builder::Col<BattleState, u32>,
|
||||
pub last_action_function_id: __sdk::__query_builder::Col<BattleState, Option::<String>>,
|
||||
pub last_action_text: __sdk::__query_builder::Col<BattleState, Option::<String>>,
|
||||
pub last_result_text: __sdk::__query_builder::Col<BattleState, Option::<String>>,
|
||||
pub last_action_function_id: __sdk::__query_builder::Col<BattleState, Option<String>>,
|
||||
pub last_action_text: __sdk::__query_builder::Col<BattleState, Option<String>>,
|
||||
pub last_result_text: __sdk::__query_builder::Col<BattleState, Option<String>>,
|
||||
pub last_damage_dealt: __sdk::__query_builder::Col<BattleState, i32>,
|
||||
pub last_damage_taken: __sdk::__query_builder::Col<BattleState, i32>,
|
||||
pub last_outcome: __sdk::__query_builder::Col<BattleState, CombatOutcome>,
|
||||
@@ -107,7 +100,10 @@ impl __sdk::__query_builder::HasCols for BattleState {
|
||||
experience_reward: __sdk::__query_builder::Col::new(table_name, "experience_reward"),
|
||||
reward_items: __sdk::__query_builder::Col::new(table_name, "reward_items"),
|
||||
turn_index: __sdk::__query_builder::Col::new(table_name, "turn_index"),
|
||||
last_action_function_id: __sdk::__query_builder::Col::new(table_name, "last_action_function_id"),
|
||||
last_action_function_id: __sdk::__query_builder::Col::new(
|
||||
table_name,
|
||||
"last_action_function_id",
|
||||
),
|
||||
last_action_text: __sdk::__query_builder::Col::new(table_name, "last_action_text"),
|
||||
last_result_text: __sdk::__query_builder::Col::new(table_name, "last_result_text"),
|
||||
last_damage_dealt: __sdk::__query_builder::Col::new(table_name, "last_damage_dealt"),
|
||||
@@ -116,7 +112,6 @@ impl __sdk::__query_builder::HasCols for BattleState {
|
||||
version: __sdk::__query_builder::Col::new(table_name, "version"),
|
||||
created_at: __sdk::__query_builder::Col::new(table_name, "created_at"),
|
||||
updated_at: __sdk::__query_builder::Col::new(table_name, "updated_at"),
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -137,12 +132,13 @@ impl __sdk::__query_builder::HasIxCols for BattleState {
|
||||
BattleStateIxCols {
|
||||
actor_user_id: __sdk::__query_builder::IxCol::new(table_name, "actor_user_id"),
|
||||
battle_state_id: __sdk::__query_builder::IxCol::new(table_name, "battle_state_id"),
|
||||
runtime_session_id: __sdk::__query_builder::IxCol::new(table_name, "runtime_session_id"),
|
||||
runtime_session_id: __sdk::__query_builder::IxCol::new(
|
||||
table_name,
|
||||
"runtime_session_id",
|
||||
),
|
||||
story_session_id: __sdk::__query_builder::IxCol::new(table_name, "story_session_id"),
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl __sdk::__query_builder::CanBeLookupTable for BattleState {}
|
||||
|
||||
|
||||
@@ -2,12 +2,7 @@
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
#![allow(unused, clippy::all)]
|
||||
use spacetimedb_sdk::__codegen::{
|
||||
self as __sdk,
|
||||
__lib,
|
||||
__sats,
|
||||
__ws,
|
||||
};
|
||||
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
||||
|
||||
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
|
||||
#[sats(crate = __lib)]
|
||||
@@ -18,12 +13,8 @@ pub enum BattleStatus {
|
||||
Resolved,
|
||||
|
||||
Aborted,
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
impl __sdk::InModule for BattleStatus {
|
||||
type Module = super::RemoteModule;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,23 +2,17 @@
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
#![allow(unused, clippy::all)]
|
||||
use spacetimedb_sdk::__codegen::{
|
||||
self as __sdk,
|
||||
__lib,
|
||||
__sats,
|
||||
__ws,
|
||||
};
|
||||
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
||||
|
||||
use super::story_session_input_type::StorySessionInput;
|
||||
use super::story_session_procedure_result_type::StorySessionProcedureResult;
|
||||
|
||||
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
|
||||
#[sats(crate = __lib)]
|
||||
struct BeginStorySessionAndReturnArgs {
|
||||
struct BeginStorySessionAndReturnArgs {
|
||||
pub input: StorySessionInput,
|
||||
}
|
||||
|
||||
|
||||
impl __sdk::InModule for BeginStorySessionAndReturnArgs {
|
||||
type Module = super::RemoteModule;
|
||||
}
|
||||
@@ -28,16 +22,19 @@ impl __sdk::InModule for BeginStorySessionAndReturnArgs {
|
||||
///
|
||||
/// Implemented for [`super::RemoteProcedures`].
|
||||
pub trait begin_story_session_and_return {
|
||||
fn begin_story_session_and_return(&self, input: StorySessionInput,
|
||||
) {
|
||||
self.begin_story_session_and_return_then(input, |_, _| {});
|
||||
fn begin_story_session_and_return(&self, input: StorySessionInput) {
|
||||
self.begin_story_session_and_return_then(input, |_, _| {});
|
||||
}
|
||||
|
||||
fn begin_story_session_and_return_then(
|
||||
&self,
|
||||
input: StorySessionInput,
|
||||
|
||||
__callback: impl FnOnce(&super::ProcedureEventContext, Result<StorySessionProcedureResult, __sdk::InternalError>) + Send + 'static,
|
||||
__callback: impl FnOnce(
|
||||
&super::ProcedureEventContext,
|
||||
Result<StorySessionProcedureResult, __sdk::InternalError>,
|
||||
) + Send
|
||||
+ 'static,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -46,13 +43,17 @@ impl begin_story_session_and_return for super::RemoteProcedures {
|
||||
&self,
|
||||
input: StorySessionInput,
|
||||
|
||||
__callback: impl FnOnce(&super::ProcedureEventContext, Result<StorySessionProcedureResult, __sdk::InternalError>) + Send + 'static,
|
||||
__callback: impl FnOnce(
|
||||
&super::ProcedureEventContext,
|
||||
Result<StorySessionProcedureResult, __sdk::InternalError>,
|
||||
) + Send
|
||||
+ 'static,
|
||||
) {
|
||||
self.imp.invoke_procedure_with_callback::<_, StorySessionProcedureResult>(
|
||||
"begin_story_session_and_return",
|
||||
BeginStorySessionAndReturnArgs { input, },
|
||||
__callback,
|
||||
);
|
||||
self.imp
|
||||
.invoke_procedure_with_callback::<_, StorySessionProcedureResult>(
|
||||
"begin_story_session_and_return",
|
||||
BeginStorySessionAndReturnArgs { input },
|
||||
__callback,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,12 +2,7 @@
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
#![allow(unused, clippy::all)]
|
||||
use spacetimedb_sdk::__codegen::{
|
||||
self as __sdk,
|
||||
__lib,
|
||||
__sats,
|
||||
__ws,
|
||||
};
|
||||
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
||||
|
||||
use super::story_session_input_type::StorySessionInput;
|
||||
|
||||
@@ -19,10 +14,8 @@ pub(super) struct BeginStorySessionArgs {
|
||||
|
||||
impl From<BeginStorySessionArgs> for super::Reducer {
|
||||
fn from(args: BeginStorySessionArgs) -> Self {
|
||||
Self::BeginStorySession {
|
||||
input: args.input,
|
||||
}
|
||||
}
|
||||
Self::BeginStorySession { input: args.input }
|
||||
}
|
||||
}
|
||||
|
||||
impl __sdk::InModule for BeginStorySessionArgs {
|
||||
@@ -40,9 +33,8 @@ pub trait begin_story_session {
|
||||
/// The reducer will run asynchronously in the future,
|
||||
/// and this method provides no way to listen for its completion status.
|
||||
/// /// Use [`begin_story_session:begin_story_session_then`] to run a callback after the reducer completes.
|
||||
fn begin_story_session(&self, input: StorySessionInput,
|
||||
) -> __sdk::Result<()> {
|
||||
self.begin_story_session_then(input, |_, _| {})
|
||||
fn begin_story_session(&self, input: StorySessionInput) -> __sdk::Result<()> {
|
||||
self.begin_story_session_then(input, |_, _| {})
|
||||
}
|
||||
|
||||
/// Request that the remote module invoke the reducer `begin_story_session` to run as soon as possible,
|
||||
@@ -55,9 +47,11 @@ pub trait begin_story_session {
|
||||
&self,
|
||||
input: StorySessionInput,
|
||||
|
||||
callback: impl FnOnce(&super::ReducerEventContext, Result<Result<(), String>, __sdk::InternalError>)
|
||||
+ Send
|
||||
+ 'static,
|
||||
callback: impl FnOnce(
|
||||
&super::ReducerEventContext,
|
||||
Result<Result<(), String>, __sdk::InternalError>,
|
||||
) + Send
|
||||
+ 'static,
|
||||
) -> __sdk::Result<()>;
|
||||
}
|
||||
|
||||
@@ -66,11 +60,13 @@ impl begin_story_session for super::RemoteReducers {
|
||||
&self,
|
||||
input: StorySessionInput,
|
||||
|
||||
callback: impl FnOnce(&super::ReducerEventContext, Result<Result<(), String>, __sdk::InternalError>)
|
||||
+ Send
|
||||
+ 'static,
|
||||
callback: impl FnOnce(
|
||||
&super::ReducerEventContext,
|
||||
Result<Result<(), String>, __sdk::InternalError>,
|
||||
) + Send
|
||||
+ 'static,
|
||||
) -> __sdk::Result<()> {
|
||||
self.imp.invoke_reducer_with_callback(BeginStorySessionArgs { input, }, callback)
|
||||
self.imp
|
||||
.invoke_reducer_with_callback(BeginStorySessionArgs { input }, callback)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,12 +2,7 @@
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
#![allow(unused, clippy::all)]
|
||||
use spacetimedb_sdk::__codegen::{
|
||||
self as __sdk,
|
||||
__lib,
|
||||
__sats,
|
||||
__ws,
|
||||
};
|
||||
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
||||
|
||||
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
|
||||
#[sats(crate = __lib)]
|
||||
@@ -20,12 +15,8 @@ pub enum BigFishAgentMessageKind {
|
||||
ActionResult,
|
||||
|
||||
Warning,
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
impl __sdk::InModule for BigFishAgentMessageKind {
|
||||
type Module = super::RemoteModule;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,12 +2,7 @@
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
#![allow(unused, clippy::all)]
|
||||
use spacetimedb_sdk::__codegen::{
|
||||
self as __sdk,
|
||||
__lib,
|
||||
__sats,
|
||||
__ws,
|
||||
};
|
||||
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
||||
|
||||
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
|
||||
#[sats(crate = __lib)]
|
||||
@@ -18,12 +13,8 @@ pub enum BigFishAgentMessageRole {
|
||||
Assistant,
|
||||
|
||||
System,
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
impl __sdk::InModule for BigFishAgentMessageRole {
|
||||
type Module = super::RemoteModule;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,15 +2,10 @@
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
#![allow(unused, clippy::all)]
|
||||
use spacetimedb_sdk::__codegen::{
|
||||
self as __sdk,
|
||||
__lib,
|
||||
__sats,
|
||||
__ws,
|
||||
};
|
||||
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
||||
|
||||
use super::big_fish_agent_message_role_type::BigFishAgentMessageRole;
|
||||
use super::big_fish_agent_message_kind_type::BigFishAgentMessageKind;
|
||||
use super::big_fish_agent_message_role_type::BigFishAgentMessageRole;
|
||||
|
||||
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
|
||||
#[sats(crate = __lib)]
|
||||
@@ -23,8 +18,6 @@ pub struct BigFishAgentMessageSnapshot {
|
||||
pub created_at_micros: i64,
|
||||
}
|
||||
|
||||
|
||||
impl __sdk::InModule for BigFishAgentMessageSnapshot {
|
||||
type Module = super::RemoteModule;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,15 +2,10 @@
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
#![allow(unused, clippy::all)]
|
||||
use spacetimedb_sdk::__codegen::{
|
||||
self as __sdk,
|
||||
__lib,
|
||||
__sats,
|
||||
__ws,
|
||||
};
|
||||
use super::big_fish_agent_message_type::BigFishAgentMessage;
|
||||
use super::big_fish_agent_message_role_type::BigFishAgentMessageRole;
|
||||
use super::big_fish_agent_message_kind_type::BigFishAgentMessageKind;
|
||||
use super::big_fish_agent_message_role_type::BigFishAgentMessageRole;
|
||||
use super::big_fish_agent_message_type::BigFishAgentMessage;
|
||||
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
||||
|
||||
/// Table handle for the table `big_fish_agent_message`.
|
||||
///
|
||||
@@ -38,7 +33,9 @@ pub trait BigFishAgentMessageTableAccess {
|
||||
impl BigFishAgentMessageTableAccess for super::RemoteTables {
|
||||
fn big_fish_agent_message(&self) -> BigFishAgentMessageTableHandle<'_> {
|
||||
BigFishAgentMessageTableHandle {
|
||||
imp: self.imp.get_table::<BigFishAgentMessage>("big_fish_agent_message"),
|
||||
imp: self
|
||||
.imp
|
||||
.get_table::<BigFishAgentMessage>("big_fish_agent_message"),
|
||||
ctx: std::marker::PhantomData,
|
||||
}
|
||||
}
|
||||
@@ -51,8 +48,12 @@ impl<'ctx> __sdk::Table for BigFishAgentMessageTableHandle<'ctx> {
|
||||
type Row = BigFishAgentMessage;
|
||||
type EventContext = super::EventContext;
|
||||
|
||||
fn count(&self) -> u64 { self.imp.count() }
|
||||
fn iter(&self) -> impl Iterator<Item = BigFishAgentMessage> + '_ { self.imp.iter() }
|
||||
fn count(&self) -> u64 {
|
||||
self.imp.count()
|
||||
}
|
||||
fn iter(&self) -> impl Iterator<Item = BigFishAgentMessage> + '_ {
|
||||
self.imp.iter()
|
||||
}
|
||||
|
||||
type InsertCallbackId = BigFishAgentMessageInsertCallbackId;
|
||||
|
||||
@@ -98,39 +99,38 @@ impl<'ctx> __sdk::TableWithPrimaryKey for BigFishAgentMessageTableHandle<'ctx> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Access to the `message_id` unique index on the table `big_fish_agent_message`,
|
||||
/// which allows point queries on the field of the same name
|
||||
/// via the [`BigFishAgentMessageMessageIdUnique::find`] method.
|
||||
///
|
||||
/// Users are encouraged not to explicitly reference this type,
|
||||
/// but to directly chain method calls,
|
||||
/// like `ctx.db.big_fish_agent_message().message_id().find(...)`.
|
||||
pub struct BigFishAgentMessageMessageIdUnique<'ctx> {
|
||||
imp: __sdk::UniqueConstraintHandle<BigFishAgentMessage, String>,
|
||||
phantom: std::marker::PhantomData<&'ctx super::RemoteTables>,
|
||||
}
|
||||
/// Access to the `message_id` unique index on the table `big_fish_agent_message`,
|
||||
/// which allows point queries on the field of the same name
|
||||
/// via the [`BigFishAgentMessageMessageIdUnique::find`] method.
|
||||
///
|
||||
/// Users are encouraged not to explicitly reference this type,
|
||||
/// but to directly chain method calls,
|
||||
/// like `ctx.db.big_fish_agent_message().message_id().find(...)`.
|
||||
pub struct BigFishAgentMessageMessageIdUnique<'ctx> {
|
||||
imp: __sdk::UniqueConstraintHandle<BigFishAgentMessage, String>,
|
||||
phantom: std::marker::PhantomData<&'ctx super::RemoteTables>,
|
||||
}
|
||||
|
||||
impl<'ctx> BigFishAgentMessageTableHandle<'ctx> {
|
||||
/// Get a handle on the `message_id` unique index on the table `big_fish_agent_message`.
|
||||
pub fn message_id(&self) -> BigFishAgentMessageMessageIdUnique<'ctx> {
|
||||
BigFishAgentMessageMessageIdUnique {
|
||||
imp: self.imp.get_unique_constraint::<String>("message_id"),
|
||||
phantom: std::marker::PhantomData,
|
||||
}
|
||||
}
|
||||
impl<'ctx> BigFishAgentMessageTableHandle<'ctx> {
|
||||
/// Get a handle on the `message_id` unique index on the table `big_fish_agent_message`.
|
||||
pub fn message_id(&self) -> BigFishAgentMessageMessageIdUnique<'ctx> {
|
||||
BigFishAgentMessageMessageIdUnique {
|
||||
imp: self.imp.get_unique_constraint::<String>("message_id"),
|
||||
phantom: std::marker::PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ctx> BigFishAgentMessageMessageIdUnique<'ctx> {
|
||||
/// Find the subscribed row whose `message_id` column value is equal to `col_val`,
|
||||
/// if such a row is present in the client cache.
|
||||
pub fn find(&self, col_val: &String) -> Option<BigFishAgentMessage> {
|
||||
self.imp.find(col_val)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ctx> BigFishAgentMessageMessageIdUnique<'ctx> {
|
||||
/// Find the subscribed row whose `message_id` column value is equal to `col_val`,
|
||||
/// if such a row is present in the client cache.
|
||||
pub fn find(&self, col_val: &String) -> Option<BigFishAgentMessage> {
|
||||
self.imp.find(col_val)
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub(super) fn register_table(client_cache: &mut __sdk::ClientCache<super::RemoteModule>) {
|
||||
|
||||
let _table = client_cache.get_or_make_table::<BigFishAgentMessage>("big_fish_agent_message");
|
||||
_table.add_unique_constraint::<String>("message_id", |row| &row.message_id);
|
||||
}
|
||||
@@ -140,26 +140,24 @@ pub(super) fn parse_table_update(
|
||||
raw_updates: __ws::v2::TableUpdate,
|
||||
) -> __sdk::Result<__sdk::TableUpdate<BigFishAgentMessage>> {
|
||||
__sdk::TableUpdate::parse_table_update(raw_updates).map_err(|e| {
|
||||
__sdk::InternalError::failed_parse(
|
||||
"TableUpdate<BigFishAgentMessage>",
|
||||
"TableUpdate",
|
||||
).with_cause(e).into()
|
||||
__sdk::InternalError::failed_parse("TableUpdate<BigFishAgentMessage>", "TableUpdate")
|
||||
.with_cause(e)
|
||||
.into()
|
||||
})
|
||||
}
|
||||
|
||||
#[allow(non_camel_case_types)]
|
||||
/// Extension trait for query builder access to the table `BigFishAgentMessage`.
|
||||
///
|
||||
/// Implemented for [`__sdk::QueryTableAccessor`].
|
||||
pub trait big_fish_agent_messageQueryTableAccess {
|
||||
#[allow(non_snake_case)]
|
||||
/// Get a query builder for the table `BigFishAgentMessage`.
|
||||
fn big_fish_agent_message(&self) -> __sdk::__query_builder::Table<BigFishAgentMessage>;
|
||||
}
|
||||
|
||||
impl big_fish_agent_messageQueryTableAccess for __sdk::QueryTableAccessor {
|
||||
fn big_fish_agent_message(&self) -> __sdk::__query_builder::Table<BigFishAgentMessage> {
|
||||
__sdk::__query_builder::Table::new("big_fish_agent_message")
|
||||
}
|
||||
}
|
||||
#[allow(non_camel_case_types)]
|
||||
/// Extension trait for query builder access to the table `BigFishAgentMessage`.
|
||||
///
|
||||
/// Implemented for [`__sdk::QueryTableAccessor`].
|
||||
pub trait big_fish_agent_messageQueryTableAccess {
|
||||
#[allow(non_snake_case)]
|
||||
/// Get a query builder for the table `BigFishAgentMessage`.
|
||||
fn big_fish_agent_message(&self) -> __sdk::__query_builder::Table<BigFishAgentMessage>;
|
||||
}
|
||||
|
||||
impl big_fish_agent_messageQueryTableAccess for __sdk::QueryTableAccessor {
|
||||
fn big_fish_agent_message(&self) -> __sdk::__query_builder::Table<BigFishAgentMessage> {
|
||||
__sdk::__query_builder::Table::new("big_fish_agent_message")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,15 +2,10 @@
|
||||
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
|
||||
|
||||
#![allow(unused, clippy::all)]
|
||||
use spacetimedb_sdk::__codegen::{
|
||||
self as __sdk,
|
||||
__lib,
|
||||
__sats,
|
||||
__ws,
|
||||
};
|
||||
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
|
||||
|
||||
use super::big_fish_agent_message_role_type::BigFishAgentMessageRole;
|
||||
use super::big_fish_agent_message_kind_type::BigFishAgentMessageKind;
|
||||
use super::big_fish_agent_message_role_type::BigFishAgentMessageRole;
|
||||
|
||||
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
|
||||
#[sats(crate = __lib)]
|
||||
@@ -23,12 +18,10 @@ pub struct BigFishAgentMessage {
|
||||
pub created_at: __sdk::Timestamp,
|
||||
}
|
||||
|
||||
|
||||
impl __sdk::InModule for BigFishAgentMessage {
|
||||
type Module = super::RemoteModule;
|
||||
}
|
||||
|
||||
|
||||
/// Column accessor struct for the table `BigFishAgentMessage`.
|
||||
///
|
||||
/// Provides typed access to columns for query building.
|
||||
@@ -51,7 +44,6 @@ impl __sdk::__query_builder::HasCols for BigFishAgentMessage {
|
||||
kind: __sdk::__query_builder::Col::new(table_name, "kind"),
|
||||
text: __sdk::__query_builder::Col::new(table_name, "text"),
|
||||
created_at: __sdk::__query_builder::Col::new(table_name, "created_at"),
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -70,10 +62,8 @@ impl __sdk::__query_builder::HasIxCols for BigFishAgentMessage {
|
||||
BigFishAgentMessageIxCols {
|
||||
message_id: __sdk::__query_builder::IxCol::new(table_name, "message_id"),
|
||||
session_id: __sdk::__query_builder::IxCol::new(table_name, "session_id"),
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl __sdk::__query_builder::CanBeLookupTable for BigFishAgentMessage {}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user