Initial TrueGrowth source import

This commit is contained in:
2026-07-07 09:36:36 +08:00
commit 3b6781d695
2283 changed files with 691996 additions and 0 deletions

View File

@@ -0,0 +1,179 @@
## 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<string, string>;
}
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` 时,设置页是否需要显式展示“供应商模板已接管模型能力”