150 lines
8.2 KiB
Markdown
150 lines
8.2 KiB
Markdown
# 模型连接请求定制技术设计
|
||
|
||
## 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.
|