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,161 @@
## 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 默认模型不同的模型时,是否需要显式提示“本次调用将按所选模型所属供应商执行”

View File

@@ -0,0 +1,42 @@
# Change: 增加多供应商配置与默认模型预设
## Why
当前设置体系只有单一 `gemini` 配置,模型发现、已添加模型、默认模型选择也都基于这一份全局状态。
当用户需要同时接入多个兼容供应商时,这种结构会带来三个直接问题:
- 不同 `Base URL + API Key` 无法并存,用户只能反复覆盖配置
- 模型目录无法区分来源,已添加模型与系统模型的关系容易混乱
- 调用时无法按“文本 / 图片 / 视频”切换到不同的接入方案
此外,当前交互把“配置供应商”“获取并选择模型”“绑定默认调用”拆成多个分离视图,用户容易误以为添加模型后就已自动生效;而在设置页直接展示已添加模型清单的方式也不适合模型数较多的场景,信息密度过高,容易把主设置页撑爆。
## What Changes
- 引入 `Provider Profile`,允许用户维护多个供应商接入配置
- 将模型发现结果改为按 `profileId` 隔离,形成各自独立的模型目录
- 将“获取模型”和“添加模型”收敛为供应商详情内的统一“管理模型”流程,不再作为独立一级视图
- 引入 `Default Model Preset`,允许用户为文本、图片、视频分别指定默认模型引用,作为未显式选择模型时的默认值
- 调整运行时请求解析逻辑,在用户显式选择模型时优先按该模型所属供应商解析 `baseUrl + apiKey`,只有在没有显式模型引用时才回退到默认模型预设
- 为现有单一 `gemini` 配置提供自动迁移路径,升级后生成默认 Profile 和默认 Preset
## Impact
- Affected specs:
- `provider-profiles`
- `model-routing-presets`
- `runtime-model-discovery`
- Affected code:
- `packages/drawnix/src/utils/settings-manager.ts`
- `packages/drawnix/src/utils/runtime-model-discovery.ts`
- `packages/drawnix/src/components/settings-dialog/*`
- `packages/drawnix/src/components/ai-input-bar/*`
- `packages/drawnix/src/components/ttd-dialog/*`
- `packages/drawnix/src/services/model-adapters/*`
- 任务创建与模型路由相关的请求服务
## Relationship To Existing Changes
- 本变更扩展并重构了 `add-runtime-model-discovery` 的方向
- 模型发现能力仍然保留,但其作用域将从“全局单实例”升级为“按 Profile 管理的模型目录”
- 相比最初的多供应商提案,本次进一步将“调用方案”降级为“默认模型预设”,真实路由以模型引用所属供应商为准,以减少重复配置和用户误解

View File

@@ -0,0 +1,74 @@
## ADDED Requirements
### Requirement: Manage Default Model Presets
The system SHALL allow users to define multiple default model presets for switching between default model combinations.
#### Scenario: Create a new default model preset
- **GIVEN** the user opens preset management
- **WHEN** the user creates a new preset
- **THEN** the preset SHALL be stored independently with its own default model configuration
#### Scenario: Switch the active preset
- **GIVEN** multiple presets exist
- **WHEN** the user selects a different preset as active
- **THEN** subsequent generation requests without an explicit model choice SHALL use the default model references from the newly active preset
### Requirement: Apply Default Models By Task Type
Each preset SHALL allow separate default model targets for text, image, and video operations.
#### Scenario: Use different provider-backed models for text and image generation
- **GIVEN** a preset assigns a text model from one provider and an image model from another
- **WHEN** the user sends a text request and then an image request without explicitly overriding the model
- **THEN** each request SHALL use the credentials of the provider that owns the selected default model for its own route type
#### Scenario: Prevent selecting unsupported routes
- **GIVEN** a provider profile does not support a required capability such as video generation
- **WHEN** the user edits a preset
- **THEN** models belonging to that provider SHALL not be offered as valid targets for that unsupported route
### Requirement: Implicitly Bind Providers Through Model Selection
Selecting a model for a preset or an explicit user action SHALL implicitly determine the provider route for that request.
#### Scenario: Selecting a preset model binds the provider automatically
- **GIVEN** the user is editing the image route of a preset
- **WHEN** the user selects a model that belongs to a specific provider profile
- **THEN** the preset SHALL store that model reference
- **AND** future image requests that rely on preset defaults SHALL resolve through the provider that owns the selected model
#### Scenario: Explicit user model choice overrides preset default
- **GIVEN** a preset already has a default model configured for a route type
- **WHEN** the user explicitly chooses another provider-backed model at invocation time
- **THEN** the current request SHALL use the provider that owns the explicitly selected model
- **AND** the preset default SHALL remain unchanged unless the user explicitly saves it back into the preset
#### Scenario: Explicit model choice does not require a separate provider route setting
- **GIVEN** the user has already selected a provider-backed model in the runtime UI
- **WHEN** the request is submitted
- **THEN** the system SHALL resolve the provider credentials from that selected model reference
- **AND** SHALL not require a separate provider route selection step
### Requirement: Keep Presets Valid When Models Change
The system SHALL protect active routing when selected models are removed or become unavailable.
#### Scenario: Fallback when a preset model is removed
- **GIVEN** a preset references a model that is later removed from its provider catalog
- **WHEN** the preset is loaded
- **THEN** the system SHALL mark that route as needing reassignment or apply a safe fallback model from the same profile
#### Scenario: Preserve preset routes for unaffected task types
- **GIVEN** only one route type in a preset becomes invalid
- **WHEN** the preset is loaded
- **THEN** the remaining valid route types SHALL stay unchanged

View File

@@ -0,0 +1,62 @@
## ADDED Requirements
### Requirement: Manage Multiple Provider Profiles
The system SHALL allow users to create and maintain multiple provider connection profiles at the same time.
#### Scenario: Add a new provider profile
- **GIVEN** the user opens provider configuration
- **WHEN** the user creates a new profile with a name, Base URL, and API key
- **THEN** the system SHALL save that profile independently from existing profiles
#### Scenario: Disable a provider profile without deleting it
- **GIVEN** an existing provider profile is configured
- **WHEN** the user disables that profile
- **THEN** the profile SHALL remain stored
- **AND** it SHALL not appear as an available routing target until re-enabled
### Requirement: Keep Model Catalogs Scoped To Profiles
Each provider profile SHALL own its own discovered and selected model catalog.
#### Scenario: Discover models for one profile without affecting another
- **GIVEN** the user has two provider profiles configured
- **WHEN** the user fetches models for one profile
- **THEN** the discovered models SHALL be stored only under that profile
- **AND** the other profile's discovered models SHALL remain unchanged
#### Scenario: Preserve selected models per profile
- **GIVEN** the user adds selected models under a specific provider profile
- **WHEN** the user switches to another profile
- **THEN** the selected model set from the first profile SHALL remain intact
- **AND** not be merged into the second profile automatically
### Requirement: Present Profile Summaries In Main Settings
The main settings surface SHALL summarize provider and model state without expanding full model lists inline.
#### Scenario: Show compact profile and model summary
- **GIVEN** the user has many added models under one or more profiles
- **WHEN** the user views the main settings surface
- **THEN** the system SHALL show compact counts and status summaries
- **AND** SHALL provide an entry into a provider-scoped model management flow instead of rendering the full list inline
### Requirement: Manage Models Within Provider Configuration
The system SHALL allow users to discover and select models from within the provider configuration flow, without requiring a separate top-level settings section for model management.
#### Scenario: Fetch and select models from a provider detail view
- **GIVEN** the user is editing a configured provider profile
- **WHEN** the user opens model management for that provider
- **THEN** the system SHALL fetch and present models in the context of that provider
- **AND** SHALL allow the user to save selected models back into that provider's catalog
### Requirement: Migrate Legacy Single-Provider Settings
The system SHALL migrate legacy single-provider settings into the new profile model without losing the user's existing configuration.
#### Scenario: Upgrade from legacy gemini settings
- **GIVEN** the user has an existing legacy `gemini` configuration but no multi-profile data
- **WHEN** the new settings system initializes
- **THEN** the system SHALL create a default provider profile from the legacy values
- **AND** preserve the existing default model selections through migration

View File

@@ -0,0 +1,57 @@
## MODIFIED Requirements
### Requirement: Discover Models From Base URL And API Key
The system SHALL allow users to fetch a provider's available model list using the currently entered Base URL and API key, and SHALL scope the result to the specific provider profile being managed.
#### Scenario: Normalize root base URL before discovery
- **GIVEN** the user enters a provider root URL such as `https://api.tu-zi.com`
- **WHEN** the system performs model discovery for that provider profile
- **THEN** the request SHALL be sent to the normalized models endpoint under `/v1/models`
#### Scenario: Keep discovery results isolated per profile
- **GIVEN** the user has multiple provider profiles
- **WHEN** the system performs model discovery for one profile
- **THEN** the fetched model list SHALL update only that profile's catalog
- **AND** SHALL not overwrite the discovered or selected models of other profiles
#### Scenario: Surface discovery failure without breaking other profiles
- **GIVEN** the current Base URL or API key cannot fetch a valid model list for one profile
- **WHEN** the discovery request fails, returns non-JSON, or returns an empty list
- **THEN** the user SHALL receive a visible failure message
- **AND** catalogs belonging to other profiles SHALL remain available
#### Scenario: Discover models from the provider management flow
- **GIVEN** the user is viewing a provider configuration
- **WHEN** the user triggers model management for that provider
- **THEN** the discovery request SHALL execute in the context of that provider
- **AND** the user SHALL be able to review and save selected models without navigating to a separate top-level model management section
### Requirement: Reuse Runtime Model Lists Across Selectors
All model selectors that currently depend on static model lists SHALL resolve models from provider-scoped catalogs while preserving provider provenance, with static models remaining available as system defaults.
#### Scenario: Selectors show enabled provider-backed models grouped by provider
- **GIVEN** one or more enabled provider profiles have selected models in their catalogs
- **WHEN** the user opens an image, video, or text model selector
- **THEN** the selector SHALL show the selected provider-backed models grouped by provider
- **AND** SHALL continue to expose system models as fallback options
#### Scenario: Preset switching updates default selection without hiding provider models
- **GIVEN** the user switches to another active preset
- **WHEN** the model selector is next resolved
- **THEN** the selector default value MAY change based on the newly active preset
- **AND** the available provider-scoped model list SHALL remain derived from enabled provider catalogs rather than only the active preset
#### Scenario: Selector keeps model ownership information
- **GIVEN** a selector shows models from multiple provider catalogs
- **WHEN** the user chooses a model
- **THEN** the selection result SHALL preserve both `modelId` and owning `profileId`
- **AND** subsequent request routing SHALL be able to resolve credentials from that preserved model ownership

View File

@@ -0,0 +1,41 @@
## 1. Data Model And Migration
- [ ] 1.1 将 `settings-manager` 从单一 `gemini` 配置扩展为 `profiles + catalogs + presets`
- [ ] 1.2 实现旧版单配置到默认 Profile/Preset 的自动迁移
- [ ] 1.3 扩展敏感信息加密逻辑以支持多个 Profile 的 API Key
- [ ] 1.4 将路由配置从 `profileId + defaultModelId` 迁移为 `defaultModelRef`
## 2. Profile-Scoped Model Discovery
- [ ] 2.1 将运行时模型发现状态改为按 `profileId` 管理
- [ ] 2.2 支持每个 Profile 独立保存 `discoveredModels``selectedModelIds`
- [ ] 2.3 为每个 Profile 记录接口能力与模型摘要
- [ ] 2.4 引入统一的 provider transport / adapter处理模型发现路径和鉴权方式
## 3. Settings And Management UI
- [ ] 3.1 将当前设置页重构为 `供应商配置 / 默认模型预设` 两个主视图
- [ ] 3.2 实现 Profile 的新增、编辑、删除、启停、测试连接
- [ ] 3.3 将“获取模型 + 添加模型”合并为供应商详情中的“管理模型”入口,主设置页只保留摘要信息
- [ ] 3.4 在默认模型预设里提供按供应商分组的模型选择器,不再先单独选 Profile 再选模型
- [ ] 3.5 将设置中的 legacy “兼容默认模型” 区域降级为兼容入口或移除出主流程,避免与默认模型预设产生双重心智
## 4. Default Presets
- [ ] 4.1 新增 Preset 的增删改查与当前激活 Preset 状态
- [ ] 4.2 支持为文本、图片、视频分别选择默认模型引用,并自动绑定其所属供应商
- [ ] 4.3 在主界面增加当前 Preset 的切换入口与“仅作为默认值”说明
- [ ] 4.4 当用户显式选择模型时,不要求额外设置供应商路由即可完成调用
## 5. Runtime Request Resolution
- [ ] 5.1 将图片、视频、文本生成请求改为优先按当前显式选择的模型引用解析路由
- [ ] 5.2 当没有显式模型引用时,再回退到当前 Preset 默认模型与 legacy 配置
- [ ] 5.3 为不支持所需能力的 Profile 提供可见的校验与失败提示
- [ ] 5.4 确保现有模型选择器按供应商分组展示,并携带模型来源信息
- [ ] 5.5 消除仍然直接依赖 `geminiSettings` 或纯 `modelId` 的旧链路,统一到 `ModelRef -> ProviderProfile` 路由
## 6. Verification
- [ ] 6.1 补充迁移与路由解析测试
- [ ] 6.2 手工验证多 Profile 并存、模型独立管理、按模型来源调用、默认预设切换后的请求行为