Feat: more error blocks (#9960)

* refactor(utils): 提取 API 调用错误序列化逻辑到独立函数

将 serializeError 中的 API 调用错误处理逻辑提取为独立的 serializeAPICallError 函数,提高代码可维护性

* feat(错误处理): 添加下载错误的序列化支持

新增 SerializedAiSdkDownloadError 接口用于表示下载错误
实现 serializeDownloadError 方法处理 DownloadError 的序列化
在 serializeError 中添加对 DownloadError 的判断处理

* feat(错误类型): 添加序列化AI SDK错误类型检查函数

修改 isSerializedAiSdkAPICallError
新增 isSerializedAiSdkDownloadError
新增 SerializedAiSdkInvalidArgumentError

* feat(错误处理): 添加对InvalidArgumentError的序列化支持

添加对InvalidArgumentError类型的序列化处理,包括定义序列化接口和实现序列化函数

* feat(错误处理): 添加对InvalidDataContentError的序列化支持

* feat(错误处理): 添加对InvalidMessageRoleError的序列化支持

新增对InvalidMessageRoleError错误的序列化处理,包括类型定义和序列化函数

* feat(错误处理): 添加对无效提示错误的序列化支持

新增 SerializedAiSdkInvalidPromptError 接口及序列化函数
在 serializeError 中添加对 InvalidPromptError 的处理逻辑

* feat(错误处理): 添加对无效工具输入错误的序列化支持

* feat(错误处理): 添加对JSON解析错误的序列化支持

* feat(错误处理): 添加消息转换错误的序列化支持

添加 SerializedAiSdkMessageConversionError 接口及序列化函数,用于处理消息转换错误的序列化

* feat(types): 添加 SerializedAiSdkNoAudioGeneratedError 类型及校验函数

* feat(错误处理): 添加对NoObjectGeneratedError的序列化支持

新增SerializedAiSdkNoObjectGeneratedError接口及序列化函数,用于处理AI SDK中对象未生成的错误情况

* feat(错误处理): 添加对NoSuchModelError的序列化支持

添加对AI SDK中NoSuchModelError类型的序列化支持,包括类型定义和序列化函数

* feat(错误处理): 添加对NoSuchProviderError的序列化支持

* feat(错误处理): 添加对NoSuchToolError的序列化支持

新增SerializedAiSdkNoSuchToolError接口及序列化函数,用于处理工具不存在错误
简化isSerializedAiSdkNoSuchProviderError的校验逻辑

* feat(错误处理): 添加序列化重试错误类型和函数

添加 SerializedAiSdkRetryError 接口和序列化函数,用于处理重试错误的序列化

* feat(types): 添加SerializedAiSdkTooManyEmbeddingValuesForCallError类型

添加未由aisdk导出的SerializedAiSdkTooManyEmbeddingValuesForCallError类型及其类型守卫,用于处理嵌入值过多错误

* feat(错误处理): 添加工具调用修复错误的序列化支持

* feat(错误处理): 添加类型验证错误的序列化支持

* feat(错误处理): 添加对不支持功能的错误序列化支持

* feat(types): 添加 AiSdkErrorUnion 类型用于聚合所有 AI SDK 错误类型

* refactor(types): 移除未使用的AiSdkErrorUnion类型并清理导入

* refactor(error): 重构错误处理逻辑,统一序列化方法

简化错误序列化逻辑,使用统一的方法处理所有可能的错误类型
移除重复的序列化函数,提高代码可维护性
更新错误类型定义,添加缺失的错误类型

* feat(error): 添加对InvalidToolInputError和NoSuchToolError的序列化支持

新增对AI SDK中InvalidToolInputError和NoSuchToolError错误的序列化处理,完善错误处理逻辑

* feat(错误处理): 完善AI SDK错误类型和错误详情展示

添加SerializedAiSdkNoSpeechGeneratedError类型并重命名相关函数
实现isSerializedAiSdkErrorUnion函数统一检查所有AI SDK错误类型
在ErrorBlock中扩展错误详情展示逻辑,支持所有AI SDK错误类型

* feat(i18n): 添加缺失的翻译字段

* docs(i18n): 添加待翻译的多语言文本字段

* fix(ErrorBlock): 修复重复的错误类型检查条件

* feat(types): 添加AISDKError到AiSdkErrorUnion类型中

* refactor(ErrorBlock): 移除未使用的类型验证错误显示逻辑

* fix(i18n): Auto update translations for PR #9960

* docs(i18n): 更新多语言翻译文件中的缺失翻译

* fix(i18n): 修正多语言文件中的翻译错误

更新法语、葡萄牙语、日语和俄语翻译文件,将中文词汇替换为正确的目标语言词汇

* fix(类型): 修正SerializedError和SerializedAiSdkJSONParseError的类型判断

移除SerializedAiSdkJSONParseError类型判断中冗余的'message'检查,因为父类型SerializedAiSdkError已包含此检查

* fix(错误处理): 修复类型验证错误判断并添加缺失的错误类型

修复 isSerializedAiSdkTypeValidationError 判断逻辑,排除包含 parameter 属性的情况
在 SerializedAiSdkErrorUnion 联合类型中添加缺失的 SerializedAiSdkNoSpeechGeneratedError 类型

---------

Co-authored-by: GitHub Action <action@github.com>
This commit is contained in:
Phantom 2025-09-05 22:24:13 +08:00 committed by GitHub
parent 973cab57ab
commit fd83834fca
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 919 additions and 87 deletions

View File

@ -773,6 +773,7 @@
"more": "More",
"name": "Name",
"no_results": "No results",
"none": "None",
"open": "Open",
"paste": "Paste",
"preview": "Preview",
@ -817,6 +818,8 @@
"openai-response": "OpenAI-Response"
},
"error": {
"availableProviders": "Available Providers",
"availableTools": "Available Tools",
"backup": {
"file_format": "Backup file format error"
},
@ -841,9 +844,13 @@
"quota_exceeded": "Your daily {{quota}} free quota has been exhausted. Please go to the <provider>{{provider}}</provider> to obtain an API key and configure the API key to continue using.",
"response": "Something went wrong. Please check if you have set your API key in the Settings > Providers"
},
"data": "data",
"content": "Content",
"data": "Data",
"detail": "Error Details",
"details": "Details",
"errors": "Errors",
"finishReason": "Finish Reason",
"functionality": "Functionality",
"http": {
"400": "Request failed. Please check if the request parameters are correct. If you have changed the model settings, please reset them to the default settings",
"401": "Authentication failed. Please check if your API key is correct",
@ -855,16 +862,27 @@
"503": "Service unavailable. Please try again later",
"504": "Gateway timeout. Please try again later"
},
"lastError": "Last Error",
"maxEmbeddingsPerCall": "Max Embeddings Per Call",
"message": "Error Message",
"missing_user_message": "Cannot switch model response: The original user message has been deleted. Please send a new message to get a response with this model.",
"model": {
"exists": "Model already exists",
"not_exists": "Model does not exist"
},
"modelId": "Model ID",
"modelType": "Model Type",
"name": "Error name",
"no_api_key": "API key is not configured",
"originalError": "Original Error",
"originalMessage": "Original Message",
"parameter": "Parameter",
"pause_placeholder": "Paused",
"prompt": "Prompt",
"provider": "Provider",
"providerId": "Provider ID",
"provider_disabled": "Model provider is not enabled",
"reason": "Reason",
"render": {
"description": "Failed to render message content. Please check if the message content format is correct",
"title": "Render Error"
@ -872,13 +890,23 @@
"requestBody": "Request Body",
"requestBodyValues": "Request Body Values",
"requestUrl": "Request URL",
"response": "Response",
"responseBody": "Response Body",
"responseHeaders": "Response Header",
"responses": "Responses",
"role": "Role",
"stack": "Stack Trace",
"status": "Status Code",
"statusCode": "Status code",
"statusCode": "Status Code",
"statusText": "Status Text",
"text": "Text",
"toolInput": "Tool Input",
"toolName": "Tool Name",
"unknown": "Unknown error",
"user_message_not_found": "Cannot find original user message to resend"
"usage": "Usage",
"user_message_not_found": "Cannot find original user message to resend",
"value": "Value",
"values": "Values"
},
"export": {
"assistant": "Assistant",

View File

@ -773,6 +773,7 @@
"more": "更多",
"name": "名称",
"no_results": "无结果",
"none": "无",
"open": "打开",
"paste": "粘贴",
"preview": "预览",
@ -817,6 +818,8 @@
"openai-response": "OpenAI-Response"
},
"error": {
"availableProviders": "可用提供商",
"availableTools": "可用工具",
"backup": {
"file_format": "备份文件格式错误"
},
@ -841,9 +844,13 @@
"quota_exceeded": "您今日免费配额已用尽,请前往 <provider>{{provider}}</provider> 获取API密钥配置API密钥后继续使用",
"response": "出错了,如果没有配置 API 密钥,请前往设置 > 模型提供商中配置密钥"
},
"content": "内容",
"data": "数据",
"detail": "错误详情",
"details": "详细信息",
"errors": "错误",
"finishReason": "结束原因",
"functionality": "功能",
"http": {
"400": "请求错误,请检查请求参数是否正确。如果修改了模型设置,请重置到默认设置",
"401": "身份验证失败,请检查 API 密钥是否正确",
@ -855,16 +862,27 @@
"503": "服务不可用,请稍后再试",
"504": "网关超时,请稍后再试"
},
"lastError": "最后错误",
"maxEmbeddingsPerCall": "每次调用的最大嵌入",
"message": "错误信息",
"missing_user_message": "无法切换模型响应:原始用户消息已被删除。请发送新消息以获取此模型的响应",
"model": {
"exists": "模型已存在",
"not_exists": "模型不存在"
},
"modelId": "模型 ID",
"modelType": "模型类型",
"name": "错误名称",
"no_api_key": "API 密钥未配置",
"originalError": "原错误",
"originalMessage": "原消息",
"parameter": "参数",
"pause_placeholder": "已中断",
"prompt": "提示词",
"provider": "提供商",
"providerId": "提供商 ID",
"provider_disabled": "模型提供商未启用",
"reason": "原因",
"render": {
"description": "消息内容渲染失败,请检查消息内容格式是否正确",
"title": "渲染错误"
@ -872,13 +890,23 @@
"requestBody": "请求内容",
"requestBodyValues": "请求体",
"requestUrl": "请求路径",
"response": "响应",
"responseBody": "响应内容",
"responseHeaders": "响应首部",
"responses": "响应",
"role": "角色",
"stack": "堆栈信息",
"status": "状态码",
"statusCode": "状态码",
"statusText": "状态文本",
"text": "文本",
"toolInput": "工具输入",
"toolName": "工具名",
"unknown": "未知错误",
"user_message_not_found": "无法找到原始用户消息"
"usage": "用量",
"user_message_not_found": "无法找到原始用户消息",
"value": "值",
"values": "值"
},
"export": {
"assistant": "助手",

View File

@ -773,6 +773,7 @@
"more": "更多",
"name": "名稱",
"no_results": "沒有結果",
"none": "無",
"open": "開啟",
"paste": "貼上",
"preview": "預覽",
@ -817,6 +818,8 @@
"openai-response": "OpenAI-Response"
},
"error": {
"availableProviders": "可用提供商",
"availableTools": "可用工具",
"backup": {
"file_format": "備份檔案格式錯誤"
},
@ -841,9 +844,13 @@
"quota_exceeded": "您今日{{quota}}免费配额已用尽,请前往 <provider>{{provider}}</provider> 获取API密钥配置API密钥后继续使用",
"response": "出現錯誤。如果尚未設定 API 金鑰,請前往設定 > 模型提供者中設定金鑰"
},
"content": "內容",
"data": "数据",
"detail": "錯誤詳情",
"details": "詳細信息",
"errors": "錯誤",
"finishReason": "結束原因",
"functionality": "功能",
"http": {
"400": "請求錯誤,請檢查請求參數是否正確。如果修改了模型設定,請重設到預設設定",
"401": "身份驗證失敗,請檢查 API 金鑰是否正確",
@ -855,16 +862,27 @@
"503": "服務無法使用,請稍後再試",
"504": "閘道器超時,請稍後再試"
},
"lastError": "最後錯誤",
"maxEmbeddingsPerCall": "每次調用的最大嵌入",
"message": "錯誤訊息",
"missing_user_message": "無法切換模型回應:原始用戶訊息已被刪除。請發送新訊息以獲得此模型回應。",
"model": {
"exists": "模型已存在",
"not_exists": "模型不存在"
},
"modelId": "模型 ID",
"modelType": "模型類型",
"name": "錯誤名稱",
"no_api_key": "API 金鑰未設定",
"originalError": "原錯誤",
"originalMessage": "原消息",
"parameter": "參數",
"pause_placeholder": "回應已暫停",
"prompt": "提示詞",
"provider": "提供商",
"providerId": "提供者 ID",
"provider_disabled": "模型供應商未啟用",
"reason": "原因",
"render": {
"description": "消息內容渲染失敗,請檢查消息內容格式是否正確",
"title": "渲染錯誤"
@ -872,13 +890,23 @@
"requestBody": "請求內容",
"requestBodyValues": "请求体",
"requestUrl": "請求路徑",
"response": "響應",
"responseBody": "响应内容",
"responseHeaders": "响应首部",
"responses": "響應",
"role": "角色",
"stack": "堆棧信息",
"status": "狀態碼",
"statusCode": "狀態碼",
"statusText": "狀態文本",
"text": "文本",
"toolInput": "工具輸入",
"toolName": "工具名",
"unknown": "未知錯誤",
"user_message_not_found": "無法找到原始用戶訊息"
"usage": "用量",
"user_message_not_found": "無法找到原始用戶訊息",
"value": "值",
"values": "值"
},
"export": {
"assistant": "助手",

View File

@ -773,6 +773,7 @@
"more": "Περισσότερα",
"name": "Όνομα",
"no_results": "Δεν βρέθηκαν αποτελέσματα",
"none": "Χωρίς",
"open": "Άνοιγμα",
"paste": "Επικόλληση",
"preview": "Προεπισκόπηση",
@ -817,6 +818,8 @@
"openai-response": "Απάντηση OpenAI"
},
"error": {
"availableProviders": "Διαθέσιμοι πάροχοι",
"availableTools": "Διαθέσιμα εργαλεία",
"backup": {
"file_format": "Λάθος μορφή αρχείου που επιστρέφεται"
},
@ -841,9 +844,13 @@
"quota_exceeded": "Η ημερήσια δωρεάν ποσόστωση {{quota}} tokens σας έχει εξαντληθεί. Παρακαλώ μεταβείτε στο <provider>{{provider}}</provider> για να λάβετε ένα κλειδί API και να ρυθμίσετε το κλειδί API για να συνεχίσετε τη χρήση.",
"response": "Σφάλμα. Εάν δεν έχετε ρυθμίσει το κλειδί API, πηγαίνετε στο ρυθμισμένα > παρέχοντας το πρόσωπο του μοντέλου"
},
"content": "Περιεχόμενο",
"data": "δεδομένα",
"detail": "Λεπτομέρειες σφάλματος",
"details": "Λεπτομέρειες",
"errors": "Λάθος",
"finishReason": "Αιτία λήξης",
"functionality": "λειτουργία",
"http": {
"400": "Σφάλμα ζητήματος, παρακαλώ ελέγξτε αν τα παράμετρα του ζητήματος είναι σωστά. Εάν έχετε αλλάξει τις ρυθμίσεις του μοντέλου, επαναφέρετε τις προεπιλεγμένες ρυθμίσεις.",
"401": "Αποτυχία επιβεβαίωσης ταυτότητας, παρακαλώ ελέγξτε αν η κλειδί API είναι σωστή",
@ -855,16 +862,27 @@
"503": "Η υπηρεσία δεν είναι διαθέσιμη, παρακαλώ δοκιμάστε ξανά",
"504": "Υπερχρονισμός φάρων, παρακαλώ δοκιμάστε ξανά"
},
"lastError": "Τελευταίο σφάλμα",
"maxEmbeddingsPerCall": "Μέγιστες ενσωματώσεις ανά κλήση",
"message": "Μήνυμα σφάλματος",
"missing_user_message": "Αδυναμία εναλλαγής απάντησης μοντέλου: το αρχικό μήνυμα χρήστη έχει διαγραφεί. Παρακαλούμε στείλτε ένα νέο μήνυμα για να λάβετε απάντηση από αυτό το μοντέλο",
"model": {
"exists": "Το μοντέλο υπάρχει ήδη",
"not_exists": "Το μοντέλο δεν υπάρχει"
},
"modelId": "Αναγνωριστικό μοντέλου",
"modelType": "Τύπος μοντέλου",
"name": "Λάθος όνομα",
"no_api_key": "Δεν έχετε ρυθμίσει το κλειδί API",
"originalError": "Αρχικό σφάλμα",
"originalMessage": "Αρχικό μήνυμα",
"parameter": "παράμετροι",
"pause_placeholder": "Διακόπηκε",
"prompt": "συμβουλές",
"provider": "πάροχος",
"providerId": "Αναγνωριστικό παρόχου",
"provider_disabled": "Ο παρεχόμενος παροχός του μοντέλου δεν είναι ενεργοποιημένος",
"reason": "αιτία",
"render": {
"description": "Απέτυχε η ώθηση της εξίσωσης, παρακαλώ ελέγξτε το σωστό μορφάτι της",
"title": "Σφάλμα Παρασκήνιου"
@ -872,13 +890,23 @@
"requestBody": "Περιεχόμενο αιτήματος",
"requestBodyValues": "Σώμα αιτήματος",
"requestUrl": "Μονοπάτι αιτήματος",
"response": "απάντηση",
"responseBody": "απάντηση περιεχομένου",
"responseHeaders": "Επικεφαλίδες απόκρισης",
"responses": "ανταπόκριση",
"role": "ρόλος",
"stack": "Πληροφορίες στοίβας",
"status": "Κωδικός κατάστασης",
"statusCode": "Κωδικός κατάστασης",
"statusText": "Κείμενο κατάστασης",
"text": "κείμενο",
"toolInput": "εισαγωγή εργαλείου",
"toolName": "Όνομα εργαλείου",
"unknown": "Άγνωστο σφάλμα",
"user_message_not_found": "Αδυναμία εύρεσης της αρχικής μηνύματος χρήστη"
"usage": "δοσολογία",
"user_message_not_found": "Αδυναμία εύρεσης της αρχικής μηνύματος χρήστη",
"value": "τιμή",
"values": "τιμή"
},
"export": {
"assistant": "βοηθός",

View File

@ -773,6 +773,7 @@
"more": "Más",
"name": "Nombre",
"no_results": "Sin resultados",
"none": "无",
"open": "Abrir",
"paste": "Pegar",
"preview": "Vista previa",
@ -817,6 +818,8 @@
"openai-response": "Respuesta de OpenAI"
},
"error": {
"availableProviders": "Proveedores disponibles",
"availableTools": "Herramientas disponibles",
"backup": {
"file_format": "Formato de archivo de copia de seguridad incorrecto"
},
@ -841,9 +844,13 @@
"quota_exceeded": "Su cuota gratuita diaria de {{quota}} tokens se ha agotado. Por favor, vaya a <provider>{{provider}}</provider> para obtener una clave API y configurar la clave API para continuar usando.",
"response": "Ha ocurrido un error, si no ha configurado la clave API, vaya a Configuración > Proveedor de modelos para configurar la clave"
},
"content": "contenido",
"data": "datos",
"detail": "Detalles del error",
"details": "Detalles",
"errors": "error",
"finishReason": "Razón de finalización",
"functionality": "función",
"http": {
"400": "Error en la solicitud, revise si los parámetros de la solicitud son correctos. Si modificó la configuración del modelo, restablezca a la configuración predeterminada",
"401": "Fallo en la autenticación, revise si la clave API es correcta",
@ -855,16 +862,27 @@
"503": "Servicio no disponible, inténtelo de nuevo más tarde",
"504": "Tiempo de espera de la puerta de enlace, inténtelo de nuevo más tarde"
},
"message": "错误信息",
"lastError": "Último error",
"maxEmbeddingsPerCall": "máximo de incrustaciones por llamada",
"message": "Mensaje de error",
"missing_user_message": "No se puede cambiar la respuesta del modelo: el mensaje original del usuario ha sido eliminado. Envíe un nuevo mensaje para obtener la respuesta de este modelo",
"model": {
"exists": "El modelo ya existe",
"not_exists": "El modelo no existe"
},
"modelId": "ID del modelo",
"modelType": "Tipo de modelo",
"name": "Nombre de error",
"no_api_key": "La clave API no está configurada",
"originalError": "Error original",
"originalMessage": "mensaje original",
"parameter": "parámetro",
"pause_placeholder": "Interrumpido",
"prompt": "prompt",
"provider": "proveedor",
"providerId": "ID del proveedor",
"provider_disabled": "El proveedor de modelos no está habilitado",
"reason": "causa",
"render": {
"description": "Error al renderizar la fórmula, por favor, compruebe si el formato de la fórmula es correcto",
"title": "Error de renderizado"
@ -872,13 +890,23 @@
"requestBody": "Contenido de la solicitud",
"requestBodyValues": "Cuerpo de la solicitud",
"requestUrl": "Ruta de solicitud",
"response": "respuesta",
"responseBody": "Contenido de la respuesta",
"responseHeaders": "Encabezados de respuesta",
"responses": "respuesta",
"role": "Rol",
"stack": "Información de la pila",
"status": "código de estado",
"statusCode": "código de estado",
"status": "Estado",
"statusCode": "Código de estado",
"statusText": "Texto de estado",
"text": "Texto",
"toolInput": "Herramienta de entrada",
"toolName": "Nombre de la herramienta",
"unknown": "Error desconocido",
"user_message_not_found": "No se pudo encontrar el mensaje original del usuario"
"usage": "Cantidad de uso",
"user_message_not_found": "No se pudo encontrar el mensaje original del usuario",
"value": "Valor",
"values": "Valor"
},
"export": {
"assistant": "Asistente",

View File

@ -773,6 +773,7 @@
"more": "Plus",
"name": "Nom",
"no_results": "Aucun résultat",
"none": "Aucun",
"open": "Ouvrir",
"paste": "Coller",
"preview": "Aperçu",
@ -817,6 +818,8 @@
"openai-response": "Réponse OpenAI"
},
"error": {
"availableProviders": "Fournisseurs disponibles",
"availableTools": "Outils disponibles",
"backup": {
"file_format": "Le format du fichier de sauvegarde est incorrect"
},
@ -841,9 +844,13 @@
"quota_exceeded": "Votre quota gratuit quotidien de {{quota}} tokens a été épuisé. Veuillez vous rendre sur <provider>{{provider}}</provider> pour obtenir une clé API et configurer la clé API pour continuer à utiliser.",
"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é"
},
"content": "suivre l'instruction du système",
"data": "données",
"detail": "Détails de l'erreur",
"details": "Informations détaillées",
"errors": "erreur",
"finishReason": "Raison de la fin",
"functionality": "fonction",
"http": {
"400": "Erreur de requête, veuillez vérifier si les paramètres de la requête sont corrects. Si vous avez modifié les paramètres du modèle, réinitialisez-les aux paramètres par défaut.",
"401": "Échec de l'authentification, veuillez vérifier que votre clé API est correcte.",
@ -855,16 +862,27 @@
"503": "Service indisponible, veuillez réessayer plus tard.",
"504": "Délai d'expiration de la passerelle, veuillez réessayer plus tard."
},
"lastError": "Dernière erreur",
"maxEmbeddingsPerCall": "Maximum dintégrations par appel",
"message": "Erreur message",
"missing_user_message": "Impossible de changer de modèle de réponse : le message utilisateur d'origine a été supprimé. Veuillez envoyer un nouveau message pour obtenir une réponse de ce modèle.",
"model": {
"exists": "Le modèle existe déjà",
"not_exists": "Le modèle n'existe pas"
},
"modelId": "ID du modèle",
"modelType": "Type de modèle",
"name": "Nom d'erreur",
"no_api_key": "La clé API n'est pas configurée",
"originalError": "Erreur d'origine",
"originalMessage": "message original",
"parameter": "paramètre",
"pause_placeholder": "Прервано",
"prompt": "mot-clé",
"provider": "fournisseur",
"providerId": "ID du fournisseur",
"provider_disabled": "Le fournisseur de modèles n'est pas activé",
"reason": "raison",
"render": {
"description": "La formule n'a pas été rendue avec succès, veuillez vérifier si le format de la formule est correct",
"title": "Erreur de rendu"
@ -872,13 +890,23 @@
"requestBody": "Contenu de la demande",
"requestBodyValues": "Corps de la requête",
"requestUrl": "Chemin de la requête",
"response": "réponse",
"responseBody": "Contenu de la réponse",
"responseHeaders": "En-têtes de réponse",
"responses": "réponse",
"role": "rôle",
"stack": "Informations de la pile",
"status": "Code d'état",
"statusCode": "Code d'état",
"statusText": "Texte d'état",
"text": "texte",
"toolInput": "entrée de l'outil",
"toolName": "Nom de l'outil",
"unknown": "Неизвестная ошибка",
"user_message_not_found": "Impossible de trouver le message d'utilisateur original"
"usage": "Quantité",
"user_message_not_found": "Impossible de trouver le message d'utilisateur original",
"value": "valeur",
"values": "valeur"
},
"export": {
"assistant": "Assistant",

View File

@ -773,6 +773,7 @@
"more": "もっと",
"name": "名前",
"no_results": "検索結果なし",
"none": "無",
"open": "開く",
"paste": "貼り付け",
"preview": "プレビュー",
@ -817,6 +818,8 @@
"openai-response": "OpenAI-Response"
},
"error": {
"availableProviders": "利用可能なプロバイダー",
"availableTools": "利用可能なツール",
"backup": {
"file_format": "バックアップファイルの形式エラー"
},
@ -841,9 +844,13 @@
"quota_exceeded": "本日の{{quota}}無料クォータが使い果たされました。<provider>{{provider}}</provider>でAPIキーを取得し、APIキーを設定して使用を続けてください。",
"response": "エラーが発生しました。APIキーが設定されていない場合は、設定 > プロバイダーでキーを設定してください"
},
"content": "内容",
"data": "データ",
"detail": "エラーの詳細",
"details": "詳細",
"errors": "エラー",
"finishReason": "終了理由",
"functionality": "機能",
"http": {
"400": "リクエストに失敗しました。リクエストパラメータが正しいか確認してください。モデルの設定を変更した場合は、デフォルトの設定にリセットしてください",
"401": "認証に失敗しました。APIキーが正しいか確認してください",
@ -855,16 +862,27 @@
"503": "サービスが利用できません。後でもう一度試してください",
"504": "ゲートウェイタイムアウトが発生しました。後でもう一度試してください"
},
"lastError": "最後のエラー",
"maxEmbeddingsPerCall": "1回の呼び出しでの最大埋め込み数",
"message": "エラーメッセージ",
"missing_user_message": "モデル応答を切り替えられません:元のユーザーメッセージが削除されました。このモデルで応答を得るには、新しいメッセージを送信してください",
"model": {
"exists": "モデルが既に存在します",
"not_exists": "モデルが存在しません"
},
"modelId": "モデル ID",
"modelType": "モデルの種類",
"name": "エラー名",
"no_api_key": "APIキーが設定されていません",
"originalError": "元のエラー",
"originalMessage": "元のメッセージ",
"parameter": "パラメータ",
"pause_placeholder": "応答を一時停止しました",
"prompt": "プロンプトを表示する",
"provider": "プロバイダー",
"providerId": "プロバイダーID",
"provider_disabled": "モデルプロバイダーが有効になっていません",
"reason": "原因",
"render": {
"description": "メッセージの内容のレンダリングに失敗しました。メッセージの内容の形式が正しいか確認してください",
"title": "レンダリングエラー"
@ -872,13 +890,23 @@
"requestBody": "要求されたコンテンツ",
"requestBodyValues": "リクエストボディ",
"requestUrl": "リクエストパス",
"response": "応答",
"responseBody": "レスポンス内容",
"responseHeaders": "レスポンスヘッダー",
"responses": "応答",
"role": "キャラクター",
"stack": "スタック情報",
"status": "ステータスコード",
"statusCode": "ステータスコード",
"statusText": "状態テキスト",
"text": "テキスト",
"toolInput": "<translate_input>\nツール入力\n</translate_input>",
"toolName": "ツール名",
"unknown": "不明なエラー",
"user_message_not_found": "元のユーザーメッセージを見つけることができませんでした"
"usage": "用量",
"user_message_not_found": "元のユーザーメッセージを見つけることができませんでした",
"value": "値",
"values": "値"
},
"export": {
"assistant": "アシスタント",

View File

@ -773,6 +773,7 @@
"more": "Mais",
"name": "Nome",
"no_results": "Nenhum resultado",
"none": "Nenhum",
"open": "Abrir",
"paste": "Colar",
"preview": "Pré-visualização",
@ -817,6 +818,8 @@
"openai-response": "Resposta OpenAI"
},
"error": {
"availableProviders": "Provedores disponíveis",
"availableTools": "Ferramentas disponíveis",
"backup": {
"file_format": "Formato do arquivo de backup está incorreto"
},
@ -841,9 +844,13 @@
"quota_exceeded": "Sua cota gratuita diária de {{quota}} tokens foi esgotada. Por favor, vá para <provider>{{provider}}</provider> para obter uma chave API e configurar a chave API para continuar usando.",
"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"
},
"content": "conteúdo",
"data": "dados",
"detail": "Detalhes do erro",
"details": "Detalhes",
"errors": "erro",
"finishReason": "Motivo de término",
"functionality": "funcionalidade",
"http": {
"400": "Erro na solicitação, por favor verifique se os parâmetros da solicitação estão corretos. Se você alterou as configurações do modelo, redefina para as configurações padrão",
"401": "Falha na autenticação, por favor verifique se a chave da API está correta",
@ -855,16 +862,27 @@
"503": "Serviço indisponível, por favor tente novamente mais tarde",
"504": "Tempo de espera do gateway excedido, por favor tente novamente mais tarde"
},
"lastError": "Último erro",
"maxEmbeddingsPerCall": "Máximo de incorporações por chamada",
"message": "Mensagem de erro",
"missing_user_message": "Não é possível alternar a resposta do modelo: a mensagem original do usuário foi excluída. Envie uma nova mensagem para obter a resposta deste modelo",
"model": {
"exists": "O modelo já existe",
"not_exists": "O modelo não existe"
},
"modelId": "ID do modelo",
"modelType": "Tipo de modelo",
"name": "Nome do erro",
"no_api_key": "A chave da API não foi configurada",
"originalError": "Erro original",
"originalMessage": "Mensagem original",
"parameter": "parâmetro",
"pause_placeholder": "Interrompido",
"prompt": "prompt",
"provider": "fornecedor",
"providerId": "ID do fornecedor",
"provider_disabled": "O provedor de modelos está desativado",
"reason": "causa",
"render": {
"description": "Falha ao renderizar a fórmula, por favor verifique se o formato da fórmula está correto",
"title": "Erro de Renderização"
@ -872,13 +890,23 @@
"requestBody": "Conteúdo da solicitação",
"requestBodyValues": "Corpo da solicitação",
"requestUrl": "Caminho da solicitação",
"response": "resposta",
"responseBody": "Conteúdo da resposta",
"responseHeaders": "Cabeçalho de resposta",
"responses": "resposta",
"role": "personagem",
"stack": "Informações da pilha",
"status": "Código de status",
"statusCode": "Código de status",
"statusText": "Texto de estado",
"text": "texto",
"toolInput": "ferramenta de entrada",
"toolName": "Nome da ferramenta",
"unknown": "Erro desconhecido",
"user_message_not_found": "Não foi possível encontrar a mensagem original do usuário"
"usage": "dosagem",
"user_message_not_found": "Não foi possível encontrar a mensagem original do usuário",
"value": "valor",
"values": "valor"
},
"export": {
"assistant": "Assistente",

View File

@ -773,6 +773,7 @@
"more": "Ещё",
"name": "Имя",
"no_results": "Результатов не найдено",
"none": "без",
"open": "Открыть",
"paste": "Вставить",
"preview": "Предварительный просмотр",
@ -817,6 +818,8 @@
"openai-response": "OpenAI-Response"
},
"error": {
"availableProviders": "Доступные провайдеры",
"availableTools": "Доступные инструменты",
"backup": {
"file_format": "Ошибка формата файла резервной копии"
},
@ -841,9 +844,13 @@
"quota_exceeded": "Ваша ежедневная {{quota}} бесплатная квота исчерпана. Пожалуйста, перейдите в <provider>{{provider}}</provider> для получения ключа API и настройте ключ API для продолжения использования.",
"response": "Что-то пошло не так. Пожалуйста, проверьте, установлен ли ваш ключ API в Настройки > Провайдеры"
},
"content": "Содержание",
"data": "данные",
"detail": "Детали ошибки",
"details": "Подробности",
"errors": "ошибка",
"finishReason": "Причина завершения",
"functionality": "функция",
"http": {
"400": "Не удалось выполнить запрос. Пожалуйста, проверьте, правильно ли настроены параметры запроса. Если вы изменили настройки модели, пожалуйста, сбросьте их до значений по умолчанию",
"401": "Не удалось пройти аутентификацию. Пожалуйста, проверьте, правильно ли настроен ваш ключ API",
@ -855,16 +862,27 @@
"503": "Серверная ошибка. Пожалуйста, попробуйте позже",
"504": "Серверная ошибка. Пожалуйста, попробуйте позже"
},
"lastError": "Последняя ошибка",
"maxEmbeddingsPerCall": "Максимальное количество вложений на вызов",
"message": "Сообщение об ошибке",
"missing_user_message": "Невозможно изменить модель ответа: исходное сообщение пользователя было удалено. Пожалуйста, отправьте новое сообщение, чтобы получить ответ от этой модели",
"model": {
"exists": "Модель уже существует",
"not_exists": "Модель не существует"
},
"name": "错误名称",
"modelId": "ID модели",
"modelType": "Тип модели",
"name": "Название ошибки",
"no_api_key": "Ключ API не настроен",
"originalError": "Исходная ошибка",
"originalMessage": "исходное сообщение",
"parameter": "параметр",
"pause_placeholder": "Получение ответа приостановлено",
"prompt": "подсказка",
"provider": "поставщик",
"providerId": "ID поставщика",
"provider_disabled": "Провайдер моделей не включен",
"reason": "причина",
"render": {
"description": "Не удалось рендерить содержимое сообщения. Пожалуйста, проверьте, правильно ли формат содержимого сообщения",
"title": "Ошибка рендеринга"
@ -872,13 +890,23 @@
"requestBody": "Запрашиваемый контент",
"requestBodyValues": "Тело запроса",
"requestUrl": "Путь запроса",
"response": "ответ",
"responseBody": "Содержание ответа",
"responseHeaders": "Заголовки ответа",
"responses": "отклик",
"role": "роль",
"stack": "Информация стека",
"status": "Код статуса",
"statusCode": "Код состояния",
"statusText": "Текст состояния",
"text": "текст",
"toolInput": "ввод инструмента",
"toolName": "имя инструмента",
"unknown": "Неизвестная ошибка",
"user_message_not_found": "Не удалось найти исходное сообщение пользователя"
"usage": "Дозировка",
"user_message_not_found": "Не удалось найти исходное сообщение пользователя",
"value": "значение",
"values": "значение"
},
"export": {
"assistant": "Ассистент",

View File

@ -6,10 +6,29 @@ import { useAppDispatch } from '@renderer/store'
import { removeBlocksThunk } from '@renderer/store/thunk/messageThunk'
import {
isSerializedAiSdkAPICallError,
isSerializedAiSdkDownloadError,
isSerializedAiSdkError,
isSerializedAiSdkErrorUnion,
isSerializedAiSdkInvalidArgumentError,
isSerializedAiSdkInvalidDataContentError,
isSerializedAiSdkInvalidMessageRoleError,
isSerializedAiSdkInvalidPromptError,
isSerializedAiSdkInvalidToolInputError,
isSerializedAiSdkJSONParseError,
isSerializedAiSdkMessageConversionError,
isSerializedAiSdkNoObjectGeneratedError,
isSerializedAiSdkNoSpeechGeneratedError,
isSerializedAiSdkNoSuchModelError,
isSerializedAiSdkNoSuchProviderError,
isSerializedAiSdkNoSuchToolError,
isSerializedAiSdkRetryError,
isSerializedAiSdkToolCallRepairError,
isSerializedAiSdkTooManyEmbeddingValuesForCallError,
isSerializedAiSdkTypeValidationError,
isSerializedAiSdkUnsupportedFunctionalityError,
isSerializedError,
SerializedAiSdkAPICallError,
SerializedAiSdkError,
SerializedAiSdkErrorUnion,
SerializedError
} from '@renderer/types/error'
import type { ErrorMessageBlock, Message } from '@renderer/types/newMessage'
@ -167,10 +186,7 @@ const ErrorDetailModal: React.FC<ErrorDetailModalProps> = ({ open, onClose, erro
const renderErrorDetails = (error?: SerializedError) => {
if (!error) return <div>{t('error.unknown')}</div>
if (isSerializedAiSdkAPICallError(error)) {
return <AiApiCallError error={error} />
}
if (isSerializedAiSdkError(error)) {
if (isSerializedAiSdkErrorUnion(error)) {
return <AiSdkError error={error} />
}
return (
@ -290,7 +306,7 @@ const BuiltinError = ({ error }: { error: SerializedError }) => {
}
// 作为 base渲染公共字段应当在 ErrorDetailList 中渲染
const AiSdkError = ({ error }: { error: SerializedAiSdkError }) => {
const AiSdkErrorBase = ({ error }: { error: SerializedAiSdkError }) => {
const { t } = useTranslation()
const cause = error.cause
return (
@ -306,60 +322,289 @@ const AiSdkError = ({ error }: { error: SerializedAiSdkError }) => {
)
}
const AiApiCallError = ({ error }: { error: SerializedAiSdkAPICallError }) => {
const AiSdkError = ({ error }: { error: SerializedAiSdkErrorUnion }) => {
const { t } = useTranslation()
// 这些字段是 unknown 类型,暂且不清楚都可能是什么类型,总之先覆盖下大部分场景
const requestBodyValues = safeToString(error.requestBodyValues)
const data = safeToString(error.data)
return (
<ErrorDetailList>
<AiSdkError error={error} />
<AiSdkErrorBase error={error} />
{error.url && (
{(isSerializedAiSdkAPICallError(error) || isSerializedAiSdkDownloadError(error)) && (
<>
{error.statusCode && (
<ErrorDetailItem>
<ErrorDetailLabel>{t('error.statusCode')}:</ErrorDetailLabel>
<ErrorDetailValue>{error.statusCode}</ErrorDetailValue>
</ErrorDetailItem>
)}
{error.url && (
<ErrorDetailItem>
<ErrorDetailLabel>{t('error.requestUrl')}:</ErrorDetailLabel>
<ErrorDetailValue>{error.url}</ErrorDetailValue>
</ErrorDetailItem>
)}
</>
)}
{isSerializedAiSdkAPICallError(error) && (
<>
{error.requestBodyValues && (
<ErrorDetailItem>
<ErrorDetailLabel>{t('error.requestBodyValues')}:</ErrorDetailLabel>
<CodeViewer
value={safeToString(error.requestBodyValues)}
className="source-view"
language="json"
expanded
/>
</ErrorDetailItem>
)}
{error.responseHeaders && (
<ErrorDetailItem>
<ErrorDetailLabel>{t('error.responseHeaders')}:</ErrorDetailLabel>
<CodeViewer
value={JSON.stringify(error.responseHeaders, null, 2)}
className="source-view"
language="json"
expanded
/>
</ErrorDetailItem>
)}
{error.responseBody && (
<ErrorDetailItem>
<ErrorDetailLabel>{t('error.responseBody')}:</ErrorDetailLabel>
<CodeViewer value={error.responseBody} className="source-view" language="json" expanded />
</ErrorDetailItem>
)}
{error.data && (
<ErrorDetailItem>
<ErrorDetailLabel>{t('error.data')}:</ErrorDetailLabel>
<CodeViewer value={safeToString(error.data)} className="source-view" language="json" expanded />
</ErrorDetailItem>
)}
</>
)}
{isSerializedAiSdkDownloadError(error) && (
<>
{error.statusText && (
<ErrorDetailItem>
<ErrorDetailLabel>{t('error.statusText')}:</ErrorDetailLabel>
<ErrorDetailValue>{error.statusText}</ErrorDetailValue>
</ErrorDetailItem>
)}
</>
)}
{isSerializedAiSdkInvalidArgumentError(error) && (
<>
{error.parameter && (
<ErrorDetailItem>
<ErrorDetailLabel>{t('error.parameter')}:</ErrorDetailLabel>
<ErrorDetailValue>{error.parameter}</ErrorDetailValue>
</ErrorDetailItem>
)}
</>
)}
{(isSerializedAiSdkInvalidArgumentError(error) || isSerializedAiSdkTypeValidationError(error)) && (
<>
{error.value && (
<ErrorDetailItem>
<ErrorDetailLabel>{t('error.value')}:</ErrorDetailLabel>
<ErrorDetailValue>{safeToString(error.value)}</ErrorDetailValue>
</ErrorDetailItem>
)}
</>
)}
{isSerializedAiSdkInvalidDataContentError(error) && (
<ErrorDetailItem>
<ErrorDetailLabel>{t('error.requestUrl')}:</ErrorDetailLabel>
<ErrorDetailValue>{error.url}</ErrorDetailValue>
<ErrorDetailLabel>{t('error.content')}:</ErrorDetailLabel>
<ErrorDetailValue>{safeToString(error.content)}</ErrorDetailValue>
</ErrorDetailItem>
)}
{requestBodyValues && (
{isSerializedAiSdkInvalidMessageRoleError(error) && (
<ErrorDetailItem>
<ErrorDetailLabel>{t('error.requestBodyValues')}:</ErrorDetailLabel>
<CodeViewer value={safeToString(error.requestBodyValues)} className="source-view" language="json" expanded />
<ErrorDetailLabel>{t('error.role')}:</ErrorDetailLabel>
<ErrorDetailValue>{error.role}</ErrorDetailValue>
</ErrorDetailItem>
)}
{error.statusCode && (
{isSerializedAiSdkInvalidPromptError(error) && (
<ErrorDetailItem>
<ErrorDetailLabel>{t('error.statusCode')}:</ErrorDetailLabel>
<ErrorDetailValue>{error.statusCode}</ErrorDetailValue>
</ErrorDetailItem>
)}
{error.responseHeaders && (
<ErrorDetailItem>
<ErrorDetailLabel>{t('error.responseHeaders')}:</ErrorDetailLabel>
<CodeViewer
value={JSON.stringify(error.responseHeaders, null, 2)}
className="source-view"
language="json"
expanded
/>
<ErrorDetailLabel>{t('error.prompt')}:</ErrorDetailLabel>
<ErrorDetailValue>{safeToString(error.prompt)}</ErrorDetailValue>
</ErrorDetailItem>
)}
{error.responseBody && (
{isSerializedAiSdkInvalidToolInputError(error) && (
<>
{error.toolName && (
<ErrorDetailItem>
<ErrorDetailLabel>{t('error.toolName')}:</ErrorDetailLabel>
<ErrorDetailValue>{error.toolName}</ErrorDetailValue>
</ErrorDetailItem>
)}
{error.toolInput && (
<ErrorDetailItem>
<ErrorDetailLabel>{t('error.toolInput')}:</ErrorDetailLabel>
<ErrorDetailValue>{error.toolInput}</ErrorDetailValue>
</ErrorDetailItem>
)}
</>
)}
{(isSerializedAiSdkJSONParseError(error) || isSerializedAiSdkNoObjectGeneratedError(error)) && (
<ErrorDetailItem>
<ErrorDetailLabel>{t('error.responseBody')}:</ErrorDetailLabel>
<CodeViewer value={error.responseBody} className="source-view" language="json" expanded />
<ErrorDetailLabel>{t('error.text')}:</ErrorDetailLabel>
<ErrorDetailValue>{error.text}</ErrorDetailValue>
</ErrorDetailItem>
)}
{data && (
{isSerializedAiSdkMessageConversionError(error) && (
<ErrorDetailItem>
<ErrorDetailLabel>{t('error.data')}:</ErrorDetailLabel>
<CodeViewer value={safeToString(error.data)} className="source-view" language="json" expanded />
<ErrorDetailLabel>{t('error.originalMessage')}:</ErrorDetailLabel>
<ErrorDetailValue>{safeToString(error.originalMessage)}</ErrorDetailValue>
</ErrorDetailItem>
)}
{isSerializedAiSdkNoSpeechGeneratedError(error) && (
<ErrorDetailItem>
<ErrorDetailLabel>{t('error.responses')}:</ErrorDetailLabel>
<ErrorDetailValue>{error.responses.join(', ')}</ErrorDetailValue>
</ErrorDetailItem>
)}
{isSerializedAiSdkNoObjectGeneratedError(error) && (
<>
{error.response && (
<ErrorDetailItem>
<ErrorDetailLabel>{t('error.response')}:</ErrorDetailLabel>
<ErrorDetailValue>{safeToString(error.response)}</ErrorDetailValue>
</ErrorDetailItem>
)}
{error.usage && (
<ErrorDetailItem>
<ErrorDetailLabel>{t('error.usage')}:</ErrorDetailLabel>
<ErrorDetailValue>{safeToString(error.usage)}</ErrorDetailValue>
</ErrorDetailItem>
)}
{error.finishReason && (
<ErrorDetailItem>
<ErrorDetailLabel>{t('error.finishReason')}:</ErrorDetailLabel>
<ErrorDetailValue>{error.finishReason}</ErrorDetailValue>
</ErrorDetailItem>
)}
</>
)}
{(isSerializedAiSdkNoSuchModelError(error) ||
isSerializedAiSdkNoSuchProviderError(error) ||
isSerializedAiSdkTooManyEmbeddingValuesForCallError(error)) && (
<ErrorDetailItem>
<ErrorDetailLabel>{t('error.modelId')}:</ErrorDetailLabel>
<ErrorDetailValue>{error.modelId}</ErrorDetailValue>
</ErrorDetailItem>
)}
{(isSerializedAiSdkNoSuchModelError(error) || isSerializedAiSdkNoSuchProviderError(error)) && (
<ErrorDetailItem>
<ErrorDetailLabel>{t('error.modelType')}:</ErrorDetailLabel>
<ErrorDetailValue>{error.modelType}</ErrorDetailValue>
</ErrorDetailItem>
)}
{isSerializedAiSdkNoSuchProviderError(error) && (
<>
<ErrorDetailItem>
<ErrorDetailLabel>{t('error.providerId')}:</ErrorDetailLabel>
<ErrorDetailValue>{error.providerId}</ErrorDetailValue>
</ErrorDetailItem>
<ErrorDetailItem>
<ErrorDetailLabel>{t('error.availableProviders')}:</ErrorDetailLabel>
<ErrorDetailValue>{error.availableProviders.join(', ')}</ErrorDetailValue>
</ErrorDetailItem>
</>
)}
{isSerializedAiSdkNoSuchToolError(error) && (
<>
<ErrorDetailItem>
<ErrorDetailLabel>{t('error.toolName')}:</ErrorDetailLabel>
<ErrorDetailValue>{error.toolName}</ErrorDetailValue>
</ErrorDetailItem>
{error.availableTools && (
<ErrorDetailItem>
<ErrorDetailLabel>{t('error.availableTools')}:</ErrorDetailLabel>
<ErrorDetailValue>{error.availableTools?.join(', ') || t('common.none')}</ErrorDetailValue>
</ErrorDetailItem>
)}
</>
)}
{isSerializedAiSdkRetryError(error) && (
<>
{error.reason && (
<ErrorDetailItem>
<ErrorDetailLabel>{t('error.reason')}:</ErrorDetailLabel>
<ErrorDetailValue>{error.reason}</ErrorDetailValue>
</ErrorDetailItem>
)}
{error.lastError && (
<ErrorDetailItem>
<ErrorDetailLabel>{t('error.lastError')}:</ErrorDetailLabel>
<ErrorDetailValue>{safeToString(error.lastError)}</ErrorDetailValue>
</ErrorDetailItem>
)}
{error.errors && error.errors.length > 0 && (
<ErrorDetailItem>
<ErrorDetailLabel>{t('error.errors')}:</ErrorDetailLabel>
<ErrorDetailValue>{error.errors.map((e) => safeToString(e)).join('\n\n')}</ErrorDetailValue>
</ErrorDetailItem>
)}
</>
)}
{isSerializedAiSdkTooManyEmbeddingValuesForCallError(error) && (
<>
{error.provider && (
<ErrorDetailItem>
<ErrorDetailLabel>{t('error.provider')}:</ErrorDetailLabel>
<ErrorDetailValue>{error.provider}</ErrorDetailValue>
</ErrorDetailItem>
)}
{error.maxEmbeddingsPerCall && (
<ErrorDetailItem>
<ErrorDetailLabel>{t('error.maxEmbeddingsPerCall')}:</ErrorDetailLabel>
<ErrorDetailValue>{error.maxEmbeddingsPerCall}</ErrorDetailValue>
</ErrorDetailItem>
)}
{error.values && (
<ErrorDetailItem>
<ErrorDetailLabel>{t('error.values')}:</ErrorDetailLabel>
<ErrorDetailValue>{safeToString(error.values)}</ErrorDetailValue>
</ErrorDetailItem>
)}
</>
)}
{isSerializedAiSdkToolCallRepairError(error) && (
<ErrorDetailItem>
<ErrorDetailLabel>{t('error.originalError')}:</ErrorDetailLabel>
<ErrorDetailValue>{safeToString(error.originalError)}</ErrorDetailValue>
</ErrorDetailItem>
)}
{isSerializedAiSdkUnsupportedFunctionalityError(error) && (
<ErrorDetailItem>
<ErrorDetailLabel>{t('error.functionality')}:</ErrorDetailLabel>
<ErrorDetailValue>{error.functionality}</ErrorDetailValue>
</ErrorDetailItem>
)}
</ErrorDetailList>

View File

@ -1,4 +1,4 @@
import type { AISDKError, APICallError, ImageModel, LanguageModel } from 'ai'
import type { ImageModel, LanguageModel } from 'ai'
import { generateObject, generateText, ModelMessage, streamObject, streamText } from 'ai'
export type StreamTextParams = Omit<Parameters<typeof streamText>[0], 'model' | 'messages'> &
@ -27,6 +27,3 @@ export type StreamObjectParams = Omit<Parameters<typeof streamObject>[0], 'model
export type GenerateObjectParams = Omit<Parameters<typeof generateObject>[0], 'model'>
export type AiSdkModel = LanguageModel | ImageModel
// 该类型用于格式化错误信息,目前只处理 APICallError待扩展
export type AiSdkErrorUnion = AISDKError | APICallError

View File

@ -1,3 +1,25 @@
import {
AISDKError,
APICallError,
DownloadError,
FinishReason,
InvalidArgumentError,
InvalidDataContentError,
InvalidMessageRoleError,
InvalidPromptError,
InvalidToolInputError,
JSONParseError,
MessageConversionError,
NoObjectGeneratedError,
NoSuchModelError,
NoSuchProviderError,
NoSuchToolError,
RetryError,
ToolCallRepairError,
TypeValidationError,
UnsupportedFunctionalityError
} from 'ai'
import { Serializable } from './serialize'
export interface SerializedError {
@ -6,7 +28,7 @@ export interface SerializedError {
stack: string | null
[key: string]: Serializable
}
export const isSerializedError = (error: Record<string, unknown>): error is SerializedAiSdkError => {
export const isSerializedError = (error: Record<string, unknown>): error is SerializedError => {
return 'name' in error && 'message' in error && 'stack' in error
}
export interface SerializedAiSdkError extends SerializedError {
@ -28,5 +50,276 @@ export interface SerializedAiSdkAPICallError extends SerializedAiSdkError {
}
export const isSerializedAiSdkAPICallError = (error: SerializedError): error is SerializedAiSdkAPICallError => {
return isSerializedAiSdkError(error) && 'url' in error && 'requestBodyValues' in error && 'isRetryable' in error
return (
isSerializedAiSdkError(error) &&
'url' in error &&
'requestBodyValues' in error &&
'statusCode' in error &&
'responseHeaders' in error &&
'responseBody' in error &&
'isRetryable' in error &&
'data' in error
)
}
export interface SerializedAiSdkDownloadError extends SerializedAiSdkError {
readonly url: string
readonly statusCode: number | null
readonly statusText: string | null
}
export const isSerializedAiSdkDownloadError = (error: SerializedError): error is SerializedAiSdkDownloadError => {
return isSerializedAiSdkError(error) && 'url' in error && 'statusCode' in error && 'statusText' in error
}
export interface SerializedAiSdkInvalidArgumentError extends SerializedAiSdkError {
readonly parameter: string
readonly value: Serializable
}
export const isSerializedAiSdkInvalidArgumentError = (
error: SerializedError
): error is SerializedAiSdkInvalidArgumentError => {
return isSerializedAiSdkError(error) && 'parameter' in error && 'value' in error
}
export interface SerializedAiSdkInvalidDataContentError extends SerializedAiSdkError {
readonly content: Serializable
}
export const isSerializedAiSdkInvalidDataContentError = (
error: SerializedError
): error is SerializedAiSdkInvalidDataContentError => {
return isSerializedAiSdkError(error) && 'content' in error
}
export interface SerializedAiSdkInvalidMessageRoleError extends SerializedAiSdkError {
readonly role: string
}
export const isSerializedAiSdkInvalidMessageRoleError = (
error: SerializedError
): error is SerializedAiSdkInvalidMessageRoleError => {
return isSerializedAiSdkError(error) && 'role' in error
}
export interface SerializedAiSdkInvalidPromptError extends SerializedAiSdkError {
readonly prompt: Serializable
}
export const isSerializedAiSdkInvalidPromptError = (
error: SerializedError
): error is SerializedAiSdkInvalidPromptError => {
return isSerializedAiSdkError(error) && 'prompt' in error
}
export interface SerializedAiSdkInvalidToolInputError extends SerializedAiSdkError {
readonly toolName: string
readonly toolInput: string
}
export const isSerializedAiSdkInvalidToolInputError = (
error: SerializedError
): error is SerializedAiSdkInvalidToolInputError => {
return isSerializedAiSdkError(error) && 'toolName' in error && 'toolInput' in error
}
export interface SerializedAiSdkJSONParseError extends SerializedAiSdkError {
readonly text: string
}
export const isSerializedAiSdkJSONParseError = (error: SerializedError): error is SerializedAiSdkJSONParseError => {
return isSerializedAiSdkError(error) && 'text' in error
}
export interface SerializedAiSdkMessageConversionError extends SerializedAiSdkError {
readonly originalMessage: Serializable
}
export const isSerializedAiSdkMessageConversionError = (
error: SerializedError
): error is SerializedAiSdkMessageConversionError => {
return isSerializedAiSdkError(error) && 'originalMessage' in error
}
// This type is not exported by aisdk.
// See https://github.com/vercel/ai/issues/8466
export interface SerializedAiSdkNoSpeechGeneratedError extends SerializedAiSdkError {
readonly responses: string[]
}
export const isSerializedAiSdkNoSpeechGeneratedError = (
error: SerializedError
): error is SerializedAiSdkNoSpeechGeneratedError => {
return isSerializedAiSdkError(error) && 'responses' in error
}
export interface SerializedAiSdkNoObjectGeneratedError extends SerializedAiSdkError {
readonly text: string | null
readonly response: Serializable
readonly usage: Serializable
readonly finishReason: FinishReason | null
}
export const isSerializedAiSdkNoObjectGeneratedError = (
error: SerializedError
): error is SerializedAiSdkNoObjectGeneratedError => {
return (
isSerializedAiSdkError(error) &&
'text' in error &&
'response' in error &&
'usage' in error &&
'finishReason' in error
)
}
export interface SerializedAiSdkNoSuchModelError extends SerializedAiSdkError {
readonly modelId: string
readonly modelType: NoSuchModelError['modelType']
}
export const isSerializedAiSdkNoSuchModelError = (error: SerializedError): error is SerializedAiSdkNoSuchModelError => {
return isSerializedAiSdkError(error) && 'modelId' in error && 'modelType' in error
}
export interface SerializedAiSdkNoSuchProviderError extends SerializedAiSdkNoSuchModelError {
readonly providerId: string
readonly availableProviders: string[]
}
export const isSerializedAiSdkNoSuchProviderError = (
error: SerializedError
): error is SerializedAiSdkNoSuchProviderError => {
return isSerializedAiSdkNoSuchModelError(error) && 'providerId' in error && 'availableProviders' in error
}
export interface SerializedAiSdkNoSuchToolError extends SerializedAiSdkError {
readonly toolName: string
readonly availableTools: string[] | null
}
export const isSerializedAiSdkNoSuchToolError = (error: SerializedError): error is SerializedAiSdkNoSuchToolError => {
return isSerializedAiSdkError(error) && 'toolName' in error && 'availableTools' in error
}
export interface SerializedAiSdkRetryError extends SerializedAiSdkError {
readonly reason: string
readonly lastError: Serializable
readonly errors: Serializable[]
}
export const isSerializedAiSdkRetryError = (error: SerializedError): error is SerializedAiSdkRetryError => {
return isSerializedAiSdkError(error) && 'reason' in error && 'lastError' in error && 'errors' in error
}
// This type is not exported by aisdk.
// See: https://github.com/vercel/ai/pull/8464
export interface SerializedAiSdkTooManyEmbeddingValuesForCallError extends SerializedAiSdkError {
readonly provider: string
readonly modelId: string
readonly maxEmbeddingsPerCall: number
readonly values: Serializable[]
}
export const isSerializedAiSdkTooManyEmbeddingValuesForCallError = (
error: SerializedError
): error is SerializedAiSdkTooManyEmbeddingValuesForCallError => {
return (
isSerializedAiSdkError(error) &&
'provider' in error &&
'modelId' in error &&
'maxEmbeddingsPerCall' in error &&
'values' in error
)
}
export interface SerializedAiSdkToolCallRepairError extends SerializedAiSdkError {
readonly originalError: SerializedAiSdkNoSuchToolError | SerializedAiSdkInvalidToolInputError
}
export const isSerializedAiSdkToolCallRepairError = (
error: SerializedError
): error is SerializedAiSdkToolCallRepairError => {
return isSerializedAiSdkError(error) && 'originalError' in error
}
export interface SerializedAiSdkTypeValidationError extends SerializedAiSdkError {
readonly value: Serializable
}
export const isSerializedAiSdkTypeValidationError = (
error: SerializedError
): error is SerializedAiSdkTypeValidationError => {
return isSerializedAiSdkError(error) && 'value' in error && !('parameter' in error)
}
export interface SerializedAiSdkUnsupportedFunctionalityError extends SerializedAiSdkError {
readonly functionality: string
}
export const isSerializedAiSdkUnsupportedFunctionalityError = (
error: SerializedError
): error is SerializedAiSdkUnsupportedFunctionalityError => {
return isSerializedAiSdkError(error) && 'functionality' in error
}
export type AiSdkErrorUnion =
| AISDKError
| APICallError
| DownloadError
| InvalidArgumentError
| InvalidDataContentError
| InvalidMessageRoleError
| InvalidPromptError
| InvalidToolInputError
| JSONParseError
| MessageConversionError
| NoObjectGeneratedError
| NoSuchModelError
| NoSuchProviderError
| NoSuchToolError
| RetryError
| ToolCallRepairError
| TypeValidationError
| UnsupportedFunctionalityError
export type SerializedAiSdkErrorUnion =
| SerializedAiSdkAPICallError
| SerializedAiSdkDownloadError
| SerializedAiSdkInvalidArgumentError
| SerializedAiSdkInvalidDataContentError
| SerializedAiSdkInvalidMessageRoleError
| SerializedAiSdkInvalidPromptError
| SerializedAiSdkInvalidToolInputError
| SerializedAiSdkJSONParseError
| SerializedAiSdkMessageConversionError
| SerializedAiSdkNoSpeechGeneratedError
| SerializedAiSdkNoObjectGeneratedError
| SerializedAiSdkNoSuchModelError
| SerializedAiSdkNoSuchProviderError
| SerializedAiSdkNoSuchToolError
| SerializedAiSdkRetryError
| SerializedAiSdkToolCallRepairError
| SerializedAiSdkTypeValidationError
| SerializedAiSdkUnsupportedFunctionalityError
export const isSerializedAiSdkErrorUnion = (error: SerializedError): error is SerializedAiSdkErrorUnion => {
return (
isSerializedAiSdkAPICallError(error) ||
isSerializedAiSdkDownloadError(error) ||
isSerializedAiSdkInvalidArgumentError(error) ||
isSerializedAiSdkInvalidDataContentError(error) ||
isSerializedAiSdkInvalidMessageRoleError(error) ||
isSerializedAiSdkInvalidPromptError(error) ||
isSerializedAiSdkInvalidToolInputError(error) ||
isSerializedAiSdkJSONParseError(error) ||
isSerializedAiSdkMessageConversionError(error) ||
isSerializedAiSdkNoObjectGeneratedError(error) ||
isSerializedAiSdkNoSuchModelError(error) ||
isSerializedAiSdkNoSuchProviderError(error) ||
isSerializedAiSdkNoSuchToolError(error) ||
isSerializedAiSdkRetryError(error) ||
isSerializedAiSdkToolCallRepairError(error) ||
isSerializedAiSdkTypeValidationError(error) ||
isSerializedAiSdkUnsupportedFunctionalityError(error)
)
}

View File

@ -1,17 +1,18 @@
import { loggerService } from '@logger'
import {
AiSdkErrorUnion,
isSerializedAiSdkAPICallError,
SerializedAiSdkAPICallError,
SerializedAiSdkError,
SerializedAiSdkInvalidToolInputError,
SerializedAiSdkNoSuchToolError,
SerializedError
} from '@renderer/types/error'
import { AISDKError, APICallError } from 'ai'
import { InvalidToolInputError, NoSuchToolError } from 'ai'
import { t } from 'i18next'
import z from 'zod'
import { safeSerialize } from './serialize'
const logger = loggerService.withContext('Utils:error')
// const logger = loggerService.withContext('Utils:error')
export function getErrorDetails(err: any, seen = new WeakSet()): any {
// Handle circular references
@ -95,33 +96,77 @@ export const formatMcpError = (error: any) => {
return error.message
}
export const serializeError = (error: AISDKError): SerializedError => {
const baseError = {
name: error.name,
message: error.message,
const getBaseError = (error: Error) => {
return {
name: error.name ?? null,
message: error.message ?? null,
stack: error.stack ?? null,
cause: error.cause ? String(error.cause) : null
} as const
}
const serializeInvalidToolInputError = (error: InvalidToolInputError): SerializedAiSdkInvalidToolInputError => {
const baseError = getBaseError(error)
return {
...baseError,
toolName: error.toolName,
toolInput: error.toolInput
} satisfies SerializedAiSdkInvalidToolInputError
}
const serializeNoSuchToolError = (error: NoSuchToolError): SerializedAiSdkNoSuchToolError => {
const baseError = getBaseError(error)
return {
...baseError,
toolName: error.toolName ?? null,
availableTools: error.availableTools ?? null
} satisfies SerializedAiSdkNoSuchToolError
}
export const serializeError = (error: AiSdkErrorUnion): SerializedError => {
// 统一所有可能的错误字段
const serializedError: SerializedError = {
name: error.name ?? null,
message: error.message ?? null,
stack: error.stack ?? null,
cause: safeSerialize(error.cause)
}
if (APICallError.isInstance(error)) {
let content = error.message === '' ? error.responseBody || 'Unknown error' : error.message
try {
const obj = JSON.parse(content)
content = obj.error.message
} catch (e: any) {
logger.warn('Error parsing error response body:', e)
}
return {
...baseError,
url: error.url,
requestBodyValues: safeSerialize(error.requestBodyValues),
statusCode: error.statusCode ?? null,
responseBody: content,
isRetryable: error.isRetryable,
data: safeSerialize(error.data),
responseHeaders: error.responseHeaders ?? null
} satisfies SerializedAiSdkAPICallError
}
return baseError
if ('url' in error) serializedError.url = error.url
if ('requestBodyValues' in error) serializedError.requestBodyValues = safeSerialize(error.requestBodyValues)
if ('statusCode' in error) serializedError.statusCode = error.statusCode ?? null
if ('responseBody' in error) serializedError.responseBody = error.responseBody ?? null
if ('isRetryable' in error) serializedError.isRetryable = error.isRetryable
if ('data' in error) serializedError.data = safeSerialize(error.data)
if ('responseHeaders' in error) serializedError.responseHeaders = error.responseHeaders ?? null
if ('statusText' in error) serializedError.statusText = error.statusText ?? null
if ('parameter' in error) serializedError.parameter = error.parameter
if ('value' in error) serializedError.value = safeSerialize(error.value)
if ('content' in error) serializedError.content = safeSerialize(error.content)
if ('role' in error) serializedError.role = error.role
if ('prompt' in error) serializedError.prompt = safeSerialize(error.prompt)
if ('toolName' in error) serializedError.toolName = error.toolName
if ('toolInput' in error) serializedError.toolInput = error.toolInput
if ('text' in error) serializedError.text = error.text ?? null
if ('originalMessage' in error) serializedError.originalMessage = safeSerialize(error.originalMessage)
if ('response' in error) serializedError.response = error.response ?? null
if ('usage' in error) serializedError.usage = safeSerialize(error.usage)
if ('finishReason' in error) serializedError.finishReason = error.finishReason ?? null
if ('modelId' in error) serializedError.modelId = error.modelId
if ('modelType' in error) serializedError.modelType = error.modelType
if ('providerId' in error) serializedError.providerId = error.providerId
if ('availableProviders' in error) serializedError.availableProviders = error.availableProviders
if ('availableTools' in error) serializedError.availableTools = error.availableTools ?? null
if ('reason' in error) serializedError.reason = error.reason
if ('lastError' in error) serializedError.lastError = safeSerialize(error.lastError)
if ('errors' in error) serializedError.errors = error.errors.map((err: unknown) => safeSerialize(err))
if ('originalError' in error)
serializedError.originalError = InvalidToolInputError.isInstance(error.originalError)
? serializeInvalidToolInputError(error.originalError)
: serializeNoSuchToolError(error.originalError)
if ('functionality' in error) serializedError.functionality = error.functionality
return serializedError
}
/**
* Zod