1 line
24 KiB
JSON
1 line
24 KiB
JSON
{"repo": "alyghaly2020-ux/prime", "files": {"src-tauri/src/ai/provider_registry.rs": {"f7d19f78c47830cc9f1d98439a70b5a945a8b75e": {"t": 1785595160.4923232, "n": 11552, "h": "de0beb5fbc41dbfd", "c": "use serde::{Deserialize, Serialize};\nuse std::collections::HashMap;\nuse std::sync::Arc;\n\n#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]\npub enum AuthType {\n ApiKey,\n OAuthDeviceCode,\n OAuthExternal,\n ExternalProcess,\n AwsSdk,\n}\n\n#[derive(Debug, Clone, Serialize, Deserialize)]\npub struct ProviderConfig {\n pub id: String,\n pub name: String,\n pub auth_type: AuthType,\n pub inference_base_url: String,\n pub api_key_env_vars: Vec<String>,\n pub base_url_env_var: Option<String>,\n pub api_mode: ApiMode,\n}\n\n#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]\npub enum ApiMode {\n /// OpenAI-compatible /v1/chat/completions\n OpenAI,\n /// Anthropic Messages API /v1/messages\n Anthropic,\n /// Google Gemini generateContent\n Gemini,\n /// Native format (e.g. MiniMax)\n Native,\n}\n\nimpl ProviderConfig {\n pub fn api_key_env_vars(&self) -> &[String] {\n &self.api_key_env_vars\n }\n}\n\npub struct ProviderRegistry {\n providers: HashMap<&'static str, Arc<ProviderConfig>>,\n}\n\nimpl Default for ProviderRegistry {\n fn default() -> Self {\n Self::new()\n }\n}\n\nimpl ProviderRegistry {\n pub fn new() -> Self {\n let mut providers: HashMap<&'static str, Arc<ProviderConfig>> = HashMap::new();\n\n macro_rules! define {\n ($id:expr, $name:expr, $auth:expr, $url:expr, $mode:expr$(,)?) => {\n define!($id, $name, $auth, $url, $mode, Vec::<String>::new(), Option::<String>::None)\n };\n ($id:expr, $name:expr, $auth:expr, $url:expr, $mode:expr, $keys:expr$(,)?) => {\n define!($id, $name, $auth, $url, $mode, $keys, Option::<String>::None)\n };\n ($id:expr, $name:expr, $auth:expr, $url:expr, $mode:expr, $keys:expr, $url_env:expr$(,)?) => {\n providers.insert(\n $id,\n Arc::new(ProviderConfig {\n id: $id.to_string(),\n name: $name.to_string(),\n auth_type: $auth,\n inference_base_url: $url.to_string(),\n api_key_env_vars: $keys.iter().map(|s| s.to_string()).collect(),\n base_url_env_var: $url_env.map(|s| s.to_string()),\n api_mode: $mode,\n }),\n );\n };\n }\n\n use AuthType::*;\n use ApiMode::*;\n\n // ── OAuth Providers ──\n define!(\"nous\", \"Nous Portal\", OAuthDeviceCode, \"https://api.nousresearch.com/v1\", OpenAI);\n define!(\"openai-codex\", \"OpenAI Codex\", OAuthExternal, \"https://api.openai.com/v1\", OpenAI);\n define!(\"qwen-oauth\", \"Qwen OAuth\", OAuthExternal, \"https://dashscope.aliyuncs.com/compatible-mode/v1\", OpenAI);\n define!(\"google-gemini-cli\", \"Google Gemini (OAuth)\", OAuthExternal, \"https://generativelanguage.googleapis.com/v1beta\", Gemini);\n\n // ── API Key Providers ──\n define!(\"copilot\", \"GitHub Copilot\", ApiKey,\n \"https://models.github.ai/inference\", OpenAI,\n &[\"COPILOT_GITHUB_TOKEN\", \"GH_TOKEN\", \"GITHUB_TOKEN\"], Some(\"COPILOT_API_BASE_URL\"));\n define!(\"copilot-acp\", \"GitHub Copilot ACP\", ExternalProcess,\n \"https://api.githubcopilot.com\", OpenAI,\n Vec::<String>::new(), Some(\"COPILOT_ACP_BASE_URL\".to_string()));\n define!(\"google\", \"Google AI Studio\", ApiKey,\n \"https://generativelanguage.googleapis.com/v1beta\", Gemini,\n &[\"GOOGLE_API_KEY\", \"GEMINI_API_KEY\"], Some(\"GEMINI_BASE_URL\"));\n define!(\"zai\", \"Z.AI / GLM\", ApiKey,\n \"https://api.z.ai/api/paas/v4\", OpenAI,\n &[\"GLM_API_KEY\", \"ZAI_API_KEY\", \"Z_AI_API_KEY\"], Some(\"GLM_BASE_URL\"));\n define!(\"kimi-coding\", \"Kimi / Moonshot\", ApiKey,\n \"https://api.moonshot.ai/v1\", OpenAI,\n &[\"KIMI_API_KEY\"], Some(\"KIMI_BASE_URL\"));\n define!(\"kimi-coding-cn\", \"Kimi / Moonshot (China)\", ApiKey,\n \"https://api.moonshot.cn/v1\", OpenAI,\n &[\"KIMI_CN_API_KEY\"]);\n define!(\"arcee\", \"Arcee AI\", ApiKey,\n \"https://api.arcee.ai/api/v1\", OpenAI,\n &[\"ARCEEAI_API_KEY\"], Some(\"ARCEE_BASE_URL\"));\n define!(\"minimax\", \"MiniMax\", ApiKey,\n \"https://api.minimax.io/anthropic\", Anthropic,\n &[\"MINIMAX_API_KEY\"], Some(\"MINIMAX_BASE_URL\"));\n define!(\"anthropic\", \"Anthropic\", ApiKey,\n \"https://api.anthropic.com\", Anthropic,\n &[\"ANTHROPIC_API_KEY\", \"ANTHROPIC_TOKEN\", \"CLAUDE_CODE_OAUTH_TOKEN\"]);\n define!(\"alibaba\", \"Alibaba Cloud (DashScope)\", ApiKey,\n \"https://dashscope-intl.aliyuncs.com/compatible-mode/v1\", OpenAI,\n &[\"DASHSCOPE_API_KEY\"], Some(\"DASHSCOPE_BASE_URL\"));\n define!(\"minimax-cn\", \"MiniMax (China)\", ApiKey,\n \"https://api.minimaxi.com/anthropic\", Anthropic,\n &[\"MINIMAX_CN_API_KEY\"], Some(\"MINIMAX_CN_BASE_URL\"));\n define!(\"deepseek\", \"DeepSeek\", ApiKey,\n \"https://api.deepseek.com/v1\", OpenAI,\n &[\"DEEPSEEK_API_KEY\"], Some(\"DEEPSEEK_BASE_URL\"));\n define!(\"xai\", \"xAI\", ApiKey,\n \"https://api.x.ai/v1\", OpenAI,\n &[\"XAI_API_KEY\"], Some(\"XAI_BASE_URL\"));\n define!(\"nvidia\", \"NVIDIA NIM\", ApiKey,\n \"https://integrate.api.nvidia.com/v1\", OpenAI,\n &[\"NVIDIA_API_KEY\"], Some(\"NVIDIA_BASE_URL\"));\n define!(\"ai-gateway\", \"Vercel AI Gateway\", ApiKey,\n \"https://ai-gateway.vercel.sh/v1\", OpenAI,\n &[\"AI_GATEWAY_API_KEY\"], Some(\"AI_GATEWAY_BASE_URL\"));\n define!(\"opencode-zen\", \"OpenCode Zen\", ApiKey,\n \"https://opencode.ai/zen/v1\", OpenAI,\n &[\"OPENCODE_ZEN_API_KEY\"], Some(\"OPENCODE_ZEN_BASE_URL\"));\n define!(\"opencode-go\", \"OpenCode Go\", ApiKey,\n \"https://opencode.ai/zen/go/v1\", OpenAI,\n &[\"OPENCODE_GO_API_KEY\"], Some(\"OPENCODE_GO_BASE_URL\"));\n define!(\"kilocode\", \"Kilo Code\", ApiKey,\n \"https://api.kilo.ai/api/gateway\", OpenAI,\n &[\"KILOCODE_API_KEY\"], Some(\"KILOCODE_BASE_URL\"));\n define!(\"huggingface\", \"Hugging Face\", ApiKey,\n \"https://router.huggingface.co/v1\", OpenAI,\n &[\"HF_TOKEN\"], Some(\"HF_BASE_URL\"));\n define!(\"xiaomi\", \"Xiaomi MiMo\", ApiKey,\n \"https://api.xiaomimimo.com/v1\", OpenAI,\n &[\"XIAOMI_API_KEY\"], Some(\"XIAOMI_BASE_URL\"));\n define!(\"ollama-cloud\", \"Ollama Cloud\", ApiKey,\n \"https://api.ollama.cloud/v1\", OpenAI,\n &[\"OLLAMA_API_KEY\"], Some(\"OLLAMA_BASE_URL\"));\n define!(\"bedrock\", \"AWS Bedrock\", AwsSdk,\n \"https://bedrock-runtime.us-east-1.amazonaws.com\", Anthropic,\n Vec::<String>::new(), Some(\"BEDROCK_BASE_URL\".to_string()));\n\n // ── OpenAI-Compatible Third Party ──\n define!(\"openai\", \"OpenAI\", ApiKey,\n \"https://api.openai.com/v1\", OpenAI,\n &[\"OPENAI_API_KEY\"]);\n define!(\"groq\", \"Groq\", ApiKey,\n \"https://api.groq.com/openai/v1\", OpenAI,\n &[\"GROQ_API_KEY\"]);\n define!(\"openrouter\", \"OpenRouter\", ApiKey,\n \"https://openrouter.ai/api/v1\", OpenAI,\n &[\"OPENROUTER_API_KEY\"]);\n define!(\"fireworks\", \"Fireworks\", ApiKey,\n \"https://api.fireworks.ai/inference/v1\", OpenAI,\n &[\"FIREWORKS_API_KEY\"]);\n define!(\"together\", \"Together\", ApiKey,\n \"https://api.together.xyz/v1\", OpenAI,\n &[\"TOGETHER_API_KEY\"]);\n define!(\"perplexity\", \"Perplexity\", ApiKey,\n \"https://api.perplexity.ai\", OpenAI,\n &[\"PERPLEXITY_API_KEY\"]);\n define!(\"mistral\", \"Mistral\", ApiKey,\n \"https://api.mistral.ai/v1\", OpenAI,\n &[\"MISTRAL_API_KEY\"]);\n define!(\"moonshot\", \"Moonshot\", ApiKey,\n \"https://api.moonshot.cn/v1\", OpenAI,\n &[\"MOONSHOT_API_KEY\"]);\n\n // ── Local / Self-hosted ──\n define!(\"ollama\", \"Ollama\", ApiKey,\n \"http://localhost:11434\", Native,\n Vec::<String>::new(), Some(\"OLLAMA_HOST\".to_string()));\n define!(\"vllm\", \"vLLM\", ApiKey,\n \"http://localhost:8000\", OpenAI,\n &[\"VLLM_API_KEY\"], Some(\"VLLM_HOST\"));\n define!(\"azure\", \"Azure OpenAI\", ApiKey,\n \"https://{resource}.openai.azure.com\", OpenAI,\n &[\"AZURE_OPENAI_API_KEY\"], Some(\"AZURE_OPENAI_BASE_URL\"));\n define!(\"byteplus\", \"BytePlus\", ApiKey,\n \"https://ark.cn-beijing.volces.com\", OpenAI,\n &[\"BYTEPLUS_API_KEY\"], Some(\"BYTEPLUS_ENDPOINT\"));\n define!(\"venice\", \"Venice AI\", ApiKey,\n \"https://api.venice.ai/api/v1\", OpenAI,\n &[\"VENICE_API_KEY\"]);\n\n // ── Custom / Generic / LocalAI / LM Studio / Groq Cloud / Anyscale ──\n define!(\"custom_openai\", \"Custom OpenAI API\", ApiKey,\n \"http://localhost:8080/v1\", OpenAI,\n &[\"CUSTOM_OPENAI_API_KEY\"], Some(\"CUSTOM_OPENAI_BASE_URL\"));\n define!(\"localai\", \"LocalAI\", ApiKey,\n \"http://localhost:8080/v1\", OpenAI,\n &[\"LOCALAI_API_KEY\"], Some(\"LOCALAI_BASE_URL\"));\n define!(\"lmstudio\", \"LM Studio\", ApiKey,\n \"http://localhost:1234/v1\", OpenAI,\n &[\"LMSTUDIO_API_KEY\"], Some(\"LMSTUDIO_BASE_URL\"));\n define!(\"groqcloud\", \"Groq Cloud\", ApiKey,\n \"https://api.groq.com/openai/v1\", OpenAI,\n &[\"GROQ_API_KEY\"]);\n define!(\"anyscale\", \"Anyscale\", ApiKey,\n \"https://api.endpoints.anyscale.com/v1\", OpenAI,\n &[\"ANYSCALE_API_KEY\"]);\n\n Self { providers }\n }\n\n pub fn get(&self, id: &str) -> Option<Arc<ProviderConfig>> {\n self.providers.get(id).cloned()\n }\n\n pub fn list_ids(&self) -> Vec<String> {\n self.providers.keys().map(|k| k.to_string()).collect()\n }\n\n pub fn list_all(&self) -> Vec<Arc<ProviderConfig>> {\n self.providers.values().cloned().collect()\n }\n\n pub fn resolve_base_url(&self, id: &str) -> Option<String> {\n let config = self.get(id)?;\n \n // 1. Check environment variable override\n if let Some(ref var) = config.base_url_env_var {\n if let Ok(val) = std::env::var(var) {\n if !val.is_empty() {\n return Some(val);\n }\n }\n }\n \n // 2. Check config.json user override under \"api_keys\" as `{id}_base_url` or `{id}`\n if let Ok(user_cfg) = crate::load_config_inner() {\n let base_url_key = format!(\"{}_base_url\", id);\n if let Some(val) = user_cfg.api_keys.get(&base_url_key) {\n if !val.is_empty() {\n return Some(val.clone());\n }\n }\n // Fallback: user entered the URL in the API key field directly (e.g. they put custom URL directly in \"custom_openai\")\n if let Some(val) = user_cfg.api_keys.get(id) {\n if val.starts_with(\"http://\") || val.starts_with(\"https://\") {\n return Some(val.clone());\n }\n }\n }\n \n Some(config.inference_base_url.clone())\n }\n\n pub fn resolve_api_key(&self, id: &str) -> Option<String> {\n let config = self.get(id)?;\n for env_var in &config.api_key_env_vars {\n if let Ok(val) = std::env::var(env_var) {\n if !val.is_empty() {\n return Some(val);\n }\n }\n }\n None\n }\n}\n"}, "299afdf57599f6ad07028b815d529c76e11d9d14": {"t": 1785757075.851942, "n": 11552, "h": "de0beb5fbc41dbfd", "c": "use serde::{Deserialize, Serialize};\nuse std::collections::HashMap;\nuse std::sync::Arc;\n\n#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]\npub enum AuthType {\n ApiKey,\n OAuthDeviceCode,\n OAuthExternal,\n ExternalProcess,\n AwsSdk,\n}\n\n#[derive(Debug, Clone, Serialize, Deserialize)]\npub struct ProviderConfig {\n pub id: String,\n pub name: String,\n pub auth_type: AuthType,\n pub inference_base_url: String,\n pub api_key_env_vars: Vec<String>,\n pub base_url_env_var: Option<String>,\n pub api_mode: ApiMode,\n}\n\n#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]\npub enum ApiMode {\n /// OpenAI-compatible /v1/chat/completions\n OpenAI,\n /// Anthropic Messages API /v1/messages\n Anthropic,\n /// Google Gemini generateContent\n Gemini,\n /// Native format (e.g. MiniMax)\n Native,\n}\n\nimpl ProviderConfig {\n pub fn api_key_env_vars(&self) -> &[String] {\n &self.api_key_env_vars\n }\n}\n\npub struct ProviderRegistry {\n providers: HashMap<&'static str, Arc<ProviderConfig>>,\n}\n\nimpl Default for ProviderRegistry {\n fn default() -> Self {\n Self::new()\n }\n}\n\nimpl ProviderRegistry {\n pub fn new() -> Self {\n let mut providers: HashMap<&'static str, Arc<ProviderConfig>> = HashMap::new();\n\n macro_rules! define {\n ($id:expr, $name:expr, $auth:expr, $url:expr, $mode:expr$(,)?) => {\n define!($id, $name, $auth, $url, $mode, Vec::<String>::new(), Option::<String>::None)\n };\n ($id:expr, $name:expr, $auth:expr, $url:expr, $mode:expr, $keys:expr$(,)?) => {\n define!($id, $name, $auth, $url, $mode, $keys, Option::<String>::None)\n };\n ($id:expr, $name:expr, $auth:expr, $url:expr, $mode:expr, $keys:expr, $url_env:expr$(,)?) => {\n providers.insert(\n $id,\n Arc::new(ProviderConfig {\n id: $id.to_string(),\n name: $name.to_string(),\n auth_type: $auth,\n inference_base_url: $url.to_string(),\n api_key_env_vars: $keys.iter().map(|s| s.to_string()).collect(),\n base_url_env_var: $url_env.map(|s| s.to_string()),\n api_mode: $mode,\n }),\n );\n };\n }\n\n use AuthType::*;\n use ApiMode::*;\n\n // ── OAuth Providers ──\n define!(\"nous\", \"Nous Portal\", OAuthDeviceCode, \"https://api.nousresearch.com/v1\", OpenAI);\n define!(\"openai-codex\", \"OpenAI Codex\", OAuthExternal, \"https://api.openai.com/v1\", OpenAI);\n define!(\"qwen-oauth\", \"Qwen OAuth\", OAuthExternal, \"https://dashscope.aliyuncs.com/compatible-mode/v1\", OpenAI);\n define!(\"google-gemini-cli\", \"Google Gemini (OAuth)\", OAuthExternal, \"https://generativelanguage.googleapis.com/v1beta\", Gemini);\n\n // ── API Key Providers ──\n define!(\"copilot\", \"GitHub Copilot\", ApiKey,\n \"https://models.github.ai/inference\", OpenAI,\n &[\"COPILOT_GITHUB_TOKEN\", \"GH_TOKEN\", \"GITHUB_TOKEN\"], Some(\"COPILOT_API_BASE_URL\"));\n define!(\"copilot-acp\", \"GitHub Copilot ACP\", ExternalProcess,\n \"https://api.githubcopilot.com\", OpenAI,\n Vec::<String>::new(), Some(\"COPILOT_ACP_BASE_URL\".to_string()));\n define!(\"google\", \"Google AI Studio\", ApiKey,\n \"https://generativelanguage.googleapis.com/v1beta\", Gemini,\n &[\"GOOGLE_API_KEY\", \"GEMINI_API_KEY\"], Some(\"GEMINI_BASE_URL\"));\n define!(\"zai\", \"Z.AI / GLM\", ApiKey,\n \"https://api.z.ai/api/paas/v4\", OpenAI,\n &[\"GLM_API_KEY\", \"ZAI_API_KEY\", \"Z_AI_API_KEY\"], Some(\"GLM_BASE_URL\"));\n define!(\"kimi-coding\", \"Kimi / Moonshot\", ApiKey,\n \"https://api.moonshot.ai/v1\", OpenAI,\n &[\"KIMI_API_KEY\"], Some(\"KIMI_BASE_URL\"));\n define!(\"kimi-coding-cn\", \"Kimi / Moonshot (China)\", ApiKey,\n \"https://api.moonshot.cn/v1\", OpenAI,\n &[\"KIMI_CN_API_KEY\"]);\n define!(\"arcee\", \"Arcee AI\", ApiKey,\n \"https://api.arcee.ai/api/v1\", OpenAI,\n &[\"ARCEEAI_API_KEY\"], Some(\"ARCEE_BASE_URL\"));\n define!(\"minimax\", \"MiniMax\", ApiKey,\n \"https://api.minimax.io/anthropic\", Anthropic,\n &[\"MINIMAX_API_KEY\"], Some(\"MINIMAX_BASE_URL\"));\n define!(\"anthropic\", \"Anthropic\", ApiKey,\n \"https://api.anthropic.com\", Anthropic,\n &[\"ANTHROPIC_API_KEY\", \"ANTHROPIC_TOKEN\", \"CLAUDE_CODE_OAUTH_TOKEN\"]);\n define!(\"alibaba\", \"Alibaba Cloud (DashScope)\", ApiKey,\n \"https://dashscope-intl.aliyuncs.com/compatible-mode/v1\", OpenAI,\n &[\"DASHSCOPE_API_KEY\"], Some(\"DASHSCOPE_BASE_URL\"));\n define!(\"minimax-cn\", \"MiniMax (China)\", ApiKey,\n \"https://api.minimaxi.com/anthropic\", Anthropic,\n &[\"MINIMAX_CN_API_KEY\"], Some(\"MINIMAX_CN_BASE_URL\"));\n define!(\"deepseek\", \"DeepSeek\", ApiKey,\n \"https://api.deepseek.com/v1\", OpenAI,\n &[\"DEEPSEEK_API_KEY\"], Some(\"DEEPSEEK_BASE_URL\"));\n define!(\"xai\", \"xAI\", ApiKey,\n \"https://api.x.ai/v1\", OpenAI,\n &[\"XAI_API_KEY\"], Some(\"XAI_BASE_URL\"));\n define!(\"nvidia\", \"NVIDIA NIM\", ApiKey,\n \"https://integrate.api.nvidia.com/v1\", OpenAI,\n &[\"NVIDIA_API_KEY\"], Some(\"NVIDIA_BASE_URL\"));\n define!(\"ai-gateway\", \"Vercel AI Gateway\", ApiKey,\n \"https://ai-gateway.vercel.sh/v1\", OpenAI,\n &[\"AI_GATEWAY_API_KEY\"], Some(\"AI_GATEWAY_BASE_URL\"));\n define!(\"opencode-zen\", \"OpenCode Zen\", ApiKey,\n \"https://opencode.ai/zen/v1\", OpenAI,\n &[\"OPENCODE_ZEN_API_KEY\"], Some(\"OPENCODE_ZEN_BASE_URL\"));\n define!(\"opencode-go\", \"OpenCode Go\", ApiKey,\n \"https://opencode.ai/zen/go/v1\", OpenAI,\n &[\"OPENCODE_GO_API_KEY\"], Some(\"OPENCODE_GO_BASE_URL\"));\n define!(\"kilocode\", \"Kilo Code\", ApiKey,\n \"https://api.kilo.ai/api/gateway\", OpenAI,\n &[\"KILOCODE_API_KEY\"], Some(\"KILOCODE_BASE_URL\"));\n define!(\"huggingface\", \"Hugging Face\", ApiKey,\n \"https://router.huggingface.co/v1\", OpenAI,\n &[\"HF_TOKEN\"], Some(\"HF_BASE_URL\"));\n define!(\"xiaomi\", \"Xiaomi MiMo\", ApiKey,\n \"https://api.xiaomimimo.com/v1\", OpenAI,\n &[\"XIAOMI_API_KEY\"], Some(\"XIAOMI_BASE_URL\"));\n define!(\"ollama-cloud\", \"Ollama Cloud\", ApiKey,\n \"https://api.ollama.cloud/v1\", OpenAI,\n &[\"OLLAMA_API_KEY\"], Some(\"OLLAMA_BASE_URL\"));\n define!(\"bedrock\", \"AWS Bedrock\", AwsSdk,\n \"https://bedrock-runtime.us-east-1.amazonaws.com\", Anthropic,\n Vec::<String>::new(), Some(\"BEDROCK_BASE_URL\".to_string()));\n\n // ── OpenAI-Compatible Third Party ──\n define!(\"openai\", \"OpenAI\", ApiKey,\n \"https://api.openai.com/v1\", OpenAI,\n &[\"OPENAI_API_KEY\"]);\n define!(\"groq\", \"Groq\", ApiKey,\n \"https://api.groq.com/openai/v1\", OpenAI,\n &[\"GROQ_API_KEY\"]);\n define!(\"openrouter\", \"OpenRouter\", ApiKey,\n \"https://openrouter.ai/api/v1\", OpenAI,\n &[\"OPENROUTER_API_KEY\"]);\n define!(\"fireworks\", \"Fireworks\", ApiKey,\n \"https://api.fireworks.ai/inference/v1\", OpenAI,\n &[\"FIREWORKS_API_KEY\"]);\n define!(\"together\", \"Together\", ApiKey,\n \"https://api.together.xyz/v1\", OpenAI,\n &[\"TOGETHER_API_KEY\"]);\n define!(\"perplexity\", \"Perplexity\", ApiKey,\n \"https://api.perplexity.ai\", OpenAI,\n &[\"PERPLEXITY_API_KEY\"]);\n define!(\"mistral\", \"Mistral\", ApiKey,\n \"https://api.mistral.ai/v1\", OpenAI,\n &[\"MISTRAL_API_KEY\"]);\n define!(\"moonshot\", \"Moonshot\", ApiKey,\n \"https://api.moonshot.cn/v1\", OpenAI,\n &[\"MOONSHOT_API_KEY\"]);\n\n // ── Local / Self-hosted ──\n define!(\"ollama\", \"Ollama\", ApiKey,\n \"http://localhost:11434\", Native,\n Vec::<String>::new(), Some(\"OLLAMA_HOST\".to_string()));\n define!(\"vllm\", \"vLLM\", ApiKey,\n \"http://localhost:8000\", OpenAI,\n &[\"VLLM_API_KEY\"], Some(\"VLLM_HOST\"));\n define!(\"azure\", \"Azure OpenAI\", ApiKey,\n \"https://{resource}.openai.azure.com\", OpenAI,\n &[\"AZURE_OPENAI_API_KEY\"], Some(\"AZURE_OPENAI_BASE_URL\"));\n define!(\"byteplus\", \"BytePlus\", ApiKey,\n \"https://ark.cn-beijing.volces.com\", OpenAI,\n &[\"BYTEPLUS_API_KEY\"], Some(\"BYTEPLUS_ENDPOINT\"));\n define!(\"venice\", \"Venice AI\", ApiKey,\n \"https://api.venice.ai/api/v1\", OpenAI,\n &[\"VENICE_API_KEY\"]);\n\n // ── Custom / Generic / LocalAI / LM Studio / Groq Cloud / Anyscale ──\n define!(\"custom_openai\", \"Custom OpenAI API\", ApiKey,\n \"http://localhost:8080/v1\", OpenAI,\n &[\"CUSTOM_OPENAI_API_KEY\"], Some(\"CUSTOM_OPENAI_BASE_URL\"));\n define!(\"localai\", \"LocalAI\", ApiKey,\n \"http://localhost:8080/v1\", OpenAI,\n &[\"LOCALAI_API_KEY\"], Some(\"LOCALAI_BASE_URL\"));\n define!(\"lmstudio\", \"LM Studio\", ApiKey,\n \"http://localhost:1234/v1\", OpenAI,\n &[\"LMSTUDIO_API_KEY\"], Some(\"LMSTUDIO_BASE_URL\"));\n define!(\"groqcloud\", \"Groq Cloud\", ApiKey,\n \"https://api.groq.com/openai/v1\", OpenAI,\n &[\"GROQ_API_KEY\"]);\n define!(\"anyscale\", \"Anyscale\", ApiKey,\n \"https://api.endpoints.anyscale.com/v1\", OpenAI,\n &[\"ANYSCALE_API_KEY\"]);\n\n Self { providers }\n }\n\n pub fn get(&self, id: &str) -> Option<Arc<ProviderConfig>> {\n self.providers.get(id).cloned()\n }\n\n pub fn list_ids(&self) -> Vec<String> {\n self.providers.keys().map(|k| k.to_string()).collect()\n }\n\n pub fn list_all(&self) -> Vec<Arc<ProviderConfig>> {\n self.providers.values().cloned().collect()\n }\n\n pub fn resolve_base_url(&self, id: &str) -> Option<String> {\n let config = self.get(id)?;\n \n // 1. Check environment variable override\n if let Some(ref var) = config.base_url_env_var {\n if let Ok(val) = std::env::var(var) {\n if !val.is_empty() {\n return Some(val);\n }\n }\n }\n \n // 2. Check config.json user override under \"api_keys\" as `{id}_base_url` or `{id}`\n if let Ok(user_cfg) = crate::load_config_inner() {\n let base_url_key = format!(\"{}_base_url\", id);\n if let Some(val) = user_cfg.api_keys.get(&base_url_key) {\n if !val.is_empty() {\n return Some(val.clone());\n }\n }\n // Fallback: user entered the URL in the API key field directly (e.g. they put custom URL directly in \"custom_openai\")\n if let Some(val) = user_cfg.api_keys.get(id) {\n if val.starts_with(\"http://\") || val.starts_with(\"https://\") {\n return Some(val.clone());\n }\n }\n }\n \n Some(config.inference_base_url.clone())\n }\n\n pub fn resolve_api_key(&self, id: &str) -> Option<String> {\n let config = self.get(id)?;\n for env_var in &config.api_key_env_vars {\n if let Ok(val) = std::env::var(env_var) {\n if !val.is_empty() {\n return Some(val);\n }\n }\n }\n None\n }\n}\n"}}}} |