Files

162 lines
5.9 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
## Context
当前实现已经具备“输入 Base URL + API Key 后获取模型列表”的基础能力,但运行时状态仍然是全局单例:
- 只有一份供应商配置
- 只有一份已发现模型列表
- 只有一份已添加模型集合
- 所有生成入口默认共享同一组模型来源
这一结构不适合继续扩展到多供应商场景。
同时,当前已经落地的多供应商雏形仍然保留了较强的“两段式”心智:
- 用户先进入供应商配置
- 再切换到模型管理视图获取并选择模型
- 最后再到默认模型预设里选择默认模型
这会把一个本应连续的操作链拆成多段,增加理解负担,也容易让用户误以为“添加模型以后就已经生效”。
## Goals / Non-Goals
- Goals:
- 支持多个供应商接入配置并存
- 支持每个接入配置拥有独立模型目录
- 支持按文本、图片、视频分别路由到不同供应商下的默认模型
- 支持用户在运行时切换默认模型预设
- 在用户显式选择模型时,按模型所属供应商自动完成调用路由
- 保证旧版单配置数据可自动迁移
- Non-Goals:
- 本次不实现云端同步多个供应商配置
- 本次不做供应商自动健康探测与自动故障切换
- 本次不统一不同供应商的计费展示
## Decisions
- Decision: 将现有单一 `gemini` 配置升级为 `profiles + presets + catalogs`
- `profiles` 解决“怎么连”
- `catalogs` 解决“这个接入源有哪些模型”
- `presets` 解决“在未显式选择模型时当前默认用哪个模型”
- Decision: 模型发现结果必须按 `profileId` 隔离
- 同一个模型 ID 在不同供应商下可能具有不同支持能力
- 同一供应商的模型添加/删除不应影响其它供应商
- Decision: “获取模型”和“添加模型”合并为供应商详情中的统一“管理模型”流程
- 用户在供应商详情中完成连接、测试、获取并选择模型
- 不再保留单独的“模型管理”一级页签
- 主设置页只展示摘要,不展示完整已添加模型清单
- Decision: 请求路由以“模型引用”优先而不是“Profile 显式选择”优先
- `Preset` 仅为每种任务类型配置默认模型引用
- 模型引用包含 `profileId + modelId`
- 用户在主界面或方案里显式选择某个模型时,请求应优先按该模型所属供应商解析凭证
- 只有在没有显式模型引用时,才回退到 Preset 默认模型或 legacy 配置
- Decision: 兼容迁移采用“懒迁移”
- 启动时检测旧结构
- 自动生成 `legacy-default` Profile 与 `default` Preset
- 将旧的默认模型选择映射到对应 Preset 路由
- Decision: `ProviderType``AuthType``capabilities` 需要下沉到适配层,而不仅停留在表单层
- 模型发现与实际请求应通过统一的 provider transport / adapter 构造请求
- 避免继续在各个 service 中写死 `/models``Authorization: Bearer`
## Proposed Data Model
```ts
interface ProviderProfile {
id: string;
name: string;
providerType: 'openai-compatible' | 'gemini-compatible' | 'custom';
baseUrl: string;
apiKey: string;
authType: 'bearer' | 'header';
extraHeaders?: Record<string, string>;
enabled: boolean;
capabilities: {
supportsModelsEndpoint: boolean;
supportsText: boolean;
supportsImage: boolean;
supportsVideo: boolean;
supportsTools: boolean;
};
}
interface ProviderCatalog {
profileId: string;
discoveredAt: number | null;
discoveredModels: ModelConfig[];
selectedModelIds: string[];
}
interface ModelRef {
profileId: string | null;
modelId: string | null;
}
interface DefaultModelPreset {
id: string;
name: string;
isDefault?: boolean;
text: { defaultModelRef: ModelRef | null };
image: { defaultModelRef: ModelRef | null };
video: { defaultModelRef: ModelRef | null };
}
```
## UI Structure
- `供应商配置`
- 列表显示所有 Profiles
- 支持新增、编辑、删除、启用/停用、测试连接
- 在供应商详情内提供“管理模型”入口
- 管理模型进入抽屉或独立工作区,自动同步后直接筛选、勾选、保存
- `默认模型预设`
- 列表显示所有 Presets
- 每个 Preset 分别配置文本、图片、视频默认模型
- 模型选择器按供应商分组展示,选择模型时自动绑定其所属供应商
- 支持切换当前激活 Preset 作为默认值来源
- `主界面模型选择器`
- 继续支持图片、视频、文本选择
- 选项按供应商分组
- 用户一旦显式选择某个模型,请求优先按该模型所属 `profileId` 路由
- 当前显式选择不会要求用户额外配置供应商路由
## Risks / Trade-offs
- 风险: 设置存储结构变化较大
- Mitigation: 提供兼容迁移与兜底默认值
- 风险: 运行时需要稳定知道“某个模型属于哪个供应商”
- Mitigation: 为运行时模型和选择器统一引入 `ModelRef` 结构,不再只传裸 `modelId`
- 风险: `ProviderType` / `AuthType` 目前只存在于设置表单中
- Mitigation: 新增 provider adapter 层,统一处理鉴权头、模型发现路径和能力校验
- 风险: 模型发现和模型选择的状态来源从单例变成多实例,复杂度上升
- Mitigation: 将 `runtime-model-discovery` 抽象为按 `profileId` 的 catalog store
## Migration Plan
1. 保留旧 `gemini` 字段读取能力
2. 启动时若检测不到 `profiles/presets`,自动从旧配置生成默认 Profile 和 Preset
3. 将 Preset 的旧 `profileId + defaultModelId` 结构迁移为 `defaultModelRef`
4. 首次保存新设置后,优先写入新结构
5. 所有请求入口先读取新结构;若新结构不存在,再回退旧结构
## Open Questions
- 主界面的 Preset 切换器放在顶部全局栏还是 AI 输入栏附近
- 当用户临时选择了与当前 Preset 默认模型不同的模型时,是否需要显式提示“本次调用将按所选模型所属供应商执行”