From 58ecc44ee69b2771151fef4c38f46804929dfc23 Mon Sep 17 00:00:00 2001 From: Jason Date: Thu, 15 Jan 2026 15:59:44 +0800 Subject: [PATCH] feat(opencode): Phase 2 - Add OpenCode provider data structures Add OpenCode-specific configuration structures for the AI SDK format: - OpenCodeProviderConfig: Main config with npm package, options, and models - OpenCodeProviderOptions: baseURL, apiKey, and headers support - OpenCodeModel: Model definition with name and token limits - OpenCodeModelLimit: Context and output token limits Key features: - Supports environment variable references (e.g., "{env:API_KEY}") - Custom headers support for specialized providers - Model-level token limits for context management - Helper methods for parsing and validation --- src-tauri/src/provider.rs | 110 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 110 insertions(+) diff --git a/src-tauri/src/provider.rs b/src-tauri/src/provider.rs index 46a6c6f92..171d6d3c3 100644 --- a/src-tauri/src/provider.rs +++ b/src-tauri/src/provider.rs @@ -462,3 +462,113 @@ requires_openai_auth = true"# }) } } + +// ============================================================================ +// OpenCode 供应商配置结构 +// ============================================================================ + +/// OpenCode 供应商的 settings_config 结构 +/// +/// OpenCode 使用 AI SDK 包名来指定供应商类型,与其他应用的配置格式不同。 +/// 配置示例: +/// ```json +/// { +/// "npm": "@ai-sdk/openai-compatible", +/// "options": { "baseURL": "https://api.example.com/v1", "apiKey": "sk-xxx" }, +/// "models": { "gpt-4o": { "name": "GPT-4o" } } +/// } +/// ``` +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct OpenCodeProviderConfig { + /// AI SDK 包名,如 "@ai-sdk/openai-compatible", "@ai-sdk/anthropic" + pub npm: String, + + /// 供应商名称(可选,用于显示) + #[serde(skip_serializing_if = "Option::is_none")] + pub name: Option, + + /// 供应商选项(API 密钥、基础 URL 等) + #[serde(default)] + pub options: OpenCodeProviderOptions, + + /// 模型定义映射 + #[serde(default)] + pub models: HashMap, +} + +impl Default for OpenCodeProviderConfig { + fn default() -> Self { + Self { + npm: "@ai-sdk/openai-compatible".to_string(), + name: None, + options: OpenCodeProviderOptions::default(), + models: HashMap::new(), + } + } +} + +/// OpenCode 供应商选项 +#[derive(Debug, Clone, Serialize, Deserialize, Default)] +pub struct OpenCodeProviderOptions { + /// API 基础 URL + #[serde(rename = "baseURL", skip_serializing_if = "Option::is_none")] + pub base_url: Option, + + /// API 密钥(支持环境变量引用,如 "{env:API_KEY}") + #[serde(rename = "apiKey", skip_serializing_if = "Option::is_none")] + pub api_key: Option, + + /// 自定义请求头 + #[serde(skip_serializing_if = "Option::is_none")] + pub headers: Option>, +} + +/// OpenCode 模型定义 +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct OpenCodeModel { + /// 模型显示名称 + pub name: String, + + /// 模型限制(上下文和输出 token 数) + #[serde(skip_serializing_if = "Option::is_none")] + pub limit: Option, +} + +/// OpenCode 模型限制 +#[derive(Debug, Clone, Serialize, Deserialize, Default)] +pub struct OpenCodeModelLimit { + /// 上下文 token 限制 + #[serde(skip_serializing_if = "Option::is_none")] + pub context: Option, + + /// 输出 token 限制 + #[serde(skip_serializing_if = "Option::is_none")] + pub output: Option, +} + +impl OpenCodeProviderConfig { + /// 从 serde_json::Value 解析 + pub fn from_value(value: &Value) -> Result { + serde_json::from_value(value.clone()) + } + + /// 转换为 serde_json::Value + pub fn to_value(&self) -> Result { + serde_json::to_value(self) + } + + /// 获取第一个模型 ID(用于默认模型选择) + pub fn first_model_id(&self) -> Option { + self.models.keys().next().cloned() + } + + /// 检查是否有有效的 API 密钥配置 + pub fn has_api_key(&self) -> bool { + self.options + .api_key + .as_ref() + .map(|k| !k.trim().is_empty()) + .unwrap_or(false) + } +} +