## Context 当前代码已经具备以下基础能力: - `settings-manager` 可以管理 `ProviderProfile / ProviderCatalog / ModelRef / InvocationPreset` - `runtime-model-discovery` 可以按 `profileId` 隔离发现和选择模型 - 图片和视频侧已经存在若干专用 adapter,分别处理 Flux、MJ、Kling、Seedance 等协议变体 但当前结构仍存在三个核心缺口: 1. `resolveInvocationRoute` 只解析凭证和模型,不解析协议与请求体策略 2. `runtime-model-discovery` 仍以统一 `/models + Bearer` 的假设执行发现 3. `model-adapters` 仍然按模型 / 厂商 / 标签进行猜测式匹配,无法表达“同名模型在不同 profile 下走不同协议”的事实 这会导致下列问题: - 同名模型在不同上游中发生协议冲突 - 同协议不同请求体的差异只能堆在 adapter 内的条件分支中,难以扩展 - 文本链路继续绕过统一路由层,无法和图片 / 视频共享同一套供应商协议规划 ## Goals / Non-Goals - Goals: - 让运行时调用基于 `profileId + modelId + operation` 选择协议绑定 - 支持同一模型 ID 在不同供应商下走不同协议 - 支持同一协议下的不同请求体 schema - 统一文本、图片、视频三类调用的运行时规划流程 - 尽量降低用户配置成本,优先自动推断协议绑定,仅在歧义时暴露高级覆盖项 - Non-Goals: - 本次不实现跨供应商自动故障切换 - 本次不实现按价格 / 延迟 / 健康度自动择优调度 - 本次不为所有第三方平台实现完全自动化、零误差的协议识别 ## Decisions - Decision: 区分“模型标识”和“可执行绑定” - `modelId` 表示用户看到的模型 - `ProviderModelBinding` 表示该模型在特定供应商下针对特定操作的调用方式 - 绑定的主键必须至少包含 `profileId + modelId + operation` - Decision: 引入 `InvocationPlanner` - 输入:`routeType + ModelRef + optional override` - 输出:`InvocationPlan` - Planner 负责在多个绑定中选择当前最合适的协议,而不是让 UI 或 service 直接拼 URL - Decision: 将适配器从“按模型注册”迁移为“按协议注册” - 现有 Flux / MJ / Kling / Seedance 等实现可以保留,但角色改为 `ProtocolAdapter` - `ProtocolAdapter` 负责请求体构建、响应解析、可选轮询策略 - Decision: 将鉴权和基础 URL 处理下沉到 `ProviderTransport` - 统一处理 `bearer / header / query / custom` 等认证方式 - 统一处理额外 Header、查询参数和供应商级发现接口 - 避免每个 adapter 再重复拼装认证信息 - Decision: discovery 必须保留原始元数据和绑定推断结果 - 不能只把远端模型压成扁平 `ModelConfig` - 必须保留 `raw`、`capabilityHints`、`bindingCandidates` - 否则后续运行时无法稳定判断同名模型的协议归属 - Decision: 同名模型不得按裸 `modelId` 全局去重 - 选择器和运行时缓存应使用 `selectionKey = profileId::modelId` - 运行时绑定必须保留 `profileId` ## Proposed Data Model ```ts interface ResolvedProviderContext { profileId: string; profileName: string; providerType: string; baseUrl: string; apiKey: string; authType: 'bearer' | 'header' | 'query' | 'custom'; extraHeaders?: Record; } interface ProviderModelBinding { id: string; profileId: string; modelId: string; operation: 'text' | 'image' | 'video'; protocol: | 'openai.chat.completions' | 'openai.images.generations' | 'openai.async.video' | 'google.generateContent' | 'mj.imagine' | 'flux.task' | 'kling.video' | 'seedance.task'; requestSchema: string; responseSchema: string; submitPath: string; pollPathTemplate?: string; priority: number; confidence: 'high' | 'medium' | 'low'; source: 'discovered' | 'template' | 'manual'; } interface DiscoveredProviderModel { profileId: string; modelId: string; selectionKey: string; raw: unknown; capabilityHints: { supportsText: boolean; supportsImage: boolean; supportsVideo: boolean; }; bindings: ProviderModelBinding[]; } interface InvocationPlan { provider: ResolvedProviderContext; modelRef: { profileId: string; modelId: string }; binding: ProviderModelBinding; } ``` ## Execution Flow 1. 用户在设置中保存 `ProviderProfile` 2. `ProviderTransport` 根据 `providerType/baseUrl` 选择供应商模板或探测策略 3. discovery 同步远端模型,并推断出每个模型的 `bindings[]` 4. 用户在运行时选择 `ModelRef(profileId + modelId)` 5. `InvocationPlanner` 根据 `operation` 找到最高优先级的可执行 binding 6. `ProtocolAdapterRegistry` 按 `binding.protocol` 取得协议适配器 7. 适配器根据 `requestSchema` 构建请求,并通过 `ProviderTransport` 发送 8. 如为异步任务,则由对应 `PollingStrategy` 轮询并标准化结果 ## Binding Inference Strategy - OpenAI 兼容供应商: - 优先使用 `/v1/models` 返回的字段、模型 ID 和供应商模板推断绑定 - 默认优先生成 `openai.chat.completions / openai.images.generations / openai.async.video` - Gemini 官方供应商: - 不再强依赖 `/models` - 允许由 provider template 直接提供 `google.generateContent` 绑定 - 自定义供应商: - 若 discovery 无法高置信度推断协议,则保留低置信度绑定 - 仅在需要时暴露高级覆盖入口 ## Request Schema Strategy 同一协议下的请求体差异不再通过大量 `if modelId.includes(...)` 解决,而是显式建模: - `openai.image.basic-json` - `openai.video.form.input-reference` - `google.gemini.generate-content.image` - `kling.video.text2video` - `kling.video.image2video` - `seedance.video.first-last-frame` 这样新增模型时,优先复用已有协议,仅为其指派合适的 `requestSchema`。 ## Migration Plan 1. 新增 provider protocol routing 类型与 planner,不改 UI 2. 将现有图片 / 视频 adapter 包装为 `ProtocolAdapter` 3. 将文本链路迁移到同一 planner 和 protocol registry 4. 扩展 discovery 存储,保留 `bindings[]` 5. 最后再清理旧的“按模型猜 adapter”逻辑 ## Risks / Trade-offs - 风险: 架构抽象增加初始复杂度 - Mitigation: 分阶段迁移,先保留现有 adapter 实现,只调整其注册方式 - 风险: 自动推断协议可能存在误判 - Mitigation: 为 binding 引入 `confidence` 与 `source`,仅在低置信度场景允许高级覆盖 - 风险: 文本 / 图片 / 视频同时迁移会影响面较大 - Mitigation: 先完成 planner 和 image/video 迁移,再迁移 text ## Open Questions - 是否需要将用户手动覆盖的 binding 保存在 `ProviderCatalog`,还是单独存为 `ProviderBindingOverrides` - `authType` 是否需要从当前的 `bearer/header` 扩展为更通用的 `query/custom` - 官方 Gemini 等不提供 OpenAI 风格 `/models` 时,设置页是否需要显式展示“供应商模板已接管模型能力”