# 模型连接请求定制技术设计 ## 1. 共享契约 `src/shared/model-request-customization.ts` 定义: - `ModelRequestHeaders`:字符串到字符串的映射。 - `ModelRequestBody`:顶层 JSON 对象。 - 两个 Zod schema、统一边界和合并函数。 模型连接输入、公开设置、Main 内 resolved profile 和远程 Prompt profile 复用这些类型。 Runtime 设置文件版本为 21;版本 20 迁移为两个空对象,不改变已有连接或凭据。 ## 2. 边界 请求头最多 64 项;名称最长 128 字符,值最长 4096 字符。名称必须是合法 HTTP token, 大小写归一后不能重复,值不能包含控制字符。 请求体编码后最多 64 KiB,最大深度 16、最大节点数 4096;对象或数组单层最多 256 项。 键最长 256 字符,字符串值最长 32768 字符,数字必须有限。 下列请求头由认证、协议或传输层拥有,不能在连接中配置: `Authorization`、`X-API-Key`、`API-Key`、`Content-Type`、`Content-Length`、`Host`、 `Connection`、`Transfer-Encoding`、`User-Agent`、`Anthropic-Version`。 下列请求体顶层字段由 GoodBuddy 或 Runtime 拥有,不能在连接中配置: `model`、`stream`、`messages`、`input`、`instructions`、`system`、`tools`、 `tool_choice`、`parallel_tool_calls`、`max_tokens`、`max_output_tokens`、 `stream_options`、`prompt`、`n`、`quality`、`response_format`。 ## 3. 合并规则 请求头先装载用户值,再装载 Runtime 和协议值,最后注入认证值。名称比较不区分大小写, 因此 Runtime、协议和认证值始终覆盖同名用户值。 请求体只做一次顶层浅合并: ```ts const providerBody = { ...connection.requestBody, ...runtimeBody } ``` 不递归合并嵌套对象。最终请求仍需通过现有协议策略和请求大小校验。 ## 4. Runtime 支持矩阵 | 请求路径 | 自定义 Header | 自定义 Body | 实现边界 | | --- | --- | --- | --- | | GoodBuddy 直连 | 支持 | 支持 | Main 在连接测试、文本、工具、摘要和图像请求中合并 | | Continue | 支持 | 支持 | 使用 Continue 1.5.47 的 `requestOptions.headers` 与 `extraBodyProperties` | | 本地 OpenCode | 支持 | 不支持 | 使用 OpenCode Provider `options.headers`;不生成未证实的 Body 配置 | | DeepSeek Harness | 支持 | 不支持 | 使用 `dsh-llm-pi-ai` Provider 的 `headers`;Utility 控制协议 v3 传递 Header | | 受管远程 OpenCode | 支持 | 支持 | Agent Prompt profile 传到可信模型网关后合并 | | Runtime 自有配置 | 不适用 | 不适用 | 该模式不使用 GoodBuddy 模型连接 | 对不支持自定义 Body 的 Runtime,设置仍保留在连接上,切换回支持路径时继续生效;当前 Runtime 不接收也不发送该 Body。 ## 5. 远程模型网关 非空请求定制随 Prompt-scoped model profile 发送,不写入 Agent 环境、命令行或模型调用 ledger。模型 profile digest 绑定规范化后的 Header 和 Body,但继续排除 API Key。 Main 网关和 Agent 网关都执行相同的可信 profile 合并、协议策略检查和最终 Body 大小检查。 认证 Header 在请求摘要生成后注入,避免凭据进入摘要或 ledger。受管远程 `runtime/acp` capability 为 v5;旧 v4 Agent 不能接收带请求定制的新 Prompt profile, 由现有受管更新流程升级后再运行。 ## 6. 凭据 API Key 继续由现有 Main 加密设置和 Prompt-scoped Agent 凭据路径管理。自定义 Header 和 Body 是普通可见连接设置,不提供第二套通用秘密存储;UI 必须提示不要把密钥放入这两个 JSON 对象,标准认证 Header 也由 schema 拒绝。 模型连接输入使用 `modelConnectionUrlSchema`,先在 Renderer 以连接名称标识无效 URL, 再由共享契约拒绝空值、超过 2048 个字符或非 HTTP/HTTPS 地址。页头错误保留完整错误上下文, 字段错误通过 `aria-describedby` 关联到 URL 控件。 复制连接不在 Renderer 读取或暴露已保存 API Key。复制草稿提交 `{ action: 'copy', sourceProfileId }`,Main 在同一设置更新事务中从当前加密凭据或默认连接 的环境凭据创建目标凭据。源连接不存在时整个更新失败;目标连接的其他字段仍由普通输入 校验。正在编辑但尚未保存的 Key 仍以一次性 `replace` 值提交,因而不与旧凭据混合。 ## 7. 验证 - 共享 schema:JSON 类型、边界、保留字段和优先级。 - 设置存储:v20 → v21 迁移与 Header/Body round trip。 - 直连:连接测试、流式文本、工具、摘要和图像请求。 - Runtime:真实 bundled OpenCode/Continue 对 loopback Provider 的请求探针;DeepSeek Utility 启动配置和 Pi-AI Provider 配置。 - 远程:Main/Agent 网关合并、认证优先级、摘要绑定、大小限制和不重放。 - UI:默认值、保存、无效 JSON 保留、连接级 URL 错误、粘附标题/页签、折叠高级设置和 包含凭据的连接复制。 ## 8. 通道直连模型选择修复 `repairChannelRuntimeSelection` 只在默认连接可用于文本通道时保留动态 `{ provider: 'model' }`。默认连接为图像生成、API Key 认证但明确缺少凭据,或默认 ID 已不存在时,若找到可用文本连接,修复结果必须携带该连接的 `profileId`,使后续 `applyRuntimeSelection` 使用已验证回退,而不是重新解析到不可用的全局默认值。 已有可用显式引用保持不变;没有可用连接时不虚构 ID 或声称可运行。 共享回归测试将修复结果直接传入 Main 的生产 resolver,验证动态默认、显式固定、 图像默认、缺少凭据和失效默认 ID。该修复不改变 OpenCode、Continue 或托管 SSH 的 Runtime 选择路径。 ## 9. Harness 平台来源公开投影 `RuntimeSettings.deepseekHarnessPlatformModel` 是可选只读元数据,描述 `platform` 选项在当前已保存模型设置与管理员环境下的实际解析结果: - `{ source: 'environment', name, modelName }`:使用完整管理员预置。 - `{ source: 'profile', profileId, name, modelName }`:使用兼容 GoodBuddy 连接回退。 - `{ source: 'unavailable' }`:没有可解析的连接。 Main 的 `toPublicSettings` 复用 `resolveDeepSeekHarnessModelProfile`,只在调用中将来源 设为 `platform`,不改变用户已保存的 `default`、`profile` 或 `platform` 选择。投影逐字段 构造,不包含地址、API Key、自定义 Header/Body 或凭据状态,不写入设置文件、迁移或新状态。 公开契约保持可选,缺失时 UI 明确来源信息不可用,不从通用模型或环境凭据猜测。 `source: 'profile'` 描述来源分支,不代表凭据来源:仅环境密钥或旧环境变量可能仍影响该 GoodBuddy 连接,但不会被误报为完整管理员预置。连接解析不代替真实模型请求或可用性测试。 UI 的未保存兼容候选只作草稿预览,保存后继续以 Main 返回的投影为准。 ## 10. Settings IPC Ownership [model-settings-ipc.ts](../../../src/main/model-settings-ipc.ts) registers the 11 model/runtime settings handlers: public settings, customization reads and updates, native inventory, settings updates, workspace/file selection, runtime detection, config actions, and model/runtime connection tests. It owns sender checks, shared schema parsing, config-file metadata, and activation rollback. [ipc.ts](../../../src/main/ipc.ts) calls this registrar at the original position and retains tracked registration, the shared settings-update queue, runtime activation, conversation-selection repair, and project execution-space resolution. These operations are injected as narrow callbacks. Runtime activation still cancels affected expert-team requests; failed activation restores the previous settings and reactivates them before the next queued update can run. Cleanup waits for the tracked handler promises. Usage recording, runtime refresh subscriptions, and runtime/Agent lifecycle ownership remain in the composition root. The integration tests in [ipc.test.ts](../../../src/main/ipc.test.ts) exercise the production registrar through `registerIpcHandlers`, including registration order, trusted senders, serialized updates, rollback failures, cleanup waiting for reload, native inventory resolution, and connection/config actions.