diff --git a/src/renderer/src/aiCore/clients/anthropic/AnthropicAPIClient.ts b/src/renderer/src/aiCore/clients/anthropic/AnthropicAPIClient.ts index 6a73bf47ce..5157eb95c8 100644 --- a/src/renderer/src/aiCore/clients/anthropic/AnthropicAPIClient.ts +++ b/src/renderer/src/aiCore/clients/anthropic/AnthropicAPIClient.ts @@ -70,6 +70,7 @@ import { mcpToolsToAnthropicTools } from '@renderer/utils/mcp-tools' import { findFileBlocks, findImageBlocks } from '@renderer/utils/messageUtils/find' +import { t } from 'i18next' import { BaseApiClient } from '../BaseApiClient' import { AnthropicStreamListener, RawStreamListener, RequestTransformer, ResponseChunkTransformer } from '../types' @@ -520,6 +521,14 @@ export class AnthropicAPIClient extends BaseApiClient< const toolCalls: Record = {} return { async transform(rawChunk: AnthropicSdkRawChunk, controller: TransformStreamDefaultController) { + if (typeof rawChunk === 'string') { + try { + rawChunk = JSON.parse(rawChunk) + } catch (error) { + logger.error('invalid chunk', { rawChunk, error }) + throw new Error(t('error.chat.chunk.non_json')) + } + } switch (rawChunk.type) { case 'message': { let i = 0 diff --git a/src/renderer/src/aiCore/clients/aws/AwsBedrockAPIClient.ts b/src/renderer/src/aiCore/clients/aws/AwsBedrockAPIClient.ts index 6117dffa18..678c0c5f05 100644 --- a/src/renderer/src/aiCore/clients/aws/AwsBedrockAPIClient.ts +++ b/src/renderer/src/aiCore/clients/aws/AwsBedrockAPIClient.ts @@ -42,6 +42,7 @@ import { mcpToolsToAwsBedrockTools } from '@renderer/utils/mcp-tools' import { findImageBlocks } from '@renderer/utils/messageUtils/find' +import { t } from 'i18next' import { BaseApiClient } from '../BaseApiClient' import { RequestTransformer, ResponseChunkTransformer } from '../types' @@ -436,6 +437,15 @@ export class AwsBedrockAPIClient extends BaseApiClient< async transform(rawChunk: AwsBedrockSdkRawChunk, controller: TransformStreamDefaultController) { logger.silly('Processing AWS Bedrock chunk:', rawChunk) + if (typeof rawChunk === 'string') { + try { + rawChunk = JSON.parse(rawChunk) + } catch (error) { + logger.error('invalid chunk', { rawChunk, error }) + throw new Error(t('error.chat.chunk.non_json')) + } + } + // 处理消息开始事件 if (rawChunk.messageStart) { controller.enqueue({ diff --git a/src/renderer/src/aiCore/clients/gemini/GeminiAPIClient.ts b/src/renderer/src/aiCore/clients/gemini/GeminiAPIClient.ts index edc8a1190a..7047298655 100644 --- a/src/renderer/src/aiCore/clients/gemini/GeminiAPIClient.ts +++ b/src/renderer/src/aiCore/clients/gemini/GeminiAPIClient.ts @@ -60,6 +60,7 @@ import { } from '@renderer/utils/mcp-tools' import { findFileBlocks, findImageBlocks, getMainTextContent } from '@renderer/utils/messageUtils/find' import { defaultTimeout, MB } from '@shared/config/constant' +import { t } from 'i18next' import { BaseApiClient } from '../BaseApiClient' import { RequestTransformer, ResponseChunkTransformer } from '../types' @@ -557,6 +558,14 @@ export class GeminiAPIClient extends BaseApiClient< return () => ({ async transform(chunk: GeminiSdkRawChunk, controller: TransformStreamDefaultController) { logger.silly('chunk', chunk) + if (typeof chunk === 'string') { + try { + chunk = JSON.parse(chunk) + } catch (error) { + logger.error('invalid chunk', { chunk, error }) + throw new Error(t('error.chat.chunk.non_json')) + } + } if (chunk.candidates && chunk.candidates.length > 0) { for (const candidate of chunk.candidates) { if (candidate.content) { diff --git a/src/renderer/src/aiCore/clients/openai/OpenAIApiClient.ts b/src/renderer/src/aiCore/clients/openai/OpenAIApiClient.ts index 1faff88983..3efbc3107e 100644 --- a/src/renderer/src/aiCore/clients/openai/OpenAIApiClient.ts +++ b/src/renderer/src/aiCore/clients/openai/OpenAIApiClient.ts @@ -63,6 +63,7 @@ import { openAIToolsToMcpTool } from '@renderer/utils/mcp-tools' import { findFileBlocks, findImageBlocks } from '@renderer/utils/messageUtils/find' +import { t } from 'i18next' import OpenAI, { AzureOpenAI } from 'openai' import { ChatCompletionContentPart, ChatCompletionContentPartRefusal, ChatCompletionTool } from 'openai/resources' @@ -758,6 +759,15 @@ export class OpenAIAPIClient extends OpenAIBaseClient< return } + if (typeof chunk === 'string') { + try { + chunk = JSON.parse(chunk) + } catch (error) { + logger.error('invalid chunk', { chunk, error }) + throw new Error(t('error.chat.chunk.non_json')) + } + } + // 处理chunk if ('choices' in chunk && chunk.choices && chunk.choices.length > 0) { for (const choice of chunk.choices) { diff --git a/src/renderer/src/aiCore/clients/openai/OpenAIResponseAPIClient.ts b/src/renderer/src/aiCore/clients/openai/OpenAIResponseAPIClient.ts index 970dd1399f..04f2c8c6a7 100644 --- a/src/renderer/src/aiCore/clients/openai/OpenAIResponseAPIClient.ts +++ b/src/renderer/src/aiCore/clients/openai/OpenAIResponseAPIClient.ts @@ -1,3 +1,4 @@ +import { loggerService } from '@logger' import { GenericChunk } from '@renderer/aiCore/middleware/schemas' import { CompletionsContext } from '@renderer/aiCore/middleware/types' import { @@ -38,6 +39,7 @@ import { } from '@renderer/utils/mcp-tools' import { findFileBlocks, findImageBlocks } from '@renderer/utils/messageUtils/find' import { MB } from '@shared/config/constant' +import { t } from 'i18next' import { isEmpty } from 'lodash' import OpenAI, { AzureOpenAI } from 'openai' import { ResponseInput } from 'openai/resources/responses/responses' @@ -46,6 +48,7 @@ import { RequestTransformer, ResponseChunkTransformer } from '../types' import { OpenAIAPIClient } from './OpenAIApiClient' import { OpenAIBaseClient } from './OpenAIBaseClient' +const logger = loggerService.withContext('OpenAIResponseAPIClient') export class OpenAIResponseAPIClient extends OpenAIBaseClient< OpenAI, OpenAIResponseSdkParams, @@ -477,6 +480,14 @@ export class OpenAIResponseAPIClient extends OpenAIBaseClient< let isFirstTextChunk = true return () => ({ async transform(chunk: OpenAIResponseSdkRawChunk, controller: TransformStreamDefaultController) { + if (typeof chunk === 'string') { + try { + chunk = JSON.parse(chunk) + } catch (error) { + logger.error('invalid chunk', { chunk, error }) + throw new Error(t('error.chat.chunk.non_json')) + } + } // 处理chunk if ('output' in chunk) { if (ctx._internal?.toolProcessingState) { diff --git a/src/renderer/src/i18n/locales/en-us.json b/src/renderer/src/i18n/locales/en-us.json index 1b87899702..6c477ad4ca 100644 --- a/src/renderer/src/i18n/locales/en-us.json +++ b/src/renderer/src/i18n/locales/en-us.json @@ -748,6 +748,9 @@ "file_format": "Backup file format error" }, "chat": { + "chunk": { + "non_json": "Returned an invalid data format" + }, "response": "Something went wrong. Please check if you have set your API key in the Settings > Providers" }, "http": { diff --git a/src/renderer/src/i18n/locales/ja-jp.json b/src/renderer/src/i18n/locales/ja-jp.json index f6b2808669..c7e3498b08 100644 --- a/src/renderer/src/i18n/locales/ja-jp.json +++ b/src/renderer/src/i18n/locales/ja-jp.json @@ -748,6 +748,9 @@ "file_format": "バックアップファイルの形式エラー" }, "chat": { + "chunk": { + "non_json": "無効なデータ形式が返されました" + }, "response": "エラーが発生しました。APIキーが設定されていない場合は、設定 > プロバイダーでキーを設定してください" }, "http": { diff --git a/src/renderer/src/i18n/locales/ru-ru.json b/src/renderer/src/i18n/locales/ru-ru.json index c62d17d702..41d2ee01fc 100644 --- a/src/renderer/src/i18n/locales/ru-ru.json +++ b/src/renderer/src/i18n/locales/ru-ru.json @@ -748,6 +748,9 @@ "file_format": "Ошибка формата файла резервной копии" }, "chat": { + "chunk": { + "non_json": "Вернулся недопустимый формат данных" + }, "response": "Что-то пошло не так. Пожалуйста, проверьте, установлен ли ваш ключ API в Настройки > Провайдеры" }, "http": { diff --git a/src/renderer/src/i18n/locales/zh-cn.json b/src/renderer/src/i18n/locales/zh-cn.json index f6f267a877..ce49fe7b2e 100644 --- a/src/renderer/src/i18n/locales/zh-cn.json +++ b/src/renderer/src/i18n/locales/zh-cn.json @@ -748,6 +748,9 @@ "file_format": "备份文件格式错误" }, "chat": { + "chunk": { + "non_json": "返回了无效的数据格式" + }, "response": "出错了,如果没有配置 API 密钥,请前往设置 > 模型提供商中配置密钥" }, "http": { diff --git a/src/renderer/src/i18n/locales/zh-tw.json b/src/renderer/src/i18n/locales/zh-tw.json index 1d7e4bb5d8..3b4511338f 100644 --- a/src/renderer/src/i18n/locales/zh-tw.json +++ b/src/renderer/src/i18n/locales/zh-tw.json @@ -748,6 +748,9 @@ "file_format": "備份檔案格式錯誤" }, "chat": { + "chunk": { + "non_json": "返回了無效的資料格式" + }, "response": "出現錯誤。如果尚未設定 API 金鑰,請前往設定 > 模型提供者中設定金鑰" }, "http": { diff --git a/src/renderer/src/i18n/translate/el-gr.json b/src/renderer/src/i18n/translate/el-gr.json index 7ba4a69d9d..85e64db788 100644 --- a/src/renderer/src/i18n/translate/el-gr.json +++ b/src/renderer/src/i18n/translate/el-gr.json @@ -505,6 +505,7 @@ "tip": "Στη γραμμή εργαλείων των εκτελέσιμων blocks κώδικα θα εμφανίζεται το κουμπί εκτέλεσης· προσέξτε να μην εκτελέσετε επικίνδυνο κώδικα!", "title": "Εκτέλεση Κώδικα" }, + "code_image_tools": "Ενεργοποίηση εργαλείου προεπισκόπησης", "code_wrappable": "Οι κώδικες μπορούν να γράφονται σε διαφορετική γραμμή", "context_count": { "label": "Πλήθος ενδιάμεσων", @@ -648,15 +649,6 @@ }, "expand": "επιλογή", "more": "Περισσότερα", - "preview": { - "copy": { - "image": "Αντιγραφή ως εικόνα" - }, - "label": "Προεπισκόπηση", - "source": "Προβολή πηγαίου κώδικα", - "zoom_in": "Μεγέθυνση", - "zoom_out": "Σμίκρυνση" - }, "run": "Εκτέλεση κώδικα", "split": { "label": "Διαχωρισμός προβολής", @@ -756,6 +748,9 @@ "file_format": "Λάθος μορφή αρχείου που επιστρέφεται" }, "chat": { + "chunk": { + "non_json": "Επέστρεψε μη έγκυρη μορφή δεδομένων" + }, "response": "Σφάλμα. Εάν δεν έχετε ρυθμίσει το κλειδί API, πηγαίνετε στο ρυθμισμένα > παρέχοντας το πρόσωπο του μοντέλου" }, "http": { @@ -1159,6 +1154,9 @@ "failed": "Η διαγραφή απέτυχε", "success": "Η διαγραφή ήταν επιτυχής" }, + "dialog": { + "failed": "Η προεπισκόπηση απέτυχε" + }, "download": { "failed": "Αποτυχία λήψης", "success": "Λήψη ολοκληρώθηκε" @@ -1561,6 +1559,7 @@ "mode": { "edit": "Επεξεργασία", "generate": "Δημιουργία", + "merge": "συγχώνευση", "remix": "Ανάμειξη", "upscale": "Μεγέθυνση" }, @@ -1653,6 +1652,22 @@ "seed_tip": "Ελέγχει την τυχαιότητα του αποτελέσματος μεγέθυνσης" } }, + "preview": { + "copy": { + "image": "Αντιγραφή ως εικόνα" + }, + "dialog": "Άνοιγμα παραθύρου προεπισκόπησης", + "label": "Προεπισκόπηση", + "pan": "Μετακίνηση", + "pan_down": "Μετακίνηση προς τα κάτω", + "pan_left": "Μετακίνηση προς τα αριστερά", + "pan_right": "μετακίνηση προς τα δεξιά", + "pan_up": "Μετακίνηση προς τα πάνω", + "reset": "Επαναφορά", + "source": "Προβολή πηγαίου κώδικα", + "zoom_in": "Μεγέθυνση", + "zoom_out": "συρρίκνωση" + }, "prompts": { "explanation": "Με βοηθήστε να εξηγήσετε αυτό το όρισμα", "summarize": "Με βοηθήστε να συνοψίσετε αυτό το κείμενο", @@ -2715,6 +2730,8 @@ "jsonSaveError": "Αποτυχία αποθήκευσης της διαμορφωτικής ρύθμισης JSON", "jsonSaveSuccess": "Η διαμορφωτική ρύθμιση JSON αποθηκεύτηκε επιτυχώς", "logoUrl": "URL Λογότυπου", + "longRunning": "Μακροχρόνια λειτουργία", + "longRunningTooltip": "Όταν ενεργοποιηθεί, ο διακομιστής υποστηρίζει μακροχρόνιες εργασίες, επαναφέρει το χρονικό όριο μετά από λήψη ειδοποίησης προόδου και επεκτείνει το μέγιστο χρονικό όριο σε 10 λεπτά.", "missingDependencies": "Απο缺失, παρακαλώ εγκαταστήστε το για να συνεχίσετε", "more": { "awesome": "Επιλεγμένος κατάλογος διακομιστών MCP", @@ -2987,6 +3004,7 @@ "select_api_key": "Επιλέξτε το API key που θέλετε να χρησιμοποιήσετε:", "single": "Μόνο", "start": "Έναρξη", + "timeout": "Χρονική καθυστέρηση", "title": "Ελεγχος υγείας μοντέλου", "use_all_keys": "Χρήση όλων των κλειδιών" }, diff --git a/src/renderer/src/i18n/translate/es-es.json b/src/renderer/src/i18n/translate/es-es.json index 83ec9a0ac6..31b8d6d0c4 100644 --- a/src/renderer/src/i18n/translate/es-es.json +++ b/src/renderer/src/i18n/translate/es-es.json @@ -505,6 +505,7 @@ "tip": "En la barra de herramientas de bloques de código ejecutables se mostrará un botón de ejecución. ¡Tenga cuidado en no ejecutar código peligroso!", "title": "Ejecución de Código" }, + "code_image_tools": "Activar herramientas de vista previa", "code_wrappable": "Bloques de código reemplazables", "context_count": { "label": "Número de contextos", @@ -648,15 +649,6 @@ }, "expand": "Expandir", "more": "Más", - "preview": { - "copy": { - "image": "Copiar como imagen" - }, - "label": "Vista previa", - "source": "Ver código fuente", - "zoom_in": "Acercar", - "zoom_out": "Alejar" - }, "run": "Ejecutar código", "split": { "label": "Dividir vista", @@ -756,6 +748,9 @@ "file_format": "Formato de archivo de copia de seguridad incorrecto" }, "chat": { + "chunk": { + "non_json": "Devuelve un formato de datos no válido" + }, "response": "Ha ocurrido un error, si no ha configurado la clave API, vaya a Configuración > Proveedor de modelos para configurar la clave" }, "http": { @@ -1159,6 +1154,9 @@ "failed": "Eliminación fallida", "success": "Eliminación exitosa" }, + "dialog": { + "failed": "Error de vista previa" + }, "download": { "failed": "Descarga fallida", "success": "Descarga exitosa" @@ -1561,6 +1559,7 @@ "mode": { "edit": "Editar", "generate": "Generar imagen", + "merge": "combinar", "remix": "Mezclar", "upscale": "Ampliar" }, @@ -1653,6 +1652,22 @@ "seed_tip": "Controla la aleatoriedad del resultado de la ampliación" } }, + "preview": { + "copy": { + "image": "Copiar como imagen" + }, + "dialog": "Abrir la ventana de vista previa", + "label": "Vista previa", + "pan": "moverse", + "pan_down": "Mover hacia abajo", + "pan_left": "Desplazarse hacia la izquierda", + "pan_right": "Desplazarse hacia la derecha", + "pan_up": "Mover hacia arriba", + "reset": "Restablecer", + "source": "Ver código fuente", + "zoom_in": "ampliar", + "zoom_out": "reducir" + }, "prompts": { "explanation": "Ayúdame a explicar este concepto", "summarize": "Ayúdame a resumir este párrafo", @@ -2715,6 +2730,8 @@ "jsonSaveError": "Fallo al guardar la configuración JSON", "jsonSaveSuccess": "Configuración JSON guardada exitosamente", "logoUrl": "URL del logotipo", + "longRunning": "Modo de ejecución prolongada", + "longRunningTooltip": "Una vez habilitado, el servidor admite tareas de larga duración, reinicia el temporizador de tiempo de espera al recibir notificaciones de progreso y amplía el tiempo máximo de espera hasta 10 minutos.", "missingDependencies": "Faltan, instalelas para continuar", "more": { "awesome": "Lista seleccionada de servidores MCP", @@ -2987,6 +3004,7 @@ "select_api_key": "Seleccionar clave API a usar:", "single": "Individual", "start": "Iniciar", + "timeout": "Tiempo de espera agotado", "title": "Verificación de salud del modelo", "use_all_keys": "Usar todas las claves" }, diff --git a/src/renderer/src/i18n/translate/fr-fr.json b/src/renderer/src/i18n/translate/fr-fr.json index 6f84b6add7..8529025e51 100644 --- a/src/renderer/src/i18n/translate/fr-fr.json +++ b/src/renderer/src/i18n/translate/fr-fr.json @@ -505,6 +505,7 @@ "tip": "Une bouton d'exécution s'affichera dans la barre d'outils des blocs de code exécutables. Attention à ne pas exécuter de code dangereux !", "title": "Exécution de code" }, + "code_image_tools": "Activer l'outil d'aperçu", "code_wrappable": "Blocs de code avec retours à la ligne", "context_count": { "label": "Nombre de contextes", @@ -648,15 +649,6 @@ }, "expand": "Développer", "more": "Plus", - "preview": { - "copy": { - "image": "Copier comme image" - }, - "label": "Aperçu", - "source": "Voir le code source", - "zoom_in": "Agrandir", - "zoom_out": "Réduire" - }, "run": "Exécuter le code", "split": { "label": "Fractionner la vue", @@ -756,6 +748,9 @@ "file_format": "Le format du fichier de sauvegarde est incorrect" }, "chat": { + "chunk": { + "non_json": "a renvoyé un format de données invalide" + }, "response": "Une erreur s'est produite, si l'API n'est pas configurée, veuillez aller dans Paramètres > Fournisseurs de modèles pour configurer la clé" }, "http": { @@ -1159,6 +1154,9 @@ "failed": "Échec de la suppression", "success": "Suppression réussie" }, + "dialog": { + "failed": "Échec de l'aperçu" + }, "download": { "failed": "Échec du téléchargement", "success": "Téléchargement réussi" @@ -1561,6 +1559,7 @@ "mode": { "edit": "Редактировать", "generate": "Создать изображение", + "merge": "fusionner", "remix": "Смешать", "upscale": "Увеличить" }, @@ -1653,6 +1652,22 @@ "seed_tip": "Contrôle la randomisation du résultat d'agrandissement" } }, + "preview": { + "copy": { + "image": "Copier en tant qu'image" + }, + "dialog": "Ouvrir la fenêtre d'aperçu", + "label": "Aperçu", + "pan": "déplacer", + "pan_down": "Déplacer vers le bas", + "pan_left": "Déplacement vers la gauche", + "pan_right": "Décalage vers la droite", + "pan_up": "Déplacer vers le haut", + "reset": "Réinitialiser", + "source": "Voir le code source", + "zoom_in": "agrandir", + "zoom_out": "réduire" + }, "prompts": { "explanation": "Aidez-moi à expliquer ce concept", "summarize": "Aidez-moi à résumer ce passage", @@ -2715,6 +2730,8 @@ "jsonSaveError": "Échec de la sauvegarde de la configuration JSON", "jsonSaveSuccess": "Configuration JSON sauvegardée", "logoUrl": "Адрес логотипа", + "longRunning": "Mode d'exécution prolongée", + "longRunningTooltip": "Activé, le serveur prend en charge les tâches de longue durée, réinitialise le minuteur de délai d'attente lorsqu'il reçoit une notification de progression et prolonge le délai d'expiration maximal à 10 minutes.", "missingDependencies": "Manquantes, veuillez les installer pour continuer", "more": { "awesome": "Liste sélectionnée de serveurs MCP", @@ -2987,6 +3004,7 @@ "select_api_key": "Sélectionner la clé API à utiliser :", "single": "Unique", "start": "Commencer", + "timeout": "Délai dépassé", "title": "Test de santé des modèles", "use_all_keys": "Utiliser toutes les clés" }, diff --git a/src/renderer/src/i18n/translate/pt-pt.json b/src/renderer/src/i18n/translate/pt-pt.json index 1dc8ed5cd0..7593bbd231 100644 --- a/src/renderer/src/i18n/translate/pt-pt.json +++ b/src/renderer/src/i18n/translate/pt-pt.json @@ -505,6 +505,7 @@ "tip": "A barra de ferramentas de blocos de código executáveis exibirá um botão de execução; atenção para não executar códigos perigosos!", "title": "Execução de Código" }, + "code_image_tools": "Ativar ferramenta de pré-visualização", "code_wrappable": "Bloco de código com quebra de linha", "context_count": { "label": "Número de contexto", @@ -648,15 +649,6 @@ }, "expand": "Expandir", "more": "Mais", - "preview": { - "copy": { - "image": "Copiar como imagem" - }, - "label": "Pré-visualizar", - "source": "Ver código-fonte", - "zoom_in": "Ampliar", - "zoom_out": "Reduzir" - }, "run": "Executar código", "split": { "label": "Dividir visualização", @@ -756,6 +748,9 @@ "file_format": "Formato do arquivo de backup está incorreto" }, "chat": { + "chunk": { + "non_json": "Devolveu um formato de dados inválido" + }, "response": "Ocorreu um erro, se a chave da API não foi configurada, por favor vá para Configurações > Provedores de Modelo para configurar a chave" }, "http": { @@ -1159,6 +1154,9 @@ "failed": "Falha ao excluir", "success": "Excluído com sucesso" }, + "dialog": { + "failed": "A pré-visualização falhou" + }, "download": { "failed": "Falha no download", "success": "Download bem-sucedido" @@ -1561,6 +1559,7 @@ "mode": { "edit": "Editar", "generate": "Gerar imagem", + "merge": "fundir", "remix": "Misturar", "upscale": "Aumentar" }, @@ -1653,6 +1652,22 @@ "seed_tip": "Controla a aleatoriedade do resultado de ampliação" } }, + "preview": { + "copy": { + "image": "Copiar como imagem" + }, + "dialog": "Abrir janela de pré-visualização", + "label": "Pré-visualização", + "pan": "mover", + "pan_down": "mover para baixo", + "pan_left": "Deslocar para a esquerda", + "pan_right": "Deslocar para a direita", + "pan_up": "Mover para cima", + "reset": "repor", + "source": "Ver código-fonte", + "zoom_in": "ampliar", + "zoom_out": "reduzir" + }, "prompts": { "explanation": "Ajude-me a explicar este conceito", "summarize": "Ajude-me a resumir este parágrafo", @@ -2715,6 +2730,8 @@ "jsonSaveError": "Falha ao salvar configuração JSON", "jsonSaveSuccess": "Configuração JSON salva com sucesso", "logoUrl": "URL do Logotipo", + "longRunning": "Modo de execução prolongada", + "longRunningTooltip": "Ativado, o servidor suporta tarefas de longa duração, reiniciando o temporizador de tempo limite ao receber notificações de progresso e prolongando o tempo máximo de tempo limite para 10 minutos.", "missingDependencies": "Ausente, instale para continuar", "more": { "awesome": "Lista selecionada de servidores MCP", @@ -2987,6 +3004,7 @@ "select_api_key": "Selecione a chave API a ser usada:", "single": "Individual", "start": "Começar", + "timeout": "tempo expirado", "title": "Verificação de saúde do modelo", "use_all_keys": "Use chaves" },