Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,17 +53,18 @@
<img src="https://img.shields.io/badge/OpenAI--Compatible-Any_Endpoint-gray" alt="OpenAI-Compatible">
</p>

<p align="center">
<img src="https://img.shields.io/badge/Skills-Gemini_CLI-4285F4" alt="Gemini CLI Skills">
<img src="https://img.shields.io/badge/Skills-Claude_Code-542683" alt="Claude Code Skills">
</p>

<p align="center">
<a href="https://github.com/askimo-ai/askimo/releases/latest"><strong>📥 Download</strong></a> •
<a href="https://askimo.chat/docs/"><strong>📖 Documentation</strong></a> •
<a href="https://github.com/askimo-ai/askimo/discussions"><strong>💬 Discussions</strong></a>
</p>

<p align="center">
<a href="https://trendshift.io/repositories/25584?utm_source=repository-badge&utm_medium=badge&utm_campaign=badge-repository-25584" target="_blank" rel="noopener noreferrer">
<img src="https://trendshift.io/api/badge/repositories/25584" alt="askimo-ai/askimo | Trendshift" width="250" height="55"/>
</a>
</p>

---

## Why Askimo?
Expand Down
47 changes: 39 additions & 8 deletions desktop-shared/src/main/kotlin/io/askimo/ui/chat/ChatInputField.kt
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ import io.askimo.core.event.error.AppErrorEvent
import io.askimo.core.event.internal.ImageCapabilityDetectedEvent
import io.askimo.core.event.internal.ReasoningEffortChangedEvent
import io.askimo.core.event.internal.ThinkingSupportDetectedEvent
import io.askimo.core.event.internal.ToolSupportDetectedEvent
import io.askimo.core.i18n.LocalizationManager
import io.askimo.core.intent.ToolConfig
import io.askimo.core.intent.ToolRegistry
Expand Down Expand Up @@ -189,6 +190,20 @@ fun chatInputField(
)
}

// Whether the current model supports tool calling.
// Only disabled when the probe has run AND returned false — unknown (null) stays enabled.
var modelSupportsTools by remember(resolvedProvider, currentModel) {
mutableStateOf(
if (resolvedProvider != null && currentModel.isNotBlank() &&
ModelCapabilitiesCache.hasTestedToolSupport(resolvedProvider, currentModel)
) {
ModelCapabilitiesCache.supportsTools(resolvedProvider, currentModel)
} else {
true // Not yet probed — optimistically keep enabled
},
)
}

// Listen for the probe result and update when it arrives for the active model.
LaunchedEffect(resolvedProvider, currentModel) {
EventBus.internalEvents.collect { event ->
Expand All @@ -201,6 +216,18 @@ fun chatInputField(
}
}

// Listen for tool support probe result and update reactively.
LaunchedEffect(resolvedProvider, currentModel) {
EventBus.internalEvents.collect { event ->
if (event is ToolSupportDetectedEvent &&
event.provider == resolvedProvider &&
event.model == currentModel
) {
modelSupportsTools = event.supportsTools
}
}
}

// Whether the current model supports native image generation (in-chat).
var supportsNativeImageGeneration by remember(resolvedProvider, currentModel) {
mutableStateOf(
Expand Down Expand Up @@ -713,6 +740,7 @@ fun chatInputField(
},
onNavigateToMcpSettings = onNavigateToMcpSettings,
iconSize = 28.dp,
modelSupportsTools = modelSupportsTools,
)

// Image mode chip — only show when user explicitly toggles to Image mode
Expand Down Expand Up @@ -915,6 +943,7 @@ private fun toolsIndicatorButton(
onEnabledServerIdsChange: (Set<String>) -> Unit,
onNavigateToMcpSettings: (() -> Unit)? = null,
iconSize: Dp = 36.dp,
modelSupportsTools: Boolean = false,
) {
var showToolsPopup by remember { mutableStateOf(false) }
var mcpServers by remember { mutableStateOf<List<McpServerInfo>>(emptyList()) }
Expand Down Expand Up @@ -985,31 +1014,32 @@ private fun toolsIndicatorButton(

Box {
themedTooltip(
text = if (hasDisabled) {
stringResource("chat.tools.button.disabled", (totalServers - enabledServers).toString())
} else {
stringResource("chat.tools.button")
text = when {
!modelSupportsTools -> stringResource("chat.tools.button.model.no.support")
hasDisabled -> stringResource("chat.tools.button.disabled", (totalServers - enabledServers).toString())
else -> stringResource("chat.tools.button")
},
) {
// Inline chip: [🔧 3] or [🔧 3/5] — avoids all badge clipping issues
Surface(
shape = RoundedCornerShape(8.dp),
color = when {
!modelSupportsTools -> MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.3f)
totalServers == 0 -> Color.Transparent
hasDisabled -> MaterialTheme.colorScheme.secondaryContainer.copy(alpha = 0.5f)
else -> MaterialTheme.colorScheme.secondaryContainer
},
tonalElevation = if (totalServers > 0) 2.dp else 0.dp,
tonalElevation = if (totalServers > 0 && modelSupportsTools) 2.dp else 0.dp,
modifier = Modifier
.height(iconSize)
.clip(RoundedCornerShape(8.dp))
.clickable(
enabled = !isLoading,
enabled = !isLoading && modelSupportsTools,
interactionSource = remember { MutableInteractionSource() },
indication = null,
onClick = { showToolsPopup = true },
)
.pointerHoverIcon(PointerIcon.Hand),
.pointerHoverIcon(if (modelSupportsTools) PointerIcon.Hand else PointerIcon.Default),
) {
Row(
verticalAlignment = Alignment.CenterVertically,
Expand All @@ -1020,13 +1050,14 @@ private fun toolsIndicatorButton(
Icons.Default.Build,
contentDescription = stringResource("chat.tools.button"),
tint = when {
!modelSupportsTools -> MaterialTheme.colorScheme.onSurface.copy(alpha = 0.3f)
hasDisabled -> MaterialTheme.colorScheme.onSurface.copy(alpha = 0.5f)
totalServers > 0 -> MaterialTheme.colorScheme.onSecondaryContainer
else -> MaterialTheme.colorScheme.onSurface
},
modifier = Modifier.size(16.dp),
)
if (totalServers > 0) {
if (totalServers > 0 && modelSupportsTools) {
Text(
text = if (hasDisabled) "$enabledServers/$totalServers" else "$enabledServers",
style = MaterialTheme.typography.labelSmall,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,11 @@ import io.askimo.core.chat.service.ChatSessionService
import io.askimo.core.context.AppContext
import io.askimo.core.event.EventBus
import io.askimo.core.event.internal.SessionCreatedEvent
import io.askimo.core.exception.ContextLengthException
import io.askimo.core.exception.ExceptionHandler
import io.askimo.core.logging.logger
import io.askimo.core.providers.ConfigurationErrorException
import io.askimo.core.providers.isContextLengthError
import io.askimo.core.providers.sendStreamingMessageWithCallback
import io.askimo.core.vision.ImageProcessor
import io.askimo.ui.chat.ChatViewModel
Expand Down Expand Up @@ -366,6 +368,12 @@ class SessionManager(
val partialResponse = thread.getCurrentContent()
val failedResponse = if (e is ConfigurationErrorException) {
e.displayMessage
} else if (e.isContextLengthError()) {
ExceptionHandler.handleWithPartialContent(
throwable = ContextLengthException(cause = e),
partialContent = partialResponse,
contextId = sessionId,
)
} else {
ExceptionHandler.handleWithPartialContent(
throwable = e,
Expand Down
4 changes: 3 additions & 1 deletion desktop-shared/src/main/resources/i18n/messages.properties
Original file line number Diff line number Diff line change
Expand Up @@ -651,6 +651,8 @@ chat.create.image.mode.cancel=Cancel image creation mode
chat.image.model.missing.title=Image model not configured
chat.image.model.missing.message=No image model is set for {0}. Open Settings > AI Provider > Image Generation Models and set a model name first.
chat.tools.button=Available Tools
chat.tools.button.disabled=Available Tools ({0} disabled)
chat.tools.button.model.no.support=Tools are not supported by this model
chat.tools.popup.title=Available Tools
chat.tools.popup.loading=Loading servers...
chat.tools.popup.no.servers.global=No global MCP servers configured.
Expand All @@ -659,7 +661,6 @@ chat.tools.server.scope.global=Global
chat.tools.server.scope.project=Project
chat.tools.server.scope.builtin=Built-in
chat.tools.tool.no.description=No description available
chat.tools.button.disabled=Available Tools ({0} disabled)
chat.reasoning.effort.label=Thinking
chat.reasoning.effort.tooltip=Reasoning effort — controls how deeply the model thinks before responding
chat.reasoning.effort.off=Off
Expand Down Expand Up @@ -1010,6 +1011,7 @@ error.provider_not_configured=⚠️ No AI provider configured.\n\nTo start chat
error.rate_limit=⚠️ Rate limit exceeded\n{retryAfter}\n\nYou've reached your API usage limit. Please wait before sending more messages.
error.insufficient_credits=⚠️ Insufficient API credits\n\nYour credit balance has been exhausted.\n\nPlease go to Plans & Billing to top up your credits.\n\nerror.timeout=⚠️ Request timed out after {timeout} seconds\n\nThis might be due to:\n• High server load - try again in a moment\n• Complex query - try simplifying\n• Network latency
error.timeout=⚠️ Request timed out after {timeout} seconds\n\nPossible causes:\n• High server load – please try again later\n• Complex request – simplify your input\n• Network delays
error.context_length=⚠️ Context window exceeded\n\nThe total input (conversation history + your message + attachments) is too large for this model to process.\n\nTo fix this:\n• Start a new chat session to clear the conversation history\n• Shorten your message or remove large attachments\n• If using a local model (Ollama / LM Studio / Docker), increase the context size in its settings\n• Switch to a model with a larger context window
error.invalid_request=⚠️ Invalid request: {details}\n\nPlease check your input and try again.
error.system=⚠️ System Error\n\nAn unexpected error occurred: {errorCode}\n\nPlease contact Askimo support with:\n• What you were doing\n• The error code above\n• The timestamp

Expand Down
2 changes: 2 additions & 0 deletions desktop-shared/src/main/resources/i18n/messages_de.properties
Original file line number Diff line number Diff line change
Expand Up @@ -638,6 +638,7 @@ chat.tools.server.scope.project=Projekt
chat.tools.server.scope.builtin=Integriert
chat.tools.tool.no.description=Keine Beschreibung verfügbar
chat.tools.button.disabled=Verfügbare Tools ({0} deaktiviert)
chat.tools.button.model.no.support=Tools werden von diesem Modell nicht unterstützt
chat.reasoning.effort.label=Denken
chat.reasoning.effort.tooltip=Denkaufwand — steuert, wie tief das Modell nachdenkt, bevor es antwortet
chat.reasoning.effort.off=Aus
Expand Down Expand Up @@ -988,6 +989,7 @@ error.provider_not_configured=⚠️ Kein KI-Anbieter konfiguriert.\n\nUm zu cha
error.rate_limit=⚠️ Ratenlimit überschritten\n{retryAfter}\n\nSie haben Ihr API-Nutzungslimit erreicht. Bitte warten Sie, bevor Sie weitere Nachrichten senden.
error.insufficient_credits=⚠️ Unzureichendes Guthaben\n\nIhr Guthaben ist zu niedrig, um diesen KI-Anbieter zu nutzen.\n\nBitte gehen Sie zur Abrechnungsseite Ihres Anbieters.
error.timeout=⚠️ Anfrage nach {timeout} Sekunden abgelaufen\n\nMögliche Ursachen:\n• Hohe Serverlast – versuchen Sie es später erneut\n• Komplexe Anfrage – vereinfachen Sie Ihre Eingabe\n• Netzwerkverzögerungen
error.context_length=⚠️ Kontextfenster überschritten\n\nDie gesamte Eingabe (Konversationsverlauf + Ihre Nachricht + Anhänge) ist zu groß, als dass dieses Modell sie verarbeiten könnte.\n\nSo beheben Sie das Problem:\n• Starten Sie eine neue Chat-Sitzung, um den Konversationsverlauf zu löschen\n• Kürzen Sie Ihre Nachricht oder entfernen Sie große Anhänge\n• Wenn Sie ein lokales Modell verwenden (Ollama / LM Studio / Docker), erhöhen Sie die Kontextgröße in den Einstellungen\n• Wechseln Sie zu einem Modell mit einem größeren Kontextfenster
error.invalid_request=⚠️ Ungültige Anfrage: {details}\n\nBitte überprüfen Sie Ihre Eingabe und versuchen Sie es erneut.
error.system=⚠️ Systemfehler\n\nEin unerwarteter Fehler ist aufgetreten: {errorCode}\n\nBitte wenden Sie sich an den Askimo-Support mit:\n• Ihrer letzten Aktion\n• Dem obigen Fehlercode\n• Dem Zeitstempel

Expand Down
2 changes: 2 additions & 0 deletions desktop-shared/src/main/resources/i18n/messages_es.properties
Original file line number Diff line number Diff line change
Expand Up @@ -637,6 +637,7 @@ chat.tools.server.scope.project=Proyecto
chat.tools.server.scope.builtin=Integrado
chat.tools.tool.no.description=No hay descripción disponible
chat.tools.button.disabled=Herramientas disponibles ({0} deshabilitadas)
chat.tools.button.model.no.support=Las herramientas no son compatibles con este modelo
chat.reasoning.effort.label=Pensando
chat.reasoning.effort.tooltip=Esfuerzo de razonamiento — controla qué tan profundo piensa el modelo antes de responder
chat.reasoning.effort.off=Desactivado
Expand Down Expand Up @@ -986,6 +987,7 @@ error.provider_not_configured=⚠️ No hay ningún proveedor de IA configurado.
error.rate_limit=⚠️ Límite de uso excedido\n{retryAfter}\n\nHa alcanzado su límite de uso de la API. Espere antes de enviar más mensajes.
error.insufficient_credits=⚠️ Créditos insuficientes\n\nSu saldo de créditos es demasiado bajo.\n\nVaya a la página de facturación para añadir créditos.
error.timeout=⚠️ La solicitud expiró después de {timeout} segundos\n\nPosibles causas:\n• Alta carga del servidor – intente nuevamente más tarde\n• Consulta compleja – intente simplificarla\n• Latencia de red
error.context_length=⚠️ Ventana de contexto excedida\n\nLa entrada total (historial de la conversación + su mensaje + archivos adjuntos) es demasiado grande para que este modelo la procese.\n\nPara solucionar esto:\n• Inicie una nueva sesión de chat para borrar el historial de la conversación\n• Acorte su mensaje o elimine archivos adjuntos grandes\n• Si utiliza un modelo local (Ollama / LM Studio / Docker), aumente el tamaño del contexto en su configuración\n• Cambie a un modelo con una ventana de contexto más grande
error.invalid_request=⚠️ Solicitud no válida: {details}\n\nRevise su entrada e inténtelo de nuevo.
error.system=⚠️ Error del sistema\n\nOcurrió un error inesperado: {errorCode}\n\nContacte al soporte de Askimo con:\n• Lo que estaba haciendo\n• El código de error anterior\n• La marca de tiempo

Expand Down
2 changes: 2 additions & 0 deletions desktop-shared/src/main/resources/i18n/messages_fr.properties
Original file line number Diff line number Diff line change
Expand Up @@ -637,6 +637,7 @@ chat.tools.server.scope.project=Projet
chat.tools.server.scope.builtin=Intégré
chat.tools.tool.no.description=Aucune description disponible
chat.tools.button.disabled=Outils disponibles ({0} désactivés)
chat.tools.button.model.no.support=Les outils ne sont pas pris en charge par ce modèle
chat.reasoning.effort.label=Réflexion
chat.reasoning.effort.tooltip=Effort de raisonnement — contrôle la profondeur de réflexion du modèle avant de répondre
chat.reasoning.effort.off=Désactivé
Expand Down Expand Up @@ -986,6 +987,7 @@ error.provider_not_configured=⚠️ Aucun fournisseur IA configuré.\n\nPour co
error.rate_limit=⚠️ Limite d’utilisation atteinte\n{retryAfter}\n\nVous avez atteint votre limite d’utilisation de l’API. Veuillez patienter avant d’envoyer d’autres messages.
error.insufficient_credits=⚠️ Crédits insuffisants\n\nVotre solde est trop faible pour utiliser ce fournisseur.\n\nVisitez la page de facturation pour ajouter des crédits.
error.timeout=⚠️ La requête a expiré après {timeout} secondes\n\nCauses possibles :\n• Charge serveur élevée – réessayez plus tard\n• Requête complexe – simplifiez-la\n• Latence réseau
error.context_length=⚠️ Fenêtre de contexte dépassée\n\nL'entrée totale (historique de la conversation + votre message + pièces jointes) est trop volumineuse pour être traitée par ce modèle.\n\nPour corriger cela :\n• Démarrez une nouvelle session de chat pour effacer l'historique de la conversation\n• Raccourcissez votre message ou supprimez les pièces jointes volumineuses\n• Si vous utilisez un modèle local (Ollama / LM Studio / Docker), augmentez la taille du contexte dans ses paramètres\n• Passez à un modèle avec une fenêtre de contexte plus grande
error.invalid_request=⚠️ Requête invalide : {details}\n\nVeuillez vérifier votre saisie et réessayer.
error.system=⚠️ Erreur système\n\nUne erreur inattendue est survenue : {errorCode}\n\nVeuillez contacter le support Askimo avec :\n• Ce que vous faisiez\n• Le code d’erreur ci-dessus\n• L’horodatage

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -636,6 +636,7 @@ chat.tools.server.scope.project=プロジェクト
chat.tools.server.scope.builtin=組み込み
chat.tools.tool.no.description=説明はありません
chat.tools.button.disabled=利用可能なツール({0} 無効)
chat.tools.button.model.no.support=ツールはこのモデルではサポートされていません
chat.reasoning.effort.label=思考
chat.reasoning.effort.tooltip=推論の労力 — 応答する前にモデルがどの程度深く思考するかを制御します
chat.reasoning.effort.off=オフ
Expand Down Expand Up @@ -986,6 +987,7 @@ error.provider_not_configured=⚠️ AI プロバイダーが設定されてい
error.rate_limit=⚠️ レート制限を超えました\n{retryAfter}\n\nAPI の利用上限に達しました。しばらく待ってから、もう一度送信してください。
error.insufficient_credits=⚠️ クレジット残高不足\n\nクレジット残高が不足しています。\n\n請求ページでクレジットを追加してください。
error.timeout=⚠️ {timeout} 秒後にリクエストがタイムアウトしました\n\n原因として考えられること:\n• サーバー負荷が高い—少し待って再試行してください\n• クエリが複雑—内容を簡略化してみてください\n• ネットワーク遅延
error.context_length=⚠️ コンテキストウィンドウの制限を超えました\n\n入力の合計(会話履歴 + あなたのメッセージ + 添付ファイル)が大きすぎて、このモデルでは処理できません。\n\n解決するには:\n• 新しいチャットセッションを開始して会話履歴をクリアしてください\n• メッセージを短くするか、大きな添付ファイルを削除してください\n• ローカルモデル(Ollama / LM Studio / Docker)を使用している場合は、設定でコンテキストサイズを増やしてください\n• より大きなコンテキストウィンドウを持つモデルに切り替えてください
error.invalid_request=⚠️ 無効なリクエスト:{details}\n\n入力内容を確認して、もう一度お試しください。
error.system=⚠️ システムエラー\n\n予期しないエラーが発生しました:{errorCode}\n\nAskimo サポートへ次を添えてご連絡ください:\n• 実行していた操作\n• 上記のエラーコード\n• タイムスタンプ

Expand Down
Loading