@@ -20,11 +20,11 @@ import io.ktor.http.isSuccess
2020import kotlinx.serialization.json.Json
2121
2222/* *
23- * AI Service backed by the SiliconFlow API (OpenAI-compatible).
23+ * AI Service supporting multiple LLM providers (OpenAI-compatible).
2424 *
25- * Default endpoint: https://api.siliconflow.cn/v1/chat/completions
25+ * Default: SiliconFlow (free Qwen models)
26+ * Supported: OpenAI, Anthropic, Google, Groq, etc.
2627 * Users can override the base URL in Settings → AI Settings.
27- * Free Qwen models available via SiliconFlow API.
2828 */
2929class AIService (
3030 private val httpClient : HttpClient ,
@@ -34,15 +34,39 @@ class AIService(
3434) {
3535
3636 companion object {
37+ // Default: SiliconFlow (free)
3738 const val DEFAULT_BASE_URL = " https://api.siliconflow.cn/v1"
38- // Qwen 3.6 Plus Free model
3939 const val DEFAULT_MODEL = " Qwen/Qwen3-30B-A3B:free"
4040
4141 val AVAILABLE_MODELS = listOf (
42- ModelInfo (" Qwen/Qwen3-30B-A3B:free" , " Qwen 3.6 Plus (Free)" , " Alibaba" , " Free — latest Qwen3" ),
43- ModelInfo (" Qwen/Qwen2.5-Coder-32B-Instruct:free" , " Qwen 2.5 Coder (Free)" , " Alibaba" , " Free — coding specialized" ),
44- ModelInfo (" Qwen/Qwen2.5-72B-Instruct" , " Qwen 2.5 72B" , " Alibaba" , " Flagship model" ),
45- ModelInfo (" deepseek-ai/DeepSeek-V2.5:free" , " DeepSeek V2.5 (Free)" , " DeepSeek" , " Free tier" ),
42+ // SiliconFlow (Free)
43+ ModelInfo (" Qwen/Qwen3-30B-A3B:free" , " Qwen 3.6 Plus (Free)" , " Alibaba" , " Free" ),
44+ ModelInfo (" Qwen/Qwen2.5-Coder-32B-Instruct:free" , " Qwen 2.5 Coder (Free)" , " Alibaba" , " Free - coding" ),
45+ ModelInfo (" deepseek-ai/DeepSeek-V2.5:free" , " DeepSeek V2.5 (Free)" , " DeepSeek" , " Free" ),
46+ ModelInfo (" THUDM/GLM-4-Flash:free" , " GLM-4 Flash (Free)" , " Zhipu AI" , " Free" ),
47+
48+ // OpenAI (requires API key)
49+ ModelInfo (" gpt-4o" , " GPT-4o" , " OpenAI" , " Latest flagship" ),
50+ ModelInfo (" gpt-4o-mini" , " GPT-4o Mini" , " OpenAI" , " Fast & cheap" ),
51+ ModelInfo (" gpt-4-turbo" , " GPT-4 Turbo" , " OpenAI" , " Powerful" ),
52+
53+ // Anthropic (requires API key)
54+ ModelInfo (" claude-sonnet-4-20250514" , " Claude Sonnet 4" , " Anthropic" , " Latest" ),
55+ ModelInfo (" claude-3-5-sonnet-20241022" , " Claude 3.5 Sonnet" , " Anthropic" , " Popular" ),
56+ ModelInfo (" claude-3-haiku-20240307" , " Claude 3 Haiku" , " Anthropic" , " Fast" ),
57+
58+ // Google (requires API key)
59+ ModelInfo (" gemini-1.5-pro" , " Gemini 1.5 Pro" , " Google" , " Multimodal" ),
60+ ModelInfo (" gemini-1.5-flash" , " Gemini 1.5 Flash" , " Google" , " Fast" ),
61+
62+ // Groq (requires API key)
63+ ModelInfo (" llama-3.1-70b-versatile" , " Llama 3.1 70B" , " Groq" , " Fast" ),
64+ ModelInfo (" mixtral-8x7b-32768" , " Mixtral 8x7B" , " Groq" , " Efficient" ),
65+
66+ // Ollama (local/self-hosted)
67+ ModelInfo (" ollama/llama3" , " Llama 3" , " Ollama" , " Local" ),
68+ ModelInfo (" ollama/codellama" , " Code Llama" , " Ollama" , " Local coding" ),
69+ ModelInfo (" ollama/mistral" , " Mistral" , " Ollama" , " Local" ),
4670 )
4771 }
4872
@@ -73,7 +97,7 @@ class AIService(
7397 val key = apiKey()
7498 if (key.isBlank()) {
7599 return ApiResponse .Error (
76- ApiError (HttpStatusCode .Unauthorized , " No Z.AI API key configured. Add one in Settings → AI Settings." )
100+ ApiError (HttpStatusCode .Unauthorized , " No API key configured. Add one in Settings → AI Settings." )
77101 )
78102 }
79103
@@ -94,7 +118,7 @@ class AIService(
94118 val parsed = json.decodeFromString<ChatCompletionResponse >(body)
95119 val text = parsed.choices.firstOrNull()?.message?.content?.trim() ? : " "
96120 if (text.isNotBlank()) ApiResponse .Success (parsed)
97- else ApiResponse .Error (ApiError (response.status, " Empty response from Z. AI" ))
121+ else ApiResponse .Error (ApiError (response.status, " Empty response from AI" ))
98122 } else {
99123 ApiResponse .Error (ApiError (response.status, " ${response.status.value} : ${body.take(200 )} " ))
100124 }
0 commit comments