feat(gateway,admin-web): 面板可编辑 provider 推理形状 / 角色级路由 / multimodal 真语义(裁决 03.4.2/03.4.1)
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled

- 后端:AdminLlmProvider 暴露 reasoning{kind}(恒下发,""=未声明=auto)、personaModels、personaProviders;
  顶层新增 reasoningKinds / personaProviderOptions 两个**取值域**(唯一来源= runtimeconfig 导出枚举,
  校验错误文案也由枚举拼接 ⇒ 不可能漂移)
- 读写共用同一映射(admin_llm_provider.go),避免「读侧少一个键 ⇒ 面板一保存就抹掉」
- 写侧语义:缺失=保留、给出=整块/整表替换;非法值一律 runtimeconfig.Group.Validate() ⇒ 400 bad_request
  (实测非法 kind 返回 400 且 models.json 完全不动,而非 500 静默失败)
- 前端:新增 LlmProviderRouting(推理形状下拉 + multimodal 开关 + 按角色路由编辑器);
  目录能力位改三态(true/false/未标注),「未标注」不再谎称「不支持」;目录/角色清单不可用时
  **不阻塞编辑**且与「字段为空」是不同状态;绝无 mock/sample 回退
- 复核:build/vet 绿 + gateway/config 包测试绿 + tsc 0 错 + vite build 成功;2 条变异反证(删 reasoning
  补丁 → 2 例红;删 Group.Validate → 该 400 用例红)已还原
- 注意(主会话已知):npm run build 把新 SPA 写进 frontend/web/dist,而运行中的二进制优先从磁盘读该目录
  ⇒ :10942 目前是「新面板 + 旧后端」,等阶段 5 全绿后按红线「先 build 到位、再一次 restart」切换
This commit is contained in:
AskaEth committed 2026-10-04 13:06:54 +08:00
1 parent 8890c30bb5
commit 1ac02bd77e
12 files changed
+998 -85

No files matched your search

+20
View File
@@ -123,6 +123,13 @@ export interface AdminUserRecord {
createdAt?: string;
}
/** 一个 provider 组的推理开关形状声明(models.json 的 `reasoning` 块的线上投影)。
* kind 为空串 = **未声明形状**(等同 auto:按模型名/baseURL 推断,形状有歧义时
* 不下发任何开关),与「后端没报告这个字段」是两种状态。 */
export interface AdminLlmReasoning {
kind: string;
}
/** LLM provider/model configuration (GET/PUT /api/v1/admin/llm, design/04 §3.1).
* API key is never returned by the backend; provider is the wire protocol. */
export interface AdminLlmProvider {
@@ -131,15 +138,28 @@ export interface AdminLlmProvider {
provider: string;
baseUrl: string;
model: string;
/** 主模型**自己能否收图**(裁决 14.4.1)。它不表示本部署有视觉能力:图片识别 /
* OCR 工具用的模型由 .env 的 LLM_VISION_MODEL / LLM_OCR_MODEL 单独配置。 */
multimodal: boolean;
enabled: boolean;
fallback?: string;
/** The group's token budget. Absent/0 = unset (the runtime falls back to
* LLM_FALLBACK_MAX_TOKENS on the last step, then the adapter default). */
maxTokens?: number;
/** 该组的推理开关形状。后端始终下发(kind 可能为空串),旧构建则整个字段缺失。 */
reasoning?: AdminLlmReasoning;
/** persona → 模型名的路由表(裁决 03.4.1);缺失 = 该组没有 per-persona 绑定。 */
personaModels?: Record<string, string>;
/** persona → wire 协议的覆盖表;取值只能是 personaProviderOptions。 */
personaProviders?: Record<string, string>;
}
export interface AdminLlmConfig {
providers: AdminLlmProvider[];
/** `reasoning.kind` 的合法取值(不含空串),由后端配置层直接下发 —— 面板的
* 下拉框据此渲染候选,绝不自己硬编码一套枚举。旧构建缺省该字段。 */
reasoningKinds?: string[];
/** `personaProviders` 值的合法取值(协议名)。旧构建缺省该字段。 */
personaProviderOptions?: string[];
}
/** 目录里的一个模型能力位(GET /api/v1/admin/llm/catalog)。attachment 为 true
+10
View File
@@ -35,10 +35,20 @@ export interface LlmProviderRequest {
baseUrl: string;
apiKey?: string;
model: string;
/** 主模型能否收图(≠ 视觉/OCR 工具用的模型,那两个走 .env)。 */
multimodal?: boolean;
fallback?: string;
/** Absent = keep the stored budget (or leave unset for a new group). */
maxTokens?: number;
/** 推理开关形状:**缺省 = 保留已存形状**;给出(哪怕 `{}`)= 用其中的 kind
* 覆盖,kind 缺省/空串 = 回到「未声明」(等同 auto)。取值见
* AdminLlmConfig.reasoningKinds —— 非法 kind 后端回 400。 */
reasoning?: { kind?: string };
/** persona → 模型名路由表:缺省 = 保留已存表;给出(哪怕 `{}`)= **整表替换**
* (否则面板无法删除一条已存路由)。 */
personaModels?: Record<string, string>;
/** persona → 协议覆盖表,规则同上;取值非法(非 openai/anthropic)回 400。 */
personaProviders?: Record<string, string>;
}
/** POST /api/v1/admin/llm/test body. */