diff --git a/desktop-shared/src/main/resources/i18n/messages.properties b/desktop-shared/src/main/resources/i18n/messages.properties index 81c238af5..0f262cfe0 100644 --- a/desktop-shared/src/main/resources/i18n/messages.properties +++ b/desktop-shared/src/main/resources/i18n/messages.properties @@ -979,6 +979,11 @@ provider.template.openrouter.tagline=Access 300+ models — GPT, Claude, Llama, provider.template.openrouter.help=💡 To use OpenRouter:\n\n1. Get your free API key at: openrouter.ai/keys\n2. Click Next to browse 300+ available models\n3. Many models have a free tier — no credit card needed provider.template.together_ai.tagline=Fast, affordable inference for 100+ open-source models. provider.template.together_ai.help=💡 To use Together AI:\n\n1. Get your API key at: api.together.ai/settings/api-keys\n2. Click Next to browse available models,\n e.g. meta-llama/Llama-3-8b-chat-hf +provider.template.lite_llm.tagline=A unified proxy that lets you call 100+ LLMs using the OpenAI format. +provider.search.placeholder=Search providers... +provider.search.no_results=No providers found +provider.template.lite_llm.help=💡 To use LiteLLM:\n\n1. Start the LiteLLM proxy: litellm --model ollama/llama3\n2. The default URL is http://localhost:4000/v1\n3. No API key is needed for local setups (set any value if prompted) + provider.configure.new.title=Configure {0} provider.edit.title=Edit "{0}" diff --git a/desktop-shared/src/main/resources/i18n/messages_de.properties b/desktop-shared/src/main/resources/i18n/messages_de.properties index 2f6dea619..9a882bd0a 100644 --- a/desktop-shared/src/main/resources/i18n/messages_de.properties +++ b/desktop-shared/src/main/resources/i18n/messages_de.properties @@ -979,6 +979,10 @@ provider.template.openrouter.tagline=Zugriff auf über 300 Modelle — GPT, Clau provider.template.openrouter.help=💡 So verwenden Sie OpenRouter:\n\n1. Holen Sie sich Ihren kostenlosen API-Schlüssel unter: openrouter.ai/keys\n2. Klicken Sie auf Weiter, um über 300 verfügbare Modelle zu durchsuchen\n3. Viele Modelle bieten eine kostenlose Stufe — keine Kreditkarte erforderlich provider.template.together_ai.tagline=Schnelle, erschwingliche Inferenz für über 100 Open-Source-Modelle. provider.template.together_ai.help=💡 So verwenden Sie Together AI:\n\n1. Holen Sie sich Ihren API-Schlüssel unter: api.together.ai/settings/api-keys\n2. Klicken Sie auf Weiter, um verfügbare Modelle zu durchsuchen,\n z. B. meta-llama/Llama-3-8b-chat-hf +provider.template.lite_llm.tagline=A unified proxy that lets you call 100+ LLMs using the OpenAI format. +provider.search.placeholder=Search providers... +provider.search.no_results=No providers found +provider.template.lite_llm.help=💡 To use LiteLLM:\n\n1. Start the LiteLLM proxy: litellm --model ollama/llama3\n2. The default URL is http://localhost:4000/v1\n3. No API key is needed for local setups (set any value if prompted) provider.configure.new.title=Configure {0} provider.edit.title=Edit "{0}" diff --git a/desktop-shared/src/main/resources/i18n/messages_es.properties b/desktop-shared/src/main/resources/i18n/messages_es.properties index 72d4ee679..c04836fee 100644 --- a/desktop-shared/src/main/resources/i18n/messages_es.properties +++ b/desktop-shared/src/main/resources/i18n/messages_es.properties @@ -979,6 +979,10 @@ provider.template.openrouter.tagline=Accede a más de 300 modelos — GPT, Claud provider.template.openrouter.help=💡 Para usar OpenRouter:\n\n1. Consigue tu clave API gratuita en: openrouter.ai/keys\n2. Haz clic en Siguiente para explorar más de 300 modelos disponibles\n3. Muchos modelos tienen un nivel gratuito — no se requiere tarjeta de crédito provider.template.together_ai.tagline=Inferencia rápida y económica para más de 100 modelos de código abierto. provider.template.together_ai.help=💡 Para usar Together AI:\n\n1. Consigue tu clave API en: api.together.ai/settings/api-keys\n2. Haz clic en Siguiente para explorar los modelos disponibles,\n p. ej. meta-llama/Llama-3-8b-chat-hf +provider.template.lite_llm.tagline=A unified proxy that lets you call 100+ LLMs using the OpenAI format. +provider.search.placeholder=Search providers... +provider.search.no_results=No providers found +provider.template.lite_llm.help=💡 To use LiteLLM:\n\n1. Start the LiteLLM proxy: litellm --model ollama/llama3\n2. The default URL is http://localhost:4000/v1\n3. No API key is needed for local setups (set any value if prompted) provider.configure.new.title=Configure {0} provider.edit.title=Edit "{0}" diff --git a/desktop-shared/src/main/resources/i18n/messages_fr.properties b/desktop-shared/src/main/resources/i18n/messages_fr.properties index 2646913b4..46736c9ab 100644 --- a/desktop-shared/src/main/resources/i18n/messages_fr.properties +++ b/desktop-shared/src/main/resources/i18n/messages_fr.properties @@ -979,6 +979,10 @@ provider.template.openrouter.tagline=Accédez à plus de 300 modèles — GPT, C provider.template.openrouter.help=💡 Pour utiliser OpenRouter :\n\n1. Obtenez votre clé API gratuite sur : openrouter.ai/keys\n2. Cliquez sur Suivant pour parcourir plus de 300 modèles disponibles\n3. De nombreux modèles offrent un niveau gratuit — aucune carte de crédit requise provider.template.together_ai.tagline=Inférence rapide et abordable pour plus de 100 modèles open source. provider.template.together_ai.help=💡 Pour utiliser Together AI :\n\n1. Obtenez votre clé API sur : api.together.ai/settings/api-keys\n2. Cliquez sur Suivant pour parcourir les modèles disponibles,\n par ex. meta-llama/Llama-3-8b-chat-hf +provider.template.lite_llm.tagline=A unified proxy that lets you call 100+ LLMs using the OpenAI format. +provider.search.placeholder=Search providers... +provider.search.no_results=No providers found +provider.template.lite_llm.help=💡 To use LiteLLM:\n\n1. Start the LiteLLM proxy: litellm --model ollama/llama3\n2. The default URL is http://localhost:4000/v1\n3. No API key is needed for local setups (set any value if prompted) provider.configure.new.title=Configure {0} provider.edit.title=Edit "{0}" diff --git a/desktop-shared/src/main/resources/i18n/messages_ja_JP.properties b/desktop-shared/src/main/resources/i18n/messages_ja_JP.properties index 4b5d13894..fef6d0f4a 100644 --- a/desktop-shared/src/main/resources/i18n/messages_ja_JP.properties +++ b/desktop-shared/src/main/resources/i18n/messages_ja_JP.properties @@ -979,6 +979,10 @@ provider.template.openrouter.tagline=300 以上のモデルにアクセス — G provider.template.openrouter.help=💡 OpenRouter の使い方:\n\n1. openrouter.ai/keys で無料の API キーを取得\n2. 「次へ」をクリックして 300 以上の利用可能なモデルを閲覧\n3. 多くのモデルに無料プランあり — クレジットカード不要 provider.template.together_ai.tagline=100 以上のオープンソースモデル向けの高速で手頃な推論。 provider.template.together_ai.help=💡 Together AI の使い方:\n\n1. api.together.ai/settings/api-keys で API キーを取得\n2. 「次へ」をクリックして利用可能なモデルを閲覧\n (例: meta-llama/Llama-3-8b-chat-hf) +provider.template.lite_llm.tagline=A unified proxy that lets you call 100+ LLMs using the OpenAI format. +provider.search.placeholder=Search providers... +provider.search.no_results=No providers found +provider.template.lite_llm.help=💡 To use LiteLLM:\n\n1. Start the LiteLLM proxy: litellm --model ollama/llama3\n2. The default URL is http://localhost:4000/v1\n3. No API key is needed for local setups (set any value if prompted) provider.configure.new.title=Configure {0} provider.edit.title=Edit "{0}" diff --git a/desktop-shared/src/main/resources/i18n/messages_ko_KR.properties b/desktop-shared/src/main/resources/i18n/messages_ko_KR.properties index fb00e44a7..03e29f20c 100644 --- a/desktop-shared/src/main/resources/i18n/messages_ko_KR.properties +++ b/desktop-shared/src/main/resources/i18n/messages_ko_KR.properties @@ -979,6 +979,10 @@ provider.template.openrouter.tagline=300개 이상의 모델에 액세스 — GP provider.template.openrouter.help=💡 OpenRouter 사용 방법:\n\n1. openrouter.ai/keys 에서 무료 API 키를 받으세요\n2. '다음'을 클릭하여 300개 이상의 사용 가능한 모델을 둘러보세요\n3. 많은 모델이 무료 티어를 제공합니다 — 신용카드 불필요 provider.template.together_ai.tagline=100개 이상의 오픈소스 모델을 위한 빠르고 저렴한 추론. provider.template.together_ai.help=💡 Together AI 사용 방법:\n\n1. api.together.ai/settings/api-keys 에서 API 키를 받으세요\n2. '다음'을 클릭하여 사용 가능한 모델을 둘러보세요\n (예: meta-llama/Llama-3-8b-chat-hf) +provider.template.lite_llm.tagline=A unified proxy that lets you call 100+ LLMs using the OpenAI format. +provider.search.placeholder=Search providers... +provider.search.no_results=No providers found +provider.template.lite_llm.help=💡 To use LiteLLM:\n\n1. Start the LiteLLM proxy: litellm --model ollama/llama3\n2. The default URL is http://localhost:4000/v1\n3. No API key is needed for local setups (set any value if prompted) provider.configure.new.title=Configure {0} provider.edit.title=Edit "{0}" diff --git a/desktop-shared/src/main/resources/i18n/messages_pt_BR.properties b/desktop-shared/src/main/resources/i18n/messages_pt_BR.properties index 4b2a72f05..a66f82fc8 100644 --- a/desktop-shared/src/main/resources/i18n/messages_pt_BR.properties +++ b/desktop-shared/src/main/resources/i18n/messages_pt_BR.properties @@ -979,6 +979,10 @@ provider.template.openrouter.tagline=Acesse mais de 300 modelos — GPT, Claude, provider.template.openrouter.help=💡 Para usar o OpenRouter:\n\n1. Obtenha sua chave API gratuita em: openrouter.ai/keys\n2. Clique em Avançar para explorar mais de 300 modelos disponíveis\n3. Muitos modelos têm um nível gratuito — sem necessidade de cartão de crédito provider.template.together_ai.tagline=Inferência rápida e acessível para mais de 100 modelos de código aberto. provider.template.together_ai.help=💡 Para usar o Together AI:\n\n1. Obtenha sua chave API em: api.together.ai/settings/api-keys\n2. Clique em Avançar para explorar os modelos disponíveis,\n ex.: meta-llama/Llama-3-8b-chat-hf +provider.template.lite_llm.tagline=A unified proxy that lets you call 100+ LLMs using the OpenAI format. +provider.search.placeholder=Search providers... +provider.search.no_results=No providers found +provider.template.lite_llm.help=💡 To use LiteLLM:\n\n1. Start the LiteLLM proxy: litellm --model ollama/llama3\n2. The default URL is http://localhost:4000/v1\n3. No API key is needed for local setups (set any value if prompted) provider.configure.new.title=Configure {0} provider.edit.title=Edit "{0}" diff --git a/desktop-shared/src/main/resources/i18n/messages_vi_VN.properties b/desktop-shared/src/main/resources/i18n/messages_vi_VN.properties index e72f15b4f..89cdd1864 100644 --- a/desktop-shared/src/main/resources/i18n/messages_vi_VN.properties +++ b/desktop-shared/src/main/resources/i18n/messages_vi_VN.properties @@ -979,6 +979,10 @@ provider.template.openrouter.tagline=Truy cập hơn 300 mô hình — GPT, Clau provider.template.openrouter.help=💡 Để dùng OpenRouter:\n\n1. Lấy API key miễn phí tại: openrouter.ai/keys\n2. Nhấp Tiếp theo để duyệt hơn 300 mô hình có sẵn\n3. Nhiều mô hình có gói miễn phí — không cần thẻ tín dụng provider.template.together_ai.tagline=Suy luận nhanh, giá cả phải chăng cho hơn 100 mô hình mã nguồn mở. provider.template.together_ai.help=💡 Để dùng Together AI:\n\n1. Lấy API key tại: api.together.ai/settings/api-keys\n2. Nhấp Tiếp theo để duyệt các mô hình có sẵn,\n ví dụ: meta-llama/Llama-3-8b-chat-hf +provider.template.lite_llm.tagline=A unified proxy that lets you call 100+ LLMs using the OpenAI format. +provider.search.placeholder=Search providers... +provider.search.no_results=No providers found +provider.template.lite_llm.help=💡 To use LiteLLM:\n\n1. Start the LiteLLM proxy: litellm --model ollama/llama3\n2. The default URL is http://localhost:4000/v1\n3. No API key is needed for local setups (set any value if prompted) provider.configure.new.title=Configure {0} provider.edit.title=Edit "{0}" diff --git a/desktop-shared/src/main/resources/i18n/messages_zh_CN.properties b/desktop-shared/src/main/resources/i18n/messages_zh_CN.properties index 0550276a1..765519fa5 100644 --- a/desktop-shared/src/main/resources/i18n/messages_zh_CN.properties +++ b/desktop-shared/src/main/resources/i18n/messages_zh_CN.properties @@ -979,6 +979,10 @@ provider.template.openrouter.tagline=通过一个 API 访问 300 多个模型 provider.template.openrouter.help=💡 使用 OpenRouter:\n\n1. 在 openrouter.ai/keys 获取免费 API 密钥\n2. 点击"下一步"浏览 300 多个可用模型\n3. 许多模型提供免费额度 — 无需信用卡 provider.template.together_ai.tagline=为 100 多个开源模型提供快速、经济的推理。 provider.template.together_ai.help=💡 使用 Together AI:\n\n1. 在 api.together.ai/settings/api-keys 获取 API 密钥\n2. 点击"下一步"浏览可用模型,\n 例如 meta-llama/Llama-3-8b-chat-hf +provider.template.lite_llm.tagline=A unified proxy that lets you call 100+ LLMs using the OpenAI format. +provider.search.placeholder=Search providers... +provider.search.no_results=No providers found +provider.template.lite_llm.help=💡 To use LiteLLM:\n\n1. Start the LiteLLM proxy: litellm --model ollama/llama3\n2. The default URL is http://localhost:4000/v1\n3. No API key is needed for local setups (set any value if prompted) provider.configure.new.title=Configure {0} provider.edit.title=Edit "{0}" diff --git a/desktop-shared/src/main/resources/i18n/messages_zh_TW.properties b/desktop-shared/src/main/resources/i18n/messages_zh_TW.properties index 4eea7942b..e9bd5d7d0 100644 --- a/desktop-shared/src/main/resources/i18n/messages_zh_TW.properties +++ b/desktop-shared/src/main/resources/i18n/messages_zh_TW.properties @@ -979,6 +979,10 @@ provider.template.openrouter.tagline=透過單一 API 存取 300 多個模型 provider.template.openrouter.help=💡 若要使用 OpenRouter:\n\n1. 在 openrouter.ai/keys 取得免費 API 金鑰\n2. 按一下「下一步」以瀏覽 300 多個可用模型\n3. 許多模型提供免費方案 — 無需信用卡 provider.template.together_ai.tagline=為 100 多個開源模型提供快速、實惠的推論。 provider.template.together_ai.help=💡 若要使用 Together AI:\n\n1. 在 api.together.ai/settings/api-keys 取得 API 金鑰\n2. 按一下「下一步」以瀏覽可用模型,\n 例如 meta-llama/Llama-3-8b-chat-hf +provider.template.lite_llm.tagline=A unified proxy that lets you call 100+ LLMs using the OpenAI format. +provider.search.placeholder=Search providers... +provider.search.no_results=No providers found +provider.template.lite_llm.help=💡 To use LiteLLM:\n\n1. Start the LiteLLM proxy: litellm --model ollama/llama3\n2. The default URL is http://localhost:4000/v1\n3. No API key is needed for local setups (set any value if prompted) provider.configure.new.title=Configure {0} provider.edit.title=Edit "{0}" diff --git a/desktop/src/main/kotlin/io/askimo/desktop/settings/ProviderSelectionDialog.kt b/desktop/src/main/kotlin/io/askimo/desktop/settings/ProviderSelectionDialog.kt index bb8266fca..86ee85c31 100644 --- a/desktop/src/main/kotlin/io/askimo/desktop/settings/ProviderSelectionDialog.kt +++ b/desktop/src/main/kotlin/io/askimo/desktop/settings/ProviderSelectionDialog.kt @@ -278,47 +278,85 @@ private fun providerTypePickerScreen(viewModel: ProviderWizardViewModel) { val mainEntries = remember(entries) { entries.filterNot { it is ProviderEntry.Custom } } val hasCustom = remember(entries) { entries.any { it is ProviderEntry.Custom } } + var searchQuery by remember(viewModel.wizardStep) { mutableStateOf("") } + val filteredMainEntries = remember(mainEntries, searchQuery) { + if (searchQuery.isBlank()) { + mainEntries + } else { + mainEntries.filter { entry -> + val name = when (entry) { + is ProviderEntry.Native -> entry.provider.name + is ProviderEntry.Template -> entry.template.displayName + is ProviderEntry.Custom -> "" + } + name.contains(searchQuery, ignoreCase = true) + } + } + } + Row( modifier = Modifier .fillMaxWidth() .height(460.dp), ) { - // ── Left column: scrollable provider list ───────────────────────────────────────── + // ── Left column: search field + scrollable provider list ────────────────────────── val listState = rememberLazyListState() - Box(modifier = Modifier.width(260.dp).fillMaxHeight()) { - LazyColumn(state = listState, modifier = Modifier.fillMaxSize()) { - items(mainEntries, key = { entry -> - when (entry) { - is ProviderEntry.Native -> "native_${entry.provider.name}" - is ProviderEntry.Template -> "template_${entry.template.name}" - is ProviderEntry.Custom -> "custom" + Column(modifier = Modifier.width(260.dp).fillMaxHeight()) { + OutlinedTextField( + value = searchQuery, + onValueChange = { searchQuery = it }, + modifier = Modifier.fillMaxWidth().padding(horizontal = Spacing.small, vertical = Spacing.extraSmall), + placeholder = { Text(stringResource("provider.search.placeholder"), style = AppTextStyles.caption) }, + singleLine = true, + colors = AppComponents.outlinedTextFieldColors(), + textStyle = AppTextStyles.caption, + ) + Box(modifier = Modifier.weight(1f)) { + LazyColumn(state = listState, modifier = Modifier.fillMaxSize()) { + if (filteredMainEntries.isEmpty() && searchQuery.isNotBlank()) { + item(key = "no_results") { + Text( + text = stringResource("provider.search.no_results"), + style = AppTextStyles.caption, + color = MaterialTheme.colorScheme.onSurfaceVariant, + modifier = Modifier.padding(horizontal = 12.dp, vertical = Spacing.medium), + ) + } + } else { + items(filteredMainEntries, key = { entry -> + when (entry) { + is ProviderEntry.Native -> "native_${entry.provider.name}" + is ProviderEntry.Template -> "template_${entry.template.name}" + is ProviderEntry.Custom -> "custom" + } + }) { entry -> + providerPickerEntryRow( + entry = entry, + isSelected = viewModel.selectedEntry == entry, + onClick = { viewModel.selectEntryForPreview(entry) }, + ) + } } - }) { entry -> - providerPickerEntryRow( - entry = entry, - isSelected = viewModel.selectedEntry == entry, - onClick = { viewModel.selectEntryForPreview(entry) }, - ) - } - if (hasCustom) { - item(key = "custom_divider") { - HorizontalDivider(modifier = Modifier.padding(vertical = Spacing.extraSmall)) - } - item(key = "custom_entry") { - providerPickerEntryRow( - entry = ProviderEntry.Custom, - isSelected = viewModel.selectedEntry is ProviderEntry.Custom, - onClick = { viewModel.selectEntryForPreview(ProviderEntry.Custom) }, - ) + if (hasCustom) { + item(key = "custom_divider") { + HorizontalDivider(modifier = Modifier.padding(vertical = Spacing.extraSmall)) + } + item(key = "custom_entry") { + providerPickerEntryRow( + entry = ProviderEntry.Custom, + isSelected = viewModel.selectedEntry is ProviderEntry.Custom, + onClick = { viewModel.selectEntryForPreview(ProviderEntry.Custom) }, + ) + } } } + VerticalScrollbar( + adapter = rememberScrollbarAdapter(listState), + modifier = Modifier.align(Alignment.CenterEnd).fillMaxHeight().padding(end = 2.dp), + style = AppComponents.scrollbarStyle(), + ) } - VerticalScrollbar( - adapter = rememberScrollbarAdapter(listState), - modifier = Modifier.align(Alignment.CenterEnd).fillMaxHeight().padding(end = 2.dp), - style = AppComponents.scrollbarStyle(), - ) } // ── Vertical divider ────────────────────────────────────────────────────────────── diff --git a/gradle.properties b/gradle.properties index c903ea35e..8fbaed72b 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,6 +1,6 @@ # Project metadata projectGroup=io.askimo -projectVersion=1.4.16 +projectVersion=1.4.17 jvmVersion=25 # About information diff --git a/shared/src/main/kotlin/io/askimo/core/providers/openaicompatible/OpenAiCompatibleTemplate.kt b/shared/src/main/kotlin/io/askimo/core/providers/openaicompatible/OpenAiCompatibleTemplate.kt index c263f6638..0df90d80f 100644 --- a/shared/src/main/kotlin/io/askimo/core/providers/openaicompatible/OpenAiCompatibleTemplate.kt +++ b/shared/src/main/kotlin/io/askimo/core/providers/openaicompatible/OpenAiCompatibleTemplate.kt @@ -120,4 +120,14 @@ enum class OpenAiCompatibleTemplate( httpVersion = HttpVersion.HTTP_1_1, helpTextKey = "provider.template.together_ai.help", ), + + LITE_LLM( + displayName = "LiteLLM", + initials = "LL", + taglineKey = "provider.template.lite_llm.tagline", + baseUrl = "http://localhost:4000/v1", + apiKeyRequired = false, + apiKeyUrl = "", + helpTextKey = "provider.template.lite_llm.help", + ), }