// 提供商鉴权方式:api_key(凭据行)或 oauth(device-flow 连接按钮,如 Kimi Code)。 export type ProviderAuthKind = "api_key" | "oauth"; export interface KnownProvider { id: string; label: string; auth: ProviderAuthKind; // OAuth 提供商在档位路由里用的默认 model(API-key 提供商由用户填写/后端默认)。 defaultModel?: string; } // 已知提供商(UX §6.10)。后端按 provider 字符串识别;这里只是 UI 候选列表。 // `kimi-code` 是 OAuth 订阅 plan 提供商(device flow,伪造客户端头,有封号风险),与 API-key // 的 `kimi` 分离(K1.4)。`kimi-code-key` 是同一订阅 plan 的**静态 Console Key** 路径(ToS 合规、 // 无伪造头)——普通 api_key 提供商,固定 model `kimi-for-coding`。 export const KNOWN_PROVIDERS: KnownProvider[] = [ { id: "anthropic", label: "Anthropic", auth: "api_key" }, { id: "deepseek", label: "DeepSeek", auth: "api_key" }, { id: "kimi", label: "Kimi", auth: "api_key" }, { id: "openai", label: "OpenAI", auth: "api_key" }, { id: "qwen", label: "通义千问", auth: "api_key" }, { id: "glm", label: "智谱 GLM", auth: "api_key" }, { id: "gemini", label: "Gemini", auth: "api_key" }, { id: "kimi-code-key", label: "Kimi Code(订阅 Key)", auth: "api_key", defaultModel: "kimi-for-coding", }, { id: "kimi-code", label: "Kimi Code(OAuth)", auth: "oauth", defaultModel: "kimi-for-coding", }, ]; // API-key 凭据行只展示 api_key 提供商;OAuth 提供商有独立连接区。 export const API_KEY_PROVIDERS: KnownProvider[] = KNOWN_PROVIDERS.filter( (p) => p.auth === "api_key", ); export const TIER_LABELS: Record = { writer: "写手档", analyst: "分析档", light: "轻量档", }; // 档位顺序(路由编辑器列出全部档位,缺省的也能配)。 export const TIERS = ["writer", "analyst", "light"] as const; export type Tier = (typeof TIERS)[number]; // 某 provider 在路由里使用的默认 model:OAuth 提供商有固定 model,其余给空串占位。 export function defaultModelFor(providerId: string): string { return ( KNOWN_PROVIDERS.find((p) => p.id === providerId)?.defaultModel ?? "" ); } // 单条档位路由的可编辑草稿(纯数据)。 export interface RoutingDraft { tier: Tier; provider: string; model: string; } export interface TierRouting { tier: string; provider: string; model: string; fallback?: string[] | null; } // 把已存路由(可能缺档位)展开成「全部档位」的可编辑草稿(缺的给空)。 export function toRoutingDrafts(existing: TierRouting[]): RoutingDraft[] { const byTier = new Map(existing.map((r) => [r.tier, r])); return TIERS.map((tier) => { const row = byTier.get(tier); return { tier, provider: row?.provider ?? "", model: row?.model ?? "", }; }); } // 选择 provider 时自动套用该 provider 的默认 model(OAuth 固定 model;否则保留已填值或清空)。 export function applyProviderChange( draft: RoutingDraft, providerId: string, ): RoutingDraft { const def = defaultModelFor(providerId); return { ...draft, provider: providerId, model: def !== "" ? def : "" }; } // 草稿 → PUT body 的 tier_routing:只取选了 provider+model 的行(不可变)。 export function draftsToRoutingInput( drafts: RoutingDraft[], ): { tier: Tier; provider: string; model: string }[] { return drafts .filter((d) => d.provider.trim() !== "" && d.model.trim() !== "") .map((d) => ({ tier: d.tier, provider: d.provider, model: d.model })); }