Skip to content

Commit 2ae6253

Browse files
author
Bot
committed
Add multiple LLM providers: OpenAI, Anthropic, Google, Groq, Ollama
1 parent c31ba3e commit 2ae6253

4 files changed

Lines changed: 44 additions & 17 deletions

File tree

api/src/commonMain/kotlin/dev/materii/gloom/api/service/ai/AIService.kt

Lines changed: 34 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,11 @@ import io.ktor.http.isSuccess
2020
import 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
*/
2929
class 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
}

ui/src/commonMain/kotlin/dev/materii/gloom/ui/screen/ai/AIScreen.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ class AIScreen : Tab {
128128
style = MaterialTheme.typography.titleMedium
129129
)
130130
Text(
131-
text = "Add your API key in Settings → AI Settings to start chatting.\n\nGet a free key at siliconflow.cn",
131+
text = "Add your API key in Settings → AI Settings to start chatting.\n\nUse SiliconFlow (free) or other OpenAI-compatible providers.",
132132
style = MaterialTheme.typography.bodyMedium,
133133
color = MaterialTheme.colorScheme.onSurfaceVariant,
134134
textAlign = androidx.compose.ui.text.style.TextAlign.Center

ui/src/commonMain/kotlin/dev/materii/gloom/ui/screen/ai/viewmodel/AIViewModel.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ class AIViewModel(
7575
val apiMessages = _messages.toList()
7676

7777
if (!aiService.hasApiKey()) {
78-
error = "No API key set. Open Settings → AI Settings and add your key from siliconflow.cn."
78+
error = "No API key set. Open Settings → AI Settings and add your API key."
7979
isLoading = false
8080
return@launch
8181
}

ui/src/commonMain/kotlin/dev/materii/gloom/ui/screen/settings/AISettingsScreen.kt

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -260,11 +260,14 @@ private fun StepsCard() {
260260
Text("How to get your API key",
261261
style = MaterialTheme.typography.titleSmall)
262262
Text(
263-
"1. Open siliconflow.cn in your browser\n" +
264-
"2. Sign up or log in to your account\n" +
265-
"3. Go to Settings → API Keys\n" +
266-
"4. Copy your free API key\n" +
267-
"5. Paste it in the field below and tap Save",
263+
"SiliconFlow (Free):\n" +
264+
"1. Open siliconflow.cn\n" +
265+
"2. Sign up and go to Settings → API Keys\n" +
266+
"3. Copy your free API key\n\n" +
267+
"Other Providers (OpenAI, Anthropic, Google, etc.):\n" +
268+
"1. Get API key from your provider\n" +
269+
"2. Update base URL if different\n" +
270+
"3. Paste key and tap Save",
268271
style = MaterialTheme.typography.bodySmall,
269272
color = MaterialTheme.colorScheme.onSurfaceVariant,
270273
)

0 commit comments

Comments
 (0)