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
4 changes: 4 additions & 0 deletions app/src/lib/i18n/ar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7002,8 +7002,12 @@ const messages: TranslationMap = {
'userErrors.apiKeyMissing.title': 'مطلوب مفتاح API',
'userErrors.apiKeyMissing.body':
'لا يوجد مفتاح API لمزوّد الذكاء الاصطناعي. أضِفه في إعدادات المزوّد للمتابعة.',
'userErrors.localModelUnavailable.title': 'النموذج المحلي غير متاح',
'userErrors.localModelUnavailable.body':
'لا يمكن الوصول إلى Ollama على النقطة الطرفية المُهيأة، أو أن النموذج المطلوب غير مثبّت عليها. شغّل Ollama ونزّل النموذج على تلك النقطة الطرفية، أو حوّل هذا العمل إلى مزوّد سحابي.',
'userErrors.scope.chat': 'الدردشة',
'userErrors.scope.cron': 'مهمة مجدوَلة',
'userErrors.scope.memory': 'الذاكرة',
// Agent World: Identity trading (confirm-before-spend + balance gate)
'agentWorld.trading.amountLabel': 'المبلغ',
'agentWorld.trading.networkLabel': 'الشبكة',
Expand Down
4 changes: 4 additions & 0 deletions app/src/lib/i18n/bn.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7161,8 +7161,12 @@ const messages: TranslationMap = {
'userErrors.apiKeyMissing.title': 'API কী প্রয়োজন',
'userErrors.apiKeyMissing.body':
'আপনার AI প্রদানকারীর কোনো API কী সেট নেই। চালিয়ে যেতে প্রদানকারী সেটিংসে একটি যোগ করুন।',
'userErrors.localModelUnavailable.title': 'লোকাল মডেল অনুপলব্ধ',
'userErrors.localModelUnavailable.body':
'কনফিগার করা এন্ডপয়েন্টে Ollama-তে পৌঁছানো যাচ্ছে না, অথবা সেখানে প্রয়োজনীয় মডেলটি ইনস্টল করা নেই। Ollama চালু করে সেই এন্ডপয়েন্টে মডেলটি পুল করুন, অথবা এই কাজটি কোনো ক্লাউড প্রোভাইডারে সরিয়ে নিন।',
'userErrors.scope.chat': 'চ্যাট',
'userErrors.scope.cron': 'নির্ধারিত কাজ',
'userErrors.scope.memory': 'মেমরি',
// Agent World: Identity trading (confirm-before-spend + balance gate)
'agentWorld.trading.amountLabel': 'পরিমাণ',
'agentWorld.trading.networkLabel': 'নেটওয়ার্ক',
Expand Down
4 changes: 4 additions & 0 deletions app/src/lib/i18n/de.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7362,8 +7362,12 @@ const messages: TranslationMap = {
'userErrors.apiKeyMissing.title': 'API-Schlüssel erforderlich',
'userErrors.apiKeyMissing.body':
'Für deinen KI-Anbieter ist kein API-Schlüssel hinterlegt. Füge in den Anbietereinstellungen einen hinzu, um fortzufahren.',
'userErrors.localModelUnavailable.title': 'Lokales Modell nicht verfügbar',
'userErrors.localModelUnavailable.body':
'Ollama ist unter dem konfigurierten Endpunkt nicht erreichbar, oder das benötigte Modell ist dort nicht installiert. Starte Ollama und lade das Modell auf diesem Endpunkt, oder verlagere diese Arbeit auf einen Cloud-Anbieter.',
'userErrors.scope.chat': 'Chat',
'userErrors.scope.cron': 'Geplante Aufgabe',
'userErrors.scope.memory': 'Speicher',
// Agent World: Identity trading (confirm-before-spend + balance gate)
'agentWorld.trading.amountLabel': 'Betrag',
'agentWorld.trading.networkLabel': 'Netzwerk',
Expand Down
4 changes: 4 additions & 0 deletions app/src/lib/i18n/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7558,8 +7558,12 @@ const en: TranslationMap = {
'userErrors.apiKeyMissing.title': 'API key required',
'userErrors.apiKeyMissing.body':
'Your AI provider has no API key set. Add one in provider settings to continue.',
'userErrors.localModelUnavailable.title': 'Local model unavailable',
'userErrors.localModelUnavailable.body':
'Ollama is not reachable at the configured endpoint, or the required model is not installed there. Start Ollama and pull the model at that endpoint, or switch this workload to a cloud provider.',
'userErrors.scope.chat': 'Chat',
'userErrors.scope.cron': 'Scheduled job',
'userErrors.scope.memory': 'Memory',
'memorySources.codingSessions.title': 'Coding-agent sessions',
'memorySources.codingSessions.description':
'Turn your Codex and Claude Code decisions and corrections into private persona memory.',
Expand Down
4 changes: 4 additions & 0 deletions app/src/lib/i18n/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7309,8 +7309,12 @@ const messages: TranslationMap = {
'userErrors.apiKeyMissing.title': 'Se requiere clave de API',
'userErrors.apiKeyMissing.body':
'Tu proveedor de IA no tiene una clave de API configurada. Añade una en los ajustes del proveedor para continuar.',
'userErrors.localModelUnavailable.title': 'Modelo local no disponible',
'userErrors.localModelUnavailable.body':
'No se puede acceder a Ollama en el punto de conexión configurado, o el modelo necesario no está instalado allí. Inicia Ollama y descarga el modelo en ese punto de conexión, o cambia este trabajo a un proveedor en la nube.',
'userErrors.scope.chat': 'Chat',
'userErrors.scope.cron': 'Tarea programada',
'userErrors.scope.memory': 'Memoria',
// Agent World: Identity trading (confirm-before-spend + balance gate)
'agentWorld.trading.amountLabel': 'Importe',
'agentWorld.trading.networkLabel': 'Red',
Expand Down
4 changes: 4 additions & 0 deletions app/src/lib/i18n/fr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7341,8 +7341,12 @@ const messages: TranslationMap = {
'userErrors.apiKeyMissing.title': 'Clé API requise',
'userErrors.apiKeyMissing.body':
"Aucune clé API n'est définie pour votre fournisseur d'IA. Ajoutez-en une dans les paramètres du fournisseur pour continuer.",
'userErrors.localModelUnavailable.title': 'Modèle local indisponible',
'userErrors.localModelUnavailable.body':
"Ollama n'est pas joignable sur le point de terminaison configuré, ou le modèle requis n'y est pas installé. Lancez Ollama et téléchargez le modèle sur ce point de terminaison, ou basculez cette charge de travail vers un fournisseur cloud.",
'userErrors.scope.chat': 'Chat',
'userErrors.scope.cron': 'Tâche planifiée',
'userErrors.scope.memory': 'Mémoire',
// Agent World: Identity trading (confirm-before-spend + balance gate)
'agentWorld.trading.amountLabel': 'Montant',
'agentWorld.trading.networkLabel': 'Réseau',
Expand Down
4 changes: 4 additions & 0 deletions app/src/lib/i18n/hi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7159,8 +7159,12 @@ const messages: TranslationMap = {
'userErrors.apiKeyMissing.title': 'API कुंजी आवश्यक',
'userErrors.apiKeyMissing.body':
'आपके AI प्रदाता के लिए कोई API कुंजी सेट नहीं है। जारी रखने के लिए प्रदाता सेटिंग्स में एक जोड़ें।',
'userErrors.localModelUnavailable.title': 'लोकल मॉडल उपलब्ध नहीं है',
'userErrors.localModelUnavailable.body':
'कॉन्फ़िगर किए गए एंडपॉइंट पर Ollama तक पहुँच नहीं है, या ज़रूरी मॉडल वहाँ इंस्टॉल नहीं है। Ollama शुरू करके उसी एंडपॉइंट पर मॉडल पुल करें, या इस काम को किसी क्लाउड प्रोवाइडर पर ले जाएँ।',
'userErrors.scope.chat': 'चैट',
'userErrors.scope.cron': 'निर्धारित कार्य',
'userErrors.scope.memory': 'मेमोरी',
// Agent World: Identity trading (confirm-before-spend + balance gate)
'agentWorld.trading.amountLabel': 'राशि',
'agentWorld.trading.networkLabel': 'नेटवर्क',
Expand Down
4 changes: 4 additions & 0 deletions app/src/lib/i18n/id.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7197,8 +7197,12 @@ const messages: TranslationMap = {
'userErrors.apiKeyMissing.title': 'Kunci API diperlukan',
'userErrors.apiKeyMissing.body':
'Penyedia AI Anda belum memiliki kunci API. Tambahkan satu di pengaturan penyedia untuk melanjutkan.',
'userErrors.localModelUnavailable.title': 'Model lokal tidak tersedia',
'userErrors.localModelUnavailable.body':
'Ollama tidak dapat dijangkau di endpoint yang dikonfigurasi, atau model yang dibutuhkan belum terpasang di sana. Jalankan Ollama dan unduh modelnya di endpoint tersebut, atau alihkan pekerjaan ini ke penyedia cloud.',
'userErrors.scope.chat': 'Obrolan',
'userErrors.scope.cron': 'Tugas terjadwal',
'userErrors.scope.memory': 'Memori',
// Agent World: Identity trading (confirm-before-spend + balance gate)
'agentWorld.trading.amountLabel': 'Jumlah',
'agentWorld.trading.networkLabel': 'Jaringan',
Expand Down
4 changes: 4 additions & 0 deletions app/src/lib/i18n/it.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7294,8 +7294,12 @@ const messages: TranslationMap = {
'userErrors.apiKeyMissing.title': 'Chiave API richiesta',
'userErrors.apiKeyMissing.body':
'Il tuo provider IA non ha una chiave API impostata. Aggiungine una nelle impostazioni del provider per continuare.',
'userErrors.localModelUnavailable.title': 'Modello locale non disponibile',
'userErrors.localModelUnavailable.body':
"Ollama non è raggiungibile sull'endpoint configurato, oppure il modello necessario non è installato lì. Avvia Ollama e scarica il modello su quell'endpoint, oppure sposta questo lavoro su un provider cloud.",
'userErrors.scope.chat': 'Chat',
'userErrors.scope.cron': 'Attività pianificata',
'userErrors.scope.memory': 'Memoria',
// Agent World: Identity trading (confirm-before-spend + balance gate)
'agentWorld.trading.amountLabel': 'Importo',
'agentWorld.trading.networkLabel': 'Rete',
Expand Down
4 changes: 4 additions & 0 deletions app/src/lib/i18n/ko.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7080,8 +7080,12 @@ const messages: TranslationMap = {
'userErrors.apiKeyMissing.title': 'API 키 필요',
'userErrors.apiKeyMissing.body':
'AI 제공업체에 API 키가 설정되지 않았습니다. 제공업체 설정에서 추가하세요.',
'userErrors.localModelUnavailable.title': '로컬 모델을 사용할 수 없음',
'userErrors.localModelUnavailable.body':
'구성된 엔드포인트에서 Ollama에 연결할 수 없거나 필요한 모델이 그곳에 설치되어 있지 않습니다. Ollama를 실행하고 해당 엔드포인트에 모델을 내려받거나, 이 작업을 클라우드 제공업체로 전환하세요.',
'userErrors.scope.chat': '채팅',
'userErrors.scope.cron': '예약된 작업',
'userErrors.scope.memory': '메모리',
// Agent World: Identity trading (confirm-before-spend + balance gate)
'agentWorld.trading.amountLabel': '금액',
'agentWorld.trading.networkLabel': '네트워크',
Expand Down
4 changes: 4 additions & 0 deletions app/src/lib/i18n/pl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7266,8 +7266,12 @@ const messages: TranslationMap = {
'userErrors.apiKeyMissing.title': 'Wymagany klucz API',
'userErrors.apiKeyMissing.body':
'Twój dostawca AI nie ma ustawionego klucza API. Dodaj go w ustawieniach dostawcy, aby kontynuować.',
'userErrors.localModelUnavailable.title': 'Model lokalny niedostępny',
'userErrors.localModelUnavailable.body':
'Ollama jest nieosiągalna pod skonfigurowanym punktem końcowym albo wymagany model nie jest tam zainstalowany. Uruchom Ollamę i pobierz model w tym punkcie końcowym lub przenieś tę pracę do dostawcy w chmurze.',
'userErrors.scope.chat': 'Czat',
'userErrors.scope.cron': 'Zaplanowane zadanie',
'userErrors.scope.memory': 'Pamięć',
// Agent World: Identity trading (confirm-before-spend + balance gate)
'agentWorld.trading.amountLabel': 'Kwota',
'agentWorld.trading.networkLabel': 'Sieć',
Expand Down
4 changes: 4 additions & 0 deletions app/src/lib/i18n/pt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7277,8 +7277,12 @@ const messages: TranslationMap = {
'userErrors.apiKeyMissing.title': 'Chave de API necessária',
'userErrors.apiKeyMissing.body':
'Seu provedor de IA não tem uma chave de API definida. Adicione uma nas configurações do provedor para continuar.',
'userErrors.localModelUnavailable.title': 'Modelo local indisponível',
'userErrors.localModelUnavailable.body':
'O Ollama não está acessível no endpoint configurado, ou o modelo necessário não está instalado nele. Inicie o Ollama e baixe o modelo nesse endpoint, ou mude este trabalho para um provedor na nuvem.',
'userErrors.scope.chat': 'Chat',
'userErrors.scope.cron': 'Tarefa agendada',
'userErrors.scope.memory': 'Memória',
// Agent World: Identity trading (confirm-before-spend + balance gate)
'agentWorld.trading.amountLabel': 'Valor',
'agentWorld.trading.networkLabel': 'Rede',
Expand Down
4 changes: 4 additions & 0 deletions app/src/lib/i18n/ru.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7239,8 +7239,12 @@ const messages: TranslationMap = {
'userErrors.apiKeyMissing.title': 'Требуется ключ API',
'userErrors.apiKeyMissing.body':
'У провайдера ИИ не задан ключ API. Добавьте его в настройках провайдера.',
'userErrors.localModelUnavailable.title': 'Локальная модель недоступна',
'userErrors.localModelUnavailable.body':
'Ollama недоступен по настроенному адресу, либо нужная модель там не установлена. Запустите Ollama и загрузите модель по этому адресу или переведите эту работу на облачного провайдера.',
'userErrors.scope.chat': 'Чат',
'userErrors.scope.cron': 'Запланированная задача',
'userErrors.scope.memory': 'Память',
// Agent World: Identity trading (confirm-before-spend + balance gate)
'agentWorld.trading.amountLabel': 'Сумма',
'agentWorld.trading.networkLabel': 'Сеть',
Expand Down
4 changes: 4 additions & 0 deletions app/src/lib/i18n/zh-CN.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6775,8 +6775,12 @@ const messages: TranslationMap = {
'userErrors.insufficientCredits.body': '提供商额度已用完,请充值或更新 API 密钥。',
'userErrors.apiKeyMissing.title': '需要 API 密钥',
'userErrors.apiKeyMissing.body': '您的 AI 提供商未设置 API 密钥,请在提供商设置中添加以继续。',
'userErrors.localModelUnavailable.title': '本地模型不可用',
'userErrors.localModelUnavailable.body':
'无法在配置的端点连接 Ollama,或所需模型未安装在该端点。请启动 Ollama 并在该端点拉取模型,或将此工作切换到云端提供商。',
'userErrors.scope.chat': '聊天',
'userErrors.scope.cron': '定时任务',
'userErrors.scope.memory': '记忆',
// Agent World:Identity trading (confirm-before-spend + balance gate)
'agentWorld.trading.amountLabel': '金额',
'agentWorld.trading.networkLabel': '网络',
Expand Down
44 changes: 44 additions & 0 deletions app/src/lib/userErrors/__tests__/classify.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,50 @@ describe('classifyUserActionableError', () => {
expect(classifyUserActionableError({ message: 'Incorrect API key provided' })).toBeNull();
});

it('classifies an unusable local model runtime (memory user_error kind token)', () => {
// Core's memory embedder health gate emits the stable kind token with
// error_source=memory (#5354); socketService maps that to the memory scope.
const a = classifyUserActionableError({
errorType: 'local_model_unavailable',
scope: 'memory',
sourceDomain: 'memory',
});
expect(a?.kind).toBe('local_model_unavailable');
expect(a?.scope).toBe('memory');
expect(a?.action).toBe('open_provider_settings');
expect(a?.titleKey).toBe('userErrors.localModelUnavailable.title');
expect(a?.bodyKey).toBe('userErrors.localModelUnavailable.body');
expect(a?.id).toBe(userErrorId('local_model_unavailable', 'memory', undefined));

// …and every prose shape the local embedder / health gate / doctor
// produce. Both Rust-side shapes are covered so the two classifiers stay
// symmetric — daemon-not-listening AND model-never-pulled.
for (const msg of [
'ollama embed request failed (is Ollama running at http://localhost:11434?)',
'Ollama embedding model `bge-m3` is not installed at http://localhost:11434. Run `ollama pull bge-m3`',
'Ollama daemon unreachable at http://localhost:11434',
'ollama embeddings opted-in but daemon unreachable at http://localhost:11434',
]) {
expect(classifyUserActionableError({ message: msg })?.kind).toBe('local_model_unavailable');
}
});

it('does NOT promote a bare "daemon unreachable" from another domain', () => {
// Backend connection-health logs use this phrase too. Matching it loosely
// would tell a user with a flaky backend link to install Ollama. The Rust
// matcher anchors on the full producer wording for the same reason.
expect(
classifyUserActionableError({ message: 'backend daemon unreachable at api.tinyhumans.ai' })
).toBeNull();
});

it('keeps billing remediation for a credits error that also names Ollama', () => {
// The local-runtime rule is last on purpose: an out-of-credits provider
// must not be told to install Ollama.
const a = classifyUserActionableError({ message: 'ollama proxy requires more credits' });
expect(a?.kind).toBe('insufficient_credits');
});

it('returns null for generic / non-actionable errors and empty input', () => {
expect(classifyUserActionableError({ message: GENERIC_MSG })).toBeNull();
expect(classifyUserActionableError({ message: '', errorType: 'inference' })).toBeNull();
Expand Down
40 changes: 40 additions & 0 deletions app/src/lib/userErrors/classify.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,5 +126,45 @@ export function classifyUserActionableError(
};
}

// The local model runtime a workload depends on is unusable — Ollama is not
// running, or the configured model was never pulled (#5354). Emitted by core
// as the stable `local_model_unavailable` kind token (memory embedder health
// gate); the prose variants match the wording the local embedder itself
// produces, so a signal that arrives with a message instead of a token still
// classifies. Deliberately last: it is the narrowest rule, and a provider
// that is out of credits should keep its billing remediation even when the
// message happens to name Ollama.
// Each prose matcher is anchored on the FULL producer wording, never a bare
// `daemon unreachable at`: backend connection-health logs in other domains
// emit that phrase too, and promoting one of those into an "install Ollama"
// panel entry would be worse than showing nothing. Mirrors the same
// deliberate anchoring in the Rust matcher `is_ollama_user_config_rejection`.
const isLocalModelUnavailable =
text.includes('local_model_unavailable') ||
// tinyagents embedder, daemon not listening.
text.includes('is ollama running') ||
// tinyagents embedder, model never pulled — the second shape the Rust
// classifier recognises, so the two sides stay symmetric.
(text.includes('ollama embedding model') && text.includes('is not installed at')) ||
// platform doctor report.
text.includes('ollama daemon unreachable') ||
// memory embedder health gate.
text.includes('ollama embeddings opted-in but daemon unreachable at');
Comment thread
greptile-apps[bot] marked this conversation as resolved.
Comment thread
coderabbitai[bot] marked this conversation as resolved.
if (isLocalModelUnavailable) {
return {
id: userErrorId('local_model_unavailable', scope, signal.provider),
kind: 'local_model_unavailable',
severity: 'warning',
scope,
sourceDomain: signal.sourceDomain,
provider: signal.provider,
titleKey: 'userErrors.localModelUnavailable.title',
bodyKey: 'userErrors.localModelUnavailable.body',
// Local AI + embedding provider selection both live behind
// `/settings/llm`, which redirects to Connections → API keys.
action: 'open_provider_settings',
};
}

return null;
}
Loading
Loading