Merge remote-tracking branch 'origin/master' into feat/fail-as-event
Project CI / AI game creator shell Rust crates (pull_request) Successful in 1m39s
Project CI / AI game creator shell Rust smoke (pull_request) Successful in 1m53s
Project CI / Backend tests (pull_request) Successful in 5m10s
Project CI / Native shell tests (pull_request) Successful in 6m36s
Project CI / Frontend tests (pull_request) Successful in 2m6s
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Successful in 9m8s
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Successful in 10m2s
Project CI / Repository checks (pull_request) Successful in 1m53s
Project CI / AI game creator shell web tests (pull_request) Successful in 1m24s

# Conflicts:
#	docs/project-memory/shared-memory/decision-log.md
This commit is contained in:
2026-09-24 19:17:48 +08:00
30 changed files with 1745 additions and 117 deletions
@@ -1,5 +1,7 @@
# AGC 后台模型别名与对话选择
更新时间:`2026-09-24`。本次只改“目录初始值从哪来”:缺配置时不再回退写死的 `高质量 → gpt-6-astra`、`快速 → gpt-5.6-luna`,改为启动期从上游同步(这两条初始目录里的模型已从上游移除)。目录结构、后台维护字段和客户端契约都保持不变。
## 本地自定义 LLM
- 本地 `game-creator.config.json` 的 `llm.customEnabled` 默认 `false`;显式设为 `true` 后,常用设置展示 API 地址、API Key、读取模型列表与勾选区域。DirectProject 沿用 OpenAI Responses 协议,地址填写 API 根地址(例如 `https://provider.example/v1`)。开关只由配置文件控制。
@@ -33,7 +35,11 @@
## 官方路由契约
- 后台 owner 在“AGC 模型”维护列表;每项包含稳定 `id`、必填 `alias`、服务端 `modelId`、`enabled`。默认项必须启用。标识唯一,别名唯一,列表最多 32 项。
- 配置保存到私有 `agc_model_catalog` 单例表,使用 revision 乐观锁,重启及多 api-server 实例共享同一事实。缺少配置时使用初始目录,高质量对应 `gpt-6-astra`,快速对应 `gpt-5.6-luna`。
- 配置保存到私有 `agc_model_catalog` 单例表,使用 revision 乐观锁,重启及多 api-server 实例共享同一事实。
- 目录初始值来自上游同步:api-server(API/All 角色)启动时检查目录,缺失、结构与当前定义不符或校验不通过都算“未初始化”;此时调用上游 Router 控制面的分组定价列表 `GET {Router 控制面}/api/pricing?group=taonier`(控制面地址由 `{LLM Router 地址}` 去掉 `/v1` 得到;公开只读接口,不带凭据),读取 `data[].model_name` 作为“该分组可见的在售模型”,按模型名排序后生成目录:每项 `modelId` 与 `alias` 都用上游原始模型名(不再填“高质量/快速”这类人工别名),`id` 是模型名的稳定 slug(小写字母、数字、`-`、`_`,同名冲突追加 `-2`),`enabled = true`,默认项取排序后第一项,并以存量 revision 写回(`revision` 自增)。不使用管理面模型注册表 `/api/models/`——它会残留已下线、没有路由绑定的条目;也不使用 `/v1/models`——它要求 Router 用户 Key,平台没有服务级 Key。并发启动的多个实例里只有一个写入成功,其余接受既有目录。
- 同步失败(网络、非 2xx、空列表、响应超过 1 MiB、缺少目录行 revision、写回失败)只记录 error 日志,不写任何替代目录、不使用任何内置模型名;本次启动保持未初始化,下一次启动继续重试,直到目录里有数据。
- 目录未初始化时 `GET /api/llm/models`、`/api/llm/responses`、`/api/llm/chat/completions` 与后台 `GET/PUT /admin/api/agc-models` 一律失败关闭(`503`),错误文案指向“模型目录未初始化”。恢复路径是修好上游可达性后重启 api-server,或由运维清空 `agc_model_catalog` 该行后再重启。
- 上游变化不自动跟随:目录只在未初始化时重建;上游新增或移除模型由 owner 在后台增删条目或调整启用、默认项。
- `GET/PUT /admin/api/agc-models` 仅 owner 可用,返回完整配置;PUT 携带上次读取的 revision,冲突拒绝覆盖。
- `GET /api/llm/models` 返回启用项的 `id/displayName`、`defaultModelId` 和目录 `revision`,不返回实际模型名、Router 目录、凭据或能力原始数据。
- 客户端缓存最近 `revision`,在项目切换 / 对话表面挂载 / 下拉展开 / 窗口聚焦时条件刷新:`revision` 未变化不更新界面,同一时刻只保留一个在途请求,刷新失败保留上一次有效目录与本地选择。发起对话前用同一份快照校验所选模型仍启用,已停用或删除则回退默认模型并提示。
@@ -48,6 +54,9 @@
## 验收
- 空目录 + 上游可达:启动后目录自动生成(`id` 为模型名 slug、`alias`/`modelId` 为上游原名、`enabled` 全为真、默认项为排序后第一项),`revision` 自增一次,`GET /api/llm/models` 的 `displayName` 就是上游原名,界面不出现任何内置模型名。
- 空目录 + 上游不可达/空列表/非 2xx:启动只记录 error,不生成替代目录;AGC 接口与后台目录接口返回 `503`“模型目录未初始化”;下游可恢复后重启即同步成功(不需要人工造目录)。
- 同一模型集合重复同步结果一致(上游返回顺序不影响目录与默认项)。
- 目录领域校验、未知/停用模型拒绝、客户端响应不包含实际模型名。
- 后台鉴权、持久化 revision 冲突处理;客户端选择保存后重新读取,设置保存不覆盖选择。
- 目录 `revision` 条件刷新与并发触发去重、发送前回退默认模型、刷新失败可恢复。