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
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,8 @@ internal fun InlineErrorBanner(
) {
val clipboard = LocalClipboardManager.current
var localRetrying by remember(error) { mutableStateOf(false) }
// The provider's own words can be long; four lines would cut them off with no way to read the rest.
var expanded by remember(error) { mutableStateOf(false) }
val canRetry = onRetry != null && !isRetrying && !localRetrying

Row(
Expand All @@ -327,7 +329,7 @@ internal fun InlineErrorBanner(
.clip(RoundedCornerShape(12.dp))
.background(ChatColors.bad.copy(alpha = 0.12f))
.combinedClickable(
onClick = {},
onClick = { expanded = !expanded },
onLongClick = {
clipboard.setText(androidx.compose.ui.text.AnnotatedString(error))
},
Expand All @@ -347,7 +349,7 @@ internal fun InlineErrorBanner(
color = ChatColors.bad,
fontSize = 12.sp,
lineHeight = 16.sp,
maxLines = 4,
maxLines = if (expanded) Int.MAX_VALUE else 4,
overflow = TextOverflow.Ellipsis,
modifier = Modifier.weight(1f),
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,12 @@ import com.openminis.app.data.model.LLMError
*/
internal object LLMErrorPresenter {

enum class Kind { AUTH, RATE_LIMIT, NETWORK, SERVER, BAD_REQUEST, NOT_FOUND, TOO_LARGE, REJECTED, DECODING, OTHER }
enum class Kind {
AUTH, RATE_LIMIT, NETWORK, SERVER, BAD_REQUEST, NOT_FOUND, TOO_LARGE, REJECTED, DECODING, OTHER,

// A 400 whose body says what was wrong. Only these five are told apart; anything else stays BAD_REQUEST.
BAD_TOOL_PAIRING, BAD_CONTEXT, BAD_IMAGE, BAD_MODEL, BAD_THINKING,
}

data class Reason(val kind: Kind, val status: Int?, val detail: String)

Expand All @@ -38,6 +43,38 @@ internal object LLMErrorPresenter {
return type?.let { TYPE_STATUS[it] }
}

private val TOOL_PAIRING = listOf(
"no tool output found", // OpenAI Responses: a function_call without its function_call_output
"tool_use ids were found without tool_result", // Anthropic
"responding to each", // Chat Completions: "...must be followed by tool messages responding to each tool_call_id"
"must be followed by tool messages",
"no tool call found", // the reverse: an output with no call
"function response parts", // Gemini: number of function response parts must equal function call parts
)
private val CONTEXT = listOf(
"context length", "context_length", "maximum context", "context window", "too many tokens",
"prompt is too long", "input is too long", "reduce the length", "max_tokens_to_sample",
)

/**
* What a 400 was about, read from the provider's own body. The providers word these differently, so this is a
* list of phrases that are specific enough not to mean something else; no match leaves the general 400 text.
* Pure, so the phrases are unit-tested.
*/
internal fun refine400(detail: String): Kind {
// Providers quote identifiers differently (`tool_use`, 'tool_calls'); the phrases below carry no quotes.
val d = detail.lowercase().replace("`", "").replace("'", "").replace("\"", "")
return when {
TOOL_PAIRING.any { it in d } -> Kind.BAD_TOOL_PAIRING
CONTEXT.any { it in d } -> Kind.BAD_CONTEXT
"image" in d && listOf("not support", "unsupported", "invalid image", "does not accept", "cannot be", "vision", "multimodal").any { it in d } -> Kind.BAD_IMAGE
"model" in d && listOf("not found", "does not exist", "invalid model", "unknown model", "model_not_found", "not supported").any { it in d } -> Kind.BAD_MODEL
listOf("reasoning", "thinking", "budget_tokens", "effort").any { it in d } &&
listOf("invalid", "unsupported", "not support", "unknown", "must be", "not allowed").any { it in d } -> Kind.BAD_THINKING
else -> Kind.BAD_REQUEST
}
}

internal fun classify(e: Throwable): Reason? {
val llm = e as? LLMError ?: return null
return when (llm) {
Expand All @@ -49,7 +86,7 @@ internal object LLMErrorPresenter {
is LLMError.ProviderError -> {
val status = statusOf(llm.detail)
val kind = when (status) {
400 -> Kind.BAD_REQUEST
400 -> refine400(llm.detail)
401, 403 -> Kind.AUTH
404 -> Kind.NOT_FOUND
413 -> Kind.TOO_LARGE
Expand All @@ -74,6 +111,11 @@ internal object LLMErrorPresenter {
Kind.NETWORK -> context.getString(R.string.llm_err_network)
Kind.SERVER -> context.getString(R.string.llm_err_server, code.ifEmpty { "5xx" })
Kind.BAD_REQUEST -> context.getString(R.string.llm_err_bad_request)
Kind.BAD_TOOL_PAIRING -> context.getString(R.string.llm_err_400_tool_pairing)
Kind.BAD_CONTEXT -> context.getString(R.string.llm_err_400_context)
Kind.BAD_IMAGE -> context.getString(R.string.llm_err_400_image)
Kind.BAD_MODEL -> context.getString(R.string.llm_err_400_model)
Kind.BAD_THINKING -> context.getString(R.string.llm_err_400_thinking)
Kind.NOT_FOUND -> context.getString(R.string.llm_err_not_found)
Kind.TOO_LARGE -> context.getString(R.string.llm_err_too_large)
Kind.REJECTED -> context.getString(R.string.llm_err_rejected, code)
Expand Down
7 changes: 6 additions & 1 deletion src/android/app/src/main/res/values-de/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2183,11 +2183,16 @@
<string name="llm_err_network">Der Anbieter ist nicht erreichbar. Prüfe das Netzwerk und versuche es erneut.</string>
<string name="llm_err_server">Der Server des Anbieters hat ein vorübergehendes Problem (%1$s). Versuche es gleich noch einmal.</string>
<string name="llm_err_bad_request">Der Anbieter hat die Anfrage abgelehnt (400). Meist liegt es an Modellname, Denkstufe, einem Anhang oder einer zu langen Unterhaltung.</string>
<string name="llm_err_400_tool_pairing">Der Anbieter hat die Anfrage abgelehnt (400): Für einen Tool-Aufruf in diesem Chat fehlt das Ergebnis. Das liegt nicht an Modellname, Denkstufe oder Anhang. Mit einem neuen Chat geht es weiter; bei einer Meldung bitte die Anbieternachricht unten mitschicken.</string>
<string name="llm_err_400_context">Die Unterhaltung ist länger, als der Kontext des Modells erlaubt (400). Komprimiere sie oder starte eine neue.</string>
<string name="llm_err_400_image">Dieses Modell oder dieser Anbieter nimmt das Bild nicht an (400). Wechsle zu einem Modell mit Bildunterstützung oder entferne das Bild.</string>
<string name="llm_err_400_model">Der Anbieter kennt diesen Modellnamen nicht (400). Prüfe den Modellnamen unter Einstellungen → Anbieter.</string>
<string name="llm_err_400_thinking">Der Anbieter akzeptiert die aktuelle Denkstufe nicht (400). Senke sie oder schalte das Denken aus und versuche es erneut.</string>
<string name="llm_err_not_found">Modell oder Adresse nicht gefunden (404). Prüfe Modellname und Endpunkt unter Einstellungen → Anbieter.</string>
<string name="llm_err_too_large">Die Anfrage ist für den Anbieter zu groß (413). Kürze die Unterhaltung oder entferne große Anhänge.</string>
<string name="llm_err_rejected">Der Anbieter hat die Anfrage abgelehnt (%1$s).</string>
<string name="llm_err_decoding">Die Antwort des Anbieters konnte nicht gelesen werden. Versuche es erneut.</string>
<string name="llm_err_detail">Details: %1$s</string>
<string name="llm_err_detail">Nachricht des Anbieters: %1$s</string>
<string name="compact_failed">Komprimierung fehlgeschlagen: %1$s</string>
<string name="chat_no_provider">Kein Modellanbieter eingerichtet. Füge unter Einstellungen → Anbieter einen hinzu.</string>
<string name="chat_provider_rejected">Die Anbieterkonfiguration wurde abgelehnt.</string>
Expand Down
7 changes: 6 additions & 1 deletion src/android/app/src/main/res/values-fr/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2079,11 +2079,16 @@
<string name="llm_err_network">Impossible de joindre le fournisseur. Vérifiez le réseau et réessayez.</string>
<string name="llm_err_server">Le serveur du fournisseur rencontre un problème temporaire (%1$s). Réessayez dans un instant.</string>
<string name="llm_err_bad_request">Le fournisseur a refusé la requête (400). Souvent à cause du nom du modèle, du niveau de réflexion, d\'une pièce jointe ou d\'une conversation trop longue.</string>
<string name="llm_err_400_tool_pairing">Le fournisseur a refusé la requête (400) : un appel d\'outil de cette conversation n\'a pas de résultat. Ce n\'est pas le nom du modèle, le niveau de réflexion ni une pièce jointe. Une nouvelle conversation permet de continuer ; joignez le message du fournisseur ci-dessous si vous le signalez.</string>
<string name="llm_err_400_context">La conversation dépasse le contexte du modèle (400). Compressez-la ou commencez-en une nouvelle.</string>
<string name="llm_err_400_image">Ce modèle ou ce fournisseur n\'accepte pas l\'image (400). Passez à un modèle qui gère les images, ou retirez l\'image.</string>
<string name="llm_err_400_model">Le fournisseur ne reconnaît pas ce nom de modèle (400). Vérifiez le nom du modèle dans Réglages → Fournisseurs.</string>
<string name="llm_err_400_thinking">Le fournisseur n\'accepte pas le niveau de réflexion actuel (400). Baissez-le ou désactivez la réflexion, puis réessayez.</string>
<string name="llm_err_not_found">Modèle ou adresse introuvable (404). Vérifiez le nom du modèle et le point d\'accès dans Réglages → Fournisseurs.</string>
<string name="llm_err_too_large">La requête est trop volumineuse pour le fournisseur (413). Raccourcissez la conversation ou retirez les grosses pièces jointes.</string>
<string name="llm_err_rejected">Le fournisseur a rejeté la requête (%1$s).</string>
<string name="llm_err_decoding">La réponse du fournisseur est illisible. Réessayez.</string>
<string name="llm_err_detail">Détails : %1$s</string>
<string name="llm_err_detail">Message du fournisseur : %1$s</string>
<string name="compact_failed">Échec de la compaction : %1$s</string>
<string name="chat_no_provider">Aucun fournisseur de modèle configuré. Ajoutez-en un dans Réglages → Fournisseurs.</string>
<string name="chat_provider_rejected">La configuration du fournisseur a été refusée.</string>
Expand Down
7 changes: 6 additions & 1 deletion src/android/app/src/main/res/values-ja/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2180,11 +2180,16 @@
<string name="llm_err_network">プロバイダーに接続できません。ネットワークを確認して再試行してください。</string>
<string name="llm_err_server">プロバイダーのサーバーで一時的な問題が発生しました(%1$s)。しばらくしてから再試行してください。</string>
<string name="llm_err_bad_request">プロバイダーがリクエストを拒否しました(400)。モデル名、思考レベル、添付ファイル、会話が長すぎることなどが原因のことが多いです。</string>
<string name="llm_err_400_tool_pairing">プロバイダーがリクエストを拒否しました(400):このチャットに、結果のないツール呼び出しがあります。モデル名・思考レベル・添付の問題ではありません。新しいチャットで続けられます。報告する場合は、下のプロバイダーのメッセージを添えてください。</string>
<string name="llm_err_400_context">会話がモデルのコンテキスト長を超えています(400)。会話を圧縮するか、新しい会話を始めてください。</string>
<string name="llm_err_400_image">このモデルまたはプロバイダーは画像を受け付けません(400)。画像対応のモデルに切り替えるか、画像を外してください。</string>
<string name="llm_err_400_model">プロバイダーがこのモデル名を認識しません(400)。「設定 → プロバイダー」でモデル名を確認してください。</string>
<string name="llm_err_400_thinking">プロバイダーが現在の思考レベルを受け付けません(400)。下げるかオフにして再試行してください。</string>
<string name="llm_err_not_found">モデルまたはアドレスが見つかりません(404)。「設定 → プロバイダー」でモデル名とエンドポイントを確認してください。</string>
<string name="llm_err_too_large">リクエストがプロバイダーの上限を超えています(413)。会話を短くするか、大きな添付ファイルを外してください。</string>
<string name="llm_err_rejected">プロバイダーがリクエストを拒否しました(%1$s)。</string>
<string name="llm_err_decoding">プロバイダーの応答を読み取れませんでした。再試行してください。</string>
<string name="llm_err_detail">詳細:%1$s</string>
<string name="llm_err_detail">プロバイダーのメッセージ:%1$s</string>
<string name="compact_failed">会話の圧縮に失敗しました:%1$s</string>
<string name="chat_no_provider">モデルプロバイダーが未設定です。「設定 → プロバイダー」で追加してください。</string>
<string name="chat_provider_rejected">プロバイダーの設定が拒否されました。</string>
Expand Down
7 changes: 6 additions & 1 deletion src/android/app/src/main/res/values-ko/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2172,11 +2172,16 @@
<string name="llm_err_network">제공업체에 연결할 수 없습니다. 네트워크를 확인한 뒤 다시 시도하세요.</string>
<string name="llm_err_server">제공업체 서버에 일시적인 문제가 있습니다(%1$s). 잠시 후 다시 시도하세요.</string>
<string name="llm_err_bad_request">제공업체가 요청을 거부했습니다(400). 모델 이름, 사고 수준, 첨부 파일, 너무 긴 대화 등이 원인인 경우가 많습니다.</string>
<string name="llm_err_400_tool_pairing">제공업체가 요청을 거부했습니다(400): 이 대화에 결과가 없는 도구 호출이 있습니다. 모델 이름, 사고 수준, 첨부 파일의 문제가 아닙니다. 새 대화를 시작하면 계속할 수 있습니다. 신고할 때는 아래 제공업체의 원문을 함께 보내 주세요.</string>
<string name="llm_err_400_context">대화가 모델의 컨텍스트 길이를 넘었습니다(400). 대화를 압축하거나 새로 시작하세요.</string>
<string name="llm_err_400_image">이 모델이나 제공업체는 이미지를 받지 않습니다(400). 이미지를 지원하는 모델로 바꾸거나 이미지를 빼세요.</string>
<string name="llm_err_400_model">제공업체가 이 모델 이름을 인식하지 못합니다(400). 설정 → 제공업체에서 모델 이름을 확인하세요.</string>
<string name="llm_err_400_thinking">제공업체가 현재 사고 수준을 받지 않습니다(400). 낮추거나 끄고 다시 시도하세요.</string>
<string name="llm_err_not_found">모델 또는 주소를 찾을 수 없습니다(404). 「설정 → 제공업체」에서 모델 이름과 엔드포인트를 확인하세요.</string>
<string name="llm_err_too_large">요청이 제공업체 한도를 넘었습니다(413). 대화를 줄이거나 큰 첨부 파일을 빼 주세요.</string>
<string name="llm_err_rejected">제공업체가 요청을 거부했습니다(%1$s).</string>
<string name="llm_err_decoding">제공업체의 응답을 읽을 수 없습니다. 다시 시도하세요.</string>
<string name="llm_err_detail">자세히: %1$s</string>
<string name="llm_err_detail">제공업체 원문: %1$s</string>
<string name="compact_failed">대화 압축 실패: %1$s</string>
<string name="chat_no_provider">모델 제공업체가 설정되지 않았습니다. 「설정 → 제공업체」에서 추가하세요.</string>
<string name="chat_provider_rejected">제공업체 설정이 거부되었습니다.</string>
Expand Down
7 changes: 6 additions & 1 deletion src/android/app/src/main/res/values-ru/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2359,11 +2359,16 @@
<string name="llm_err_network">Не удаётся связаться с провайдером. Проверьте сеть и повторите попытку.</string>
<string name="llm_err_server">На сервере провайдера временная неполадка (%1$s). Повторите чуть позже.</string>
<string name="llm_err_bad_request">Провайдер отклонил запрос (400). Чаще всего причина в названии модели, уровне размышления, вложении или слишком длинной беседе.</string>
<string name="llm_err_400_tool_pairing">Провайдер отклонил запрос (400): у вызова инструмента в этом чате нет результата. Дело не в имени модели, уровне размышления или вложении. Начните новый чат; если сообщаете об ошибке, приложите сообщение провайдера ниже.</string>
<string name="llm_err_400_context">Разговор длиннее, чем допускает контекст модели (400). Сожмите его или начните новый.</string>
<string name="llm_err_400_image">Эта модель или провайдер не принимает изображение (400). Выберите модель с поддержкой изображений или уберите изображение.</string>
<string name="llm_err_400_model">Провайдер не знает это имя модели (400). Проверьте имя модели в разделе «Настройки → Провайдеры».</string>
<string name="llm_err_400_thinking">Провайдер не принимает текущий уровень размышления (400). Понизьте его или отключите размышление и повторите.</string>
<string name="llm_err_not_found">Модель или адрес не найдены (404). Проверьте название модели и адрес в Настройки → Провайдеры.</string>
<string name="llm_err_too_large">Запрос слишком велик для провайдера (413). Сократите беседу или уберите большие вложения.</string>
<string name="llm_err_rejected">Провайдер отклонил запрос (%1$s).</string>
<string name="llm_err_decoding">Не удалось прочитать ответ провайдера. Повторите попытку.</string>
<string name="llm_err_detail">Подробности: %1$s</string>
<string name="llm_err_detail">Сообщение провайдера: %1$s</string>
<string name="compact_failed">Не удалось сжать беседу: %1$s</string>
<string name="chat_no_provider">Провайдер модели не настроен. Добавьте его в Настройки → Провайдеры.</string>
<string name="chat_provider_rejected">Настройка провайдера отклонена.</string>
Expand Down
Loading
Loading