将 v1 schema 中 `providers.llm` 从 Ark 专用结构推广为通用 `CredentialsProviderLLMEntry`(containing displayName / temperature / extraHeaders), 为 OpenAI / DeepSeek / DashScope / Moonshot 等多 LLM provider 共用一套字段。 旧 `CredentialsProviderLLMArk` 保留为 typealias,已迁移过的 v1 文件无需再次迁移即可继续解码。 新增公开 API: - `CredentialsVault.activeLLMProviderId` getter/setter - `CredentialsVault.configuredLLMProviderIds` - `CredentialsVault.llmProviderConfig(for:)` - `CredentialsVault.setLLMProviderConfig(_:)` - `CredentialsVault.removeLLMProvider(_:)`,禁删 active provider `OpenLessCore` 新增 `LLMProviderRegistry`,集中维护 5 家 OpenAI 兼容供应商 预设(baseURL / model / 帮助文案 / docs URL),UI 与 vault 兜底逻辑共用。 `PolishOutputCleaner` 从 DoubaoPolishClient 内部提升为独立 public 类型, 让 `OpenAICompatibleLLMProvider` 输出统一过 boilerplate / markdown 栅栏清理。 DoubaoPolishClient.swift 已无调用方,整个文件删除。 Tests: - LLMProviderRegistryTests:5 家预设健康检查 + 唯一性 / URL 合法性 - CredentialsVaultMultiProviderTests:active 默认 / 增删 / 往返 / 老 `ark.*` 账号 key 路由到 active provider
95 lines
4.4 KiB
Swift
95 lines
4.4 KiB
Swift
import Foundation
|
||
|
||
/// LLM provider 预设注册表。
|
||
///
|
||
/// 集中维护"用户能开箱即用的供应商列表"——豆包(Ark)、OpenAI、阿里通义 DashScope、
|
||
/// DeepSeek、Moonshot——以及 baseURL / model / 帮助文案。UI 层用它生成添加 provider
|
||
/// 的下拉菜单与帮助提示,运行期则用它兜底未填写的 baseURL / displayName。
|
||
///
|
||
/// `customProviderId` 是占位常量,不是真正的"id":用户在添加自定义 provider 时由 UI 收集
|
||
/// 一个 slug 写入 schema;这里仅用来在添加 sheet 中区分"预设 vs 自定义"。
|
||
public enum LLMProviderRegistry {
|
||
/// 单条预设记录。
|
||
public struct Preset: Sendable, Hashable {
|
||
public let providerId: String
|
||
public let displayName: String
|
||
public let defaultBaseURL: URL
|
||
/// 预设的默认 model。Ark 故意留空——endpoint id 必须由用户在控制台拿到再填。
|
||
public let defaultModel: String
|
||
/// "怎么拿到 API Key" 的提示文案,UI 在帮助 disclosure 里展示。
|
||
public let helpText: String
|
||
/// 供应商 API Key 页面(可选)。UI 给一个"打开"按钮。
|
||
public let docsURL: URL?
|
||
|
||
public init(
|
||
providerId: String,
|
||
displayName: String,
|
||
defaultBaseURL: URL,
|
||
defaultModel: String,
|
||
helpText: String,
|
||
docsURL: URL?
|
||
) {
|
||
self.providerId = providerId
|
||
self.displayName = displayName
|
||
self.defaultBaseURL = defaultBaseURL
|
||
self.defaultModel = defaultModel
|
||
self.helpText = helpText
|
||
self.docsURL = docsURL
|
||
}
|
||
}
|
||
|
||
/// 5 家 OpenAI 兼容供应商的开箱即用预设。
|
||
/// 顺序即为 UI 添加菜单里的展示顺序——第一个是 Ark(老用户迁移过来的默认)。
|
||
public static let presets: [Preset] = [
|
||
Preset(
|
||
providerId: "ark",
|
||
displayName: "豆包 (Ark)",
|
||
defaultBaseURL: URL(string: "https://ark.cn-beijing.volces.com/api/v3")!,
|
||
defaultModel: "",
|
||
helpText: "在火山引擎方舟控制台创建 endpoint 并把 endpoint id 填到 Model;API Key 在「我的 API Key」页生成。",
|
||
docsURL: URL(string: "https://www.volcengine.com/docs/82379/1099475")
|
||
),
|
||
Preset(
|
||
providerId: "openai",
|
||
displayName: "OpenAI",
|
||
defaultBaseURL: URL(string: "https://api.openai.com/v1")!,
|
||
defaultModel: "gpt-4o-mini",
|
||
helpText: "platform.openai.com 创建 API key;推荐 gpt-4o-mini(性价比)或 gpt-4o(高质量)。需国外网络。",
|
||
docsURL: URL(string: "https://platform.openai.com/api-keys")
|
||
),
|
||
Preset(
|
||
providerId: "aliyun-dashscope",
|
||
displayName: "阿里通义 (DashScope)",
|
||
defaultBaseURL: URL(string: "https://dashscope.aliyuncs.com/compatible-mode/v1")!,
|
||
defaultModel: "qwen-turbo",
|
||
helpText: "百炼控制台「API-KEY 管理」生成 sk-...;推荐 qwen-turbo(快)或 qwen-plus(强)。",
|
||
docsURL: URL(string: "https://help.aliyun.com/zh/model-studio/get-api-key")
|
||
),
|
||
Preset(
|
||
providerId: "deepseek",
|
||
displayName: "DeepSeek",
|
||
defaultBaseURL: URL(string: "https://api.deepseek.com/v1")!,
|
||
defaultModel: "deepseek-chat",
|
||
helpText: "platform.deepseek.com 创建 API key;推荐 deepseek-chat。",
|
||
docsURL: URL(string: "https://platform.deepseek.com/api_keys")
|
||
),
|
||
Preset(
|
||
providerId: "moonshot",
|
||
displayName: "Moonshot 月之暗面",
|
||
defaultBaseURL: URL(string: "https://api.moonshot.cn/v1")!,
|
||
defaultModel: "moonshot-v1-8k",
|
||
helpText: "platform.moonshot.cn 创建 API key;推荐 moonshot-v1-8k(短)或 moonshot-v1-32k(长)。",
|
||
docsURL: URL(string: "https://platform.moonshot.cn/console/api-keys")
|
||
),
|
||
]
|
||
|
||
/// 自定义 provider 的占位 id(不是真实 providerId,仅用于 UI 添加菜单)。
|
||
public static let customProviderId = "custom"
|
||
public static let customDisplayName = "自定义 OpenAI 兼容"
|
||
|
||
/// 根据 providerId 查预设;未知 id 返回 nil(自定义 provider 通常落到这里)。
|
||
public static func preset(for providerId: String) -> Preset? {
|
||
presets.first { $0.providerId == providerId }
|
||
}
|
||
}
|