From 13edffc8ce03a7e36edec9e5cedebdca16c4f363 Mon Sep 17 00:00:00 2001 From: open-master Date: Tue, 7 Jul 2026 16:58:18 +0800 Subject: [PATCH] fix(knowledge): show friendly ASR parse failure messages for knowledge-base model MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Point users to System Model Settings → Knowledge Base Model → ASR when media parsing fails due to missing or invalid ASR configuration, including platform upload preview and client knowledge space tooltips. Co-authored-by: Cursor --- src/backend/bisheng/common/errcode/server.py | 12 +- src/frontend/client/src/api/knowledge.ts | 12 +- .../src/api/knowledgeParseFailureMessage.ts | 91 +++++++++++++ .../client/src/locales/en/translation.json | 12 +- .../client/src/locales/ja/translation.json | 12 +- .../src/locales/zh-Hans/translation.json | 12 +- .../knowledge/hooks/useFileUpload.test.ts | 14 ++ .../platform/public/locales/en-US/bs.json | 12 +- .../platform/public/locales/ja/bs.json | 12 +- .../platform/public/locales/zh-Hans/bs.json | 12 +- .../platform/src/controllers/API/index.ts | 4 +- .../components/FileUploadStep4.tsx | 22 +--- .../pages/KnowledgePage/components/Files.tsx | 12 +- .../KnowledgePage/components/PreviewFile.tsx | 15 ++- .../components/PreviewResult.tsx | 10 ++ .../knowledgeParseFailureMessage.ts | 121 ++++++++++++++++++ 16 files changed, 303 insertions(+), 82 deletions(-) create mode 100644 src/frontend/client/src/api/knowledgeParseFailureMessage.ts create mode 100644 src/frontend/platform/src/pages/KnowledgePage/knowledgeParseFailureMessage.ts diff --git a/src/backend/bisheng/common/errcode/server.py b/src/backend/bisheng/common/errcode/server.py index 76ce88bab..d2ae5762f 100644 --- a/src/backend/bisheng/common/errcode/server.py +++ b/src/backend/bisheng/common/errcode/server.py @@ -81,32 +81,32 @@ class InitLlmError(BaseErrorCode): class NoAsrModelConfigError(BaseErrorCode): Code: int = 10014 - Msg: str = 'No bulkpost found in Trashasrmodel config' + Msg: str = 'Knowledge base ASR model is not configured' class AsrModelConfigDeletedError(BaseErrorCode): Code: int = 10015 - Msg: str = 'asrModel configuration has been deleted, please reconfigure the model' + Msg: str = 'Knowledge base ASR model configuration has been deleted' class AsrProviderDeletedError(BaseErrorCode): Code: int = 10016 - Msg: str = 'Service provider configuration has been deleted, please reconfigureasrModels' + Msg: str = 'Knowledge base ASR provider has been deleted' class AsrModelTypeError(BaseErrorCode): Code: int = 10017 - Msg: str = 'Support onlyASRModel of type, not supported{model_type}Type of model' + Msg: str = 'Only ASR-type models are supported for knowledge base media transcription, got {model_type}' class AsrModelOfflineError(BaseErrorCode): Code: int = 10018 - Msg: str = '{server_name}under{model_name}The model is offline, please contact the administrator to launch the corresponding model' + Msg: str = 'Knowledge base ASR model {model_name} under {server_name} is offline' class InitAsrError(BaseErrorCode): Code: int = 10019 - Msg: str = 'InisialisasiasrFailed, please check the configuration or contact the administrator.Error message:{exception}' + Msg: str = 'Failed to initialize knowledge base ASR. Error: {exception}' class NoTtsModelConfigError(BaseErrorCode): diff --git a/src/frontend/client/src/api/knowledge.ts b/src/frontend/client/src/api/knowledge.ts index 9cb65e83d..739773ba2 100644 --- a/src/frontend/client/src/api/knowledge.ts +++ b/src/frontend/client/src/api/knowledge.ts @@ -1,4 +1,5 @@ import request from "./request"; +import { resolveKnowledgeParseFailureMessage } from "./knowledgeParseFailureMessage"; // Standard backend response wrapper interface ApiResponse { @@ -471,16 +472,7 @@ function formatSensitiveViolationMessage(hits: any[]): string { } function formatMediaParseFailureMessage(parsed: any): string | undefined { - if (parsed?.status_code === 10956) { - return "未检测到可识别音频,无法生成识别文本。请上传包含清晰人声的音频或视频文件。"; - } - - const exception = String(parsed?.data?.exception ?? "").trim().toLowerCase(); - if (exception === "media audio extraction failed" || exception === "asr returned empty text") { - return "未检测到可识别音频,无法生成识别文本。请上传包含清晰人声的音频或视频文件。"; - } - - return undefined; + return resolveKnowledgeParseFailureMessage(parsed); } export function extractKnowledgeFileError(raw: any): string | undefined { diff --git a/src/frontend/client/src/api/knowledgeParseFailureMessage.ts b/src/frontend/client/src/api/knowledgeParseFailureMessage.ts new file mode 100644 index 000000000..887ec2688 --- /dev/null +++ b/src/frontend/client/src/api/knowledgeParseFailureMessage.ts @@ -0,0 +1,91 @@ +import i18next from "i18next"; + +const ASR_ERROR_CODES = new Set([10014, 10015, 10016, 10017, 10018, 10019]); + +const API_ERROR_FALLBACKS: Record = { + 10014: "未配置知识库语音转文字(ASR)模型,请在「系统模型设置 → 知识库模型」中配置后再上传音视频。", + 10015: "知识库 ASR 模型配置已被删除,请在「系统模型设置 → 知识库模型」中重新配置。", + 10016: "知识库 ASR 服务提供方已被删除,请在「系统模型设置 → 知识库模型」中重新配置。", + 10017: "知识库 ASR 模型类型不正确,请在「系统模型设置 → 知识库模型」中选择 ASR 类型模型(当前:{{model_type}})。", + 10018: "知识库 ASR 模型「{{server_name}}/{{model_name}}」已下线,请在「系统模型设置 → 知识库模型」中更换可用模型。", + 10019: "知识库 ASR 初始化失败,请检查「系统模型设置 → 知识库模型」中的配置。错误信息:{{exception}}", + 10956: "未检测到可识别音频,无法生成识别文本。请上传包含清晰人声的音频或视频文件。", +}; + +function applyTemplate(template: string, data?: Record): string { + if (!data) return template; + return template.replace(/\{\{(\w+)\}\}/g, (_, key: string) => { + const value = data[key]; + return value === undefined || value === null ? "" : String(value); + }); +} + +function translateApiError(statusCode: number, data?: Record): string | undefined { + const key = `api_errors.${statusCode}`; + if (i18next.exists(key)) { + return i18next.t(key, data); + } + const fallback = API_ERROR_FALLBACKS[statusCode]; + return fallback ? applyTemplate(fallback, data) : undefined; +} + +function translateKnowledgeMedia(key: string): string | undefined { + const fullKey = `com_knowledge.${key}`; + if (i18next.exists(fullKey)) { + return i18next.t(fullKey); + } + return undefined; +} + +function resolve10954Message(parsed: Record, errorData: Record): string | undefined { + const statusMessage = String( + parsed.status_message ?? parsed.message ?? errorData.exception ?? "", + ).toLowerCase(); + + if (statusMessage.includes("api key is missing")) { + return translateKnowledgeMedia("media_asr_api_key_missing"); + } + if ( + statusMessage.includes("only supports aliyun/qwen") + || statusMessage.includes("knowledge media transcription only supports") + ) { + return translateKnowledgeMedia("media_asr_provider_unsupported"); + } + if (statusMessage.includes("asr request failed")) { + return translateKnowledgeMedia("media_asr_request_failed"); + } + return translateApiError(10954, errorData); +} + +/** Resolve knowledge file parse failure messages, including knowledge-base ASR errors. */ +export function resolveKnowledgeParseFailureMessage(parsed: Record): string | undefined { + const statusCode = Number(parsed.status_code ?? parsed.code); + if (!Number.isFinite(statusCode)) { + return undefined; + } + + const errorData = (parsed.data && typeof parsed.data === "object" + ? parsed.data + : {}) as Record; + + if (statusCode === 10956) { + return translateApiError(10956, errorData) + ?? translateKnowledgeMedia("media_no_recognizable_audio"); + } + + const exception = String(errorData.exception ?? "").trim().toLowerCase(); + if (exception === "media audio extraction failed" || exception === "asr returned empty text") { + return translateApiError(10956, errorData) + ?? translateKnowledgeMedia("media_no_recognizable_audio"); + } + + if (ASR_ERROR_CODES.has(statusCode)) { + return translateApiError(statusCode, errorData); + } + + if (statusCode === 10954) { + return resolve10954Message(parsed, errorData); + } + + return undefined; +} diff --git a/src/frontend/client/src/locales/en/translation.json b/src/frontend/client/src/locales/en/translation.json index 8fddb1a50..5142b4ab6 100644 --- a/src/frontend/client/src/locales/en/translation.json +++ b/src/frontend/client/src/locales/en/translation.json @@ -1273,12 +1273,12 @@ "10011": "Only LLM-type models are supported. {{model_type}} is not supported", "10012": "Model {{model_name}} under {{server_name}} is offline. Contact admin", "10013": "Failed to initialize LLM. Check settings or contact admin. Error: {{exception}}", - "10014": "ASR configuration not found", - "10015": "ASR configuration deleted. Please reconfigure", - "10016": "Provider settings removed. Please reconfigure ASR", - "10017": "Only ASR-type models are supported. {{model_type}} is not supported", - "10018": "ASR model {{model_name}} under {{server_name}} is offline", - "10019": "Failed to initialize ASR: {{exception}}", + "10014": "Knowledge base speech-to-text (ASR) model is not configured. Configure it under System Model Settings → Knowledge Base Model before uploading audio or video.", + "10015": "Knowledge base ASR model configuration has been deleted. Reconfigure it under System Model Settings → Knowledge Base Model.", + "10016": "Knowledge base ASR provider has been deleted. Reconfigure it under System Model Settings → Knowledge Base Model.", + "10017": "Invalid knowledge base ASR model type. Select an ASR model under System Model Settings → Knowledge Base Model (current: {{model_type}}).", + "10018": "Knowledge base ASR model \"{{server_name}}/{{model_name}}\" is offline. Choose an available model under System Model Settings → Knowledge Base Model.", + "10019": "Failed to initialize knowledge base ASR. Check the configuration under System Model Settings → Knowledge Base Model. Error: {{exception}}", "10020": "TTS configuration not found", "10021": "TTS configuration deleted. Please reconfigure", "10022": "Provider settings removed. Please reconfigure TTS", diff --git a/src/frontend/client/src/locales/ja/translation.json b/src/frontend/client/src/locales/ja/translation.json index aca0b5c5c..84660daba 100644 --- a/src/frontend/client/src/locales/ja/translation.json +++ b/src/frontend/client/src/locales/ja/translation.json @@ -1197,12 +1197,12 @@ "10011": "LLMタイプのみサポートしています。{{model_type}} は非対応です", "10012": "{{server_name}} の {{model_name}} はオフラインです", "10013": "LLMの初期化に失敗しました:{{exception}}", - "10014": "ASR設定が見つかりません", - "10015": "ASR設定が削除されました", - "10016": "提供元設定が削除されました。ASRを再設定してください", - "10017": "ASRタイプのみサポートしています", - "10018": "ASRモデル {{model_name}} はオフラインです", - "10019": "ASR初期化に失敗しました:{{exception}}", + "10014": "ナレッジベースの音声文字起こし(ASR)モデルが未設定です。「システムモデル設定 → ナレッジベースモデル」で設定してから音声・動画をアップロードしてください。", + "10015": "ナレッジベース ASR モデル設定が削除されました。「システムモデル設定 → ナレッジベースモデル」で再設定してください。", + "10016": "ナレッジベース ASR プロバイダーが削除されました。「システムモデル設定 → ナレッジベースモデル」で再設定してください。", + "10017": "ナレッジベース ASR モデルタイプが不正です。「システムモデル設定 → ナレッジベースモデル」で ASR タイプのモデルを選択してください(現在:{{model_type}})。", + "10018": "ナレッジベース ASR モデル「{{server_name}}/{{model_name}}」がオフラインです。「システムモデル設定 → ナレッジベースモデル」で利用可能なモデルに変更してください。", + "10019": "ナレッジベース ASR の初期化に失敗しました。「システムモデル設定 → ナレッジベースモデル」の設定を確認してください。エラー:{{exception}}", "10020": "TTS設定が見つかりません", "10021": "TTS設定が削除されました", "10022": "提供元設定が削除されました。TTSを再設定してください", diff --git a/src/frontend/client/src/locales/zh-Hans/translation.json b/src/frontend/client/src/locales/zh-Hans/translation.json index 448787d4c..be777a78f 100644 --- a/src/frontend/client/src/locales/zh-Hans/translation.json +++ b/src/frontend/client/src/locales/zh-Hans/translation.json @@ -1200,12 +1200,12 @@ "10011": "只支持LLM类型的模型,不支持{{model_type}}类型的模型", "10012": "{{server_name}}下的{{model_name}}模型已下线,请联系管理员上线对应的模型", "10013": "初始化llm失败,请检查配置或联系管理员。错误信息:{{exception}}", - "10014": "没有找到asr模型配置", - "10015": "asr模型配置已被删除,请重新配置模型", - "10016": "服务提供方配置已被删除,请重新配置asr模型", - "10017": "只支持ASR类型的模型,不支持{{model_type}}类型的模型", - "10018": "{{server_name}}下的{{model_name}}模型已下线,请联系管理员上线对应的模型", - "10019": "初始化asr失败,请检查配置或联系管理员。错误信息:{{exception}}", + "10014": "未配置知识库语音转文字(ASR)模型,请在「系统模型设置 → 知识库模型」中配置后再上传音视频。", + "10015": "知识库 ASR 模型配置已被删除,请在「系统模型设置 → 知识库模型」中重新配置。", + "10016": "知识库 ASR 服务提供方已被删除,请在「系统模型设置 → 知识库模型」中重新配置。", + "10017": "知识库 ASR 模型类型不正确,请在「系统模型设置 → 知识库模型」中选择 ASR 类型模型(当前:{{model_type}})。", + "10018": "知识库 ASR 模型「{{server_name}}/{{model_name}}」已下线,请在「系统模型设置 → 知识库模型」中更换可用模型。", + "10019": "知识库 ASR 初始化失败,请检查「系统模型设置 → 知识库模型」中的配置。错误信息:{{exception}}", "10020": "没有找到tts模型配置", "10021": "tts模型配置已被删除,请重新配置模型", "10022": "服务提供方配置已被删除,请重新配置tts模型", diff --git a/src/frontend/client/src/pages/knowledge/hooks/useFileUpload.test.ts b/src/frontend/client/src/pages/knowledge/hooks/useFileUpload.test.ts index c7a622b9c..02dc142b8 100644 --- a/src/frontend/client/src/pages/knowledge/hooks/useFileUpload.test.ts +++ b/src/frontend/client/src/pages/knowledge/hooks/useFileUpload.test.ts @@ -114,6 +114,20 @@ describe("useFileUpload helpers", () => { ); }); + test("extractKnowledgeFileError formats missing knowledge-base ASR configuration", () => { + const remark = JSON.stringify({ + status_code: 10014, + status_message: "Knowledge base ASR model is not configured", + data: { + exception: "Knowledge base ASR model is not configured", + }, + }); + + expect(extractKnowledgeFileError({ remark })).toBe( + "未配置知识库语音转文字(ASR)模型,请在「系统模型设置 → 知识库模型」中配置后再上传音视频。", + ); + }); + test("extractKnowledgeFileError formats sensitive check hits for violation detail", () => { const remark = JSON.stringify({ reason: "sensitive_check", diff --git a/src/frontend/platform/public/locales/en-US/bs.json b/src/frontend/platform/public/locales/en-US/bs.json index ef1e4f3ca..9b118da41 100644 --- a/src/frontend/platform/public/locales/en-US/bs.json +++ b/src/frontend/platform/public/locales/en-US/bs.json @@ -1765,12 +1765,12 @@ "10011": "Only LLM-type models are supported. {{model_type}} is not supported", "10012": "Model {{model_name}} under {{server_name}} is offline. Contact admin", "10013": "Failed to initialize LLM. Check settings or contact admin. Error: {{exception}}", - "10014": "ASR configuration not found", - "10015": "ASR configuration deleted. Please reconfigure", - "10016": "Provider settings removed. Please reconfigure ASR", - "10017": "Only ASR-type models are supported. {{model_type}} is not supported", - "10018": "ASR model {{model_name}} under {{server_name}} is offline", - "10019": "Failed to initialize ASR: {{exception}}", + "10014": "Knowledge base speech-to-text (ASR) model is not configured. Configure it under System Model Settings → Knowledge Base Model before uploading audio or video.", + "10015": "Knowledge base ASR model configuration has been deleted. Reconfigure it under System Model Settings → Knowledge Base Model.", + "10016": "Knowledge base ASR provider has been deleted. Reconfigure it under System Model Settings → Knowledge Base Model.", + "10017": "Invalid knowledge base ASR model type. Select an ASR model under System Model Settings → Knowledge Base Model (current: {{model_type}}).", + "10018": "Knowledge base ASR model \"{{server_name}}/{{model_name}}\" is offline. Choose an available model under System Model Settings → Knowledge Base Model.", + "10019": "Failed to initialize knowledge base ASR. Check the configuration under System Model Settings → Knowledge Base Model. Error: {{exception}}", "10020": "TTS configuration not found", "10021": "TTS configuration deleted. Please reconfigure", "10022": "Provider settings removed. Please reconfigure TTS", diff --git a/src/frontend/platform/public/locales/ja/bs.json b/src/frontend/platform/public/locales/ja/bs.json index 1e880a3d0..167a14ef1 100644 --- a/src/frontend/platform/public/locales/ja/bs.json +++ b/src/frontend/platform/public/locales/ja/bs.json @@ -1710,12 +1710,12 @@ "10011": "LLMタイプのみサポートしています。{{model_type}} は非対応です", "10012": "{{server_name}} の {{model_name}} はオフラインです", "10013": "LLMの初期化に失敗しました:{{exception}}", - "10014": "ASR設定が見つかりません", - "10015": "ASR設定が削除されました", - "10016": "提供元設定が削除されました。ASRを再設定してください", - "10017": "ASRタイプのみサポートしています", - "10018": "ASRモデル {{model_name}} はオフラインです", - "10019": "ASR初期化に失敗しました:{{exception}}", + "10014": "ナレッジベースの音声文字起こし(ASR)モデルが未設定です。「システムモデル設定 → ナレッジベースモデル」で設定してから音声・動画をアップロードしてください。", + "10015": "ナレッジベース ASR モデル設定が削除されました。「システムモデル設定 → ナレッジベースモデル」で再設定してください。", + "10016": "ナレッジベース ASR プロバイダーが削除されました。「システムモデル設定 → ナレッジベースモデル」で再設定してください。", + "10017": "ナレッジベース ASR モデルタイプが不正です。「システムモデル設定 → ナレッジベースモデル」で ASR タイプのモデルを選択してください(現在:{{model_type}})。", + "10018": "ナレッジベース ASR モデル「{{server_name}}/{{model_name}}」がオフラインです。「システムモデル設定 → ナレッジベースモデル」で利用可能なモデルに変更してください。", + "10019": "ナレッジベース ASR の初期化に失敗しました。「システムモデル設定 → ナレッジベースモデル」の設定を確認してください。エラー:{{exception}}", "10020": "TTS設定が見つかりません", "10021": "TTS設定が削除されました", "10022": "提供元設定が削除されました。TTSを再設定してください", diff --git a/src/frontend/platform/public/locales/zh-Hans/bs.json b/src/frontend/platform/public/locales/zh-Hans/bs.json index 274eb8a29..a1254eb9b 100644 --- a/src/frontend/platform/public/locales/zh-Hans/bs.json +++ b/src/frontend/platform/public/locales/zh-Hans/bs.json @@ -1710,12 +1710,12 @@ "10011": "只支持LLM类型的模型,不支持{{model_type}}类型的模型", "10012": "{{server_name}}下的{{model_name}}模型已下线,请联系管理员上线对应的模型", "10013": "初始化llm失败,请检查配置或联系管理员。错误信息:{{exception}}", - "10014": "没有找到asr模型配置", - "10015": "asr模型配置已被删除,请重新配置模型", - "10016": "服务提供方配置已被删除,请重新配置asr模型", - "10017": "只支持ASR类型的模型,不支持{{model_type}}类型的模型", - "10018": "{{server_name}}下的{{model_name}}模型已下线,请联系管理员上线对应的模型", - "10019": "初始化asr失败,请检查配置或联系管理员。错误信息:{{exception}}", + "10014": "未配置知识库语音转文字(ASR)模型,请在「系统模型设置 → 知识库模型」中配置后再上传音视频。", + "10015": "知识库 ASR 模型配置已被删除,请在「系统模型设置 → 知识库模型」中重新配置。", + "10016": "知识库 ASR 服务提供方已被删除,请在「系统模型设置 → 知识库模型」中重新配置。", + "10017": "知识库 ASR 模型类型不正确,请在「系统模型设置 → 知识库模型」中选择 ASR 类型模型(当前:{{model_type}})。", + "10018": "知识库 ASR 模型「{{server_name}}/{{model_name}}」已下线,请在「系统模型设置 → 知识库模型」中更换可用模型。", + "10019": "知识库 ASR 初始化失败,请检查「系统模型设置 → 知识库模型」中的配置。错误信息:{{exception}}", "10020": "没有找到tts模型配置", "10021": "tts模型配置已被删除,请重新配置模型", "10022": "服务提供方配置已被删除,请重新配置tts模型", diff --git a/src/frontend/platform/src/controllers/API/index.ts b/src/frontend/platform/src/controllers/API/index.ts index 8db50c4bc..821ee0e58 100644 --- a/src/frontend/platform/src/controllers/API/index.ts +++ b/src/frontend/platform/src/controllers/API/index.ts @@ -541,7 +541,9 @@ export function previewFileSplitApi( const emitError = (payload) => { onEvent('error', { code: payload?.code || 10952, - message: payload?.message || '文档解析失败' + message: payload?.message || '文档解析失败', + data: payload?.data, + ...payload, }); }; diff --git a/src/frontend/platform/src/pages/KnowledgePage/components/FileUploadStep4.tsx b/src/frontend/platform/src/pages/KnowledgePage/components/FileUploadStep4.tsx index 5300f5838..eb72759c7 100644 --- a/src/frontend/platform/src/pages/KnowledgePage/components/FileUploadStep4.tsx +++ b/src/frontend/platform/src/pages/KnowledgePage/components/FileUploadStep4.tsx @@ -11,29 +11,17 @@ import { useToast } from "@/components/bs-ui/toast/use-toast"; import { useEffect, useRef, useState } from "react"; import { useTranslation } from "react-i18next"; import { useNavigate, useParams } from "react-router-dom"; +import { resolveKnowledgeParseFailure } from "../knowledgeParseFailureMessage"; type TranslateFn = (key: string, options?: Record) => string; function resolveParseFailureReason(remark: unknown, t: TranslateFn) { + const resolved = resolveKnowledgeParseFailure(remark, t); + if (resolved) return resolved; + const trimmedRemark = typeof remark === "string" ? remark.trim() : ""; if (!trimmedRemark) return t("parseFailed"); - if (!trimmedRemark.startsWith("{")) return trimmedRemark; - - try { - const parsed = JSON.parse(trimmedRemark); - if (parsed?.status_code === 10956) { - return t("mediaNoRecognizableAudio"); - } - - const exception = String(parsed?.data?.exception ?? "").trim().toLowerCase(); - if (exception === "media audio extraction failed" || exception === "asr returned empty text") { - return t("mediaNoRecognizableAudio"); - } - - return t(`bs:errors.${parsed.status_code}`, parsed.data); - } catch { - return trimmedRemark; - } + return trimmedRemark; } export default function FileUploadStep4({ data, kId, hasRepeat }) { diff --git a/src/frontend/platform/src/pages/KnowledgePage/components/Files.tsx b/src/frontend/platform/src/pages/KnowledgePage/components/Files.tsx index 034f7133e..80d65d44d 100644 --- a/src/frontend/platform/src/pages/KnowledgePage/components/Files.tsx +++ b/src/frontend/platform/src/pages/KnowledgePage/components/Files.tsx @@ -32,6 +32,7 @@ import AddKnowledgeFileMenu from "./AddKnowledgeFileMenu"; import WebLinkImportDialog from "./WebLinkImportDialog"; import FileTagList from "./tags/FileTagList"; import KnowledgeTagSelect from "./tags/KnowledgeTagSelect"; +import { resolveKnowledgeParseFailure } from "../knowledgeParseFailureMessage"; interface StatusIndicatorProps { status: number; @@ -73,16 +74,7 @@ function formatSensitiveViolationMessage(hits: any[], t: (key: string, options?: } function formatMediaParseFailureMessage(obj: any, t: (key: string, options?: Record) => string) { - if (obj?.status_code === 10956) { - return t("mediaNoRecognizableAudio", { ns: "knowledge" }); - } - - const exception = String(obj?.data?.exception ?? "").trim().toLowerCase(); - if (exception === "media audio extraction failed" || exception === "asr returned empty text") { - return t("mediaNoRecognizableAudio", { ns: "knowledge" }); - } - - return ""; + return resolveKnowledgeParseFailure(obj, t) ?? ""; } export const StatusIndicator: React.FC = ({ status, remark }) => { diff --git a/src/frontend/platform/src/pages/KnowledgePage/components/PreviewFile.tsx b/src/frontend/platform/src/pages/KnowledgePage/components/PreviewFile.tsx index 789609c20..4ae360c2d 100644 --- a/src/frontend/platform/src/pages/KnowledgePage/components/PreviewFile.tsx +++ b/src/frontend/platform/src/pages/KnowledgePage/components/PreviewFile.tsx @@ -24,7 +24,8 @@ export default function PreviewFile({ resultFiles, etl, previewUrl, - previewData + previewData, + previewErrorMessage, }: { urlState: { load: boolean; url: string }; file: any; @@ -34,6 +35,7 @@ export default function PreviewFile({ setChunks: any; edit?: boolean; previewData?: any; + previewErrorMessage?: string; }) { const { t } = useTranslation('knowledge') const MemoizedFileView = React.memo(FileView); @@ -226,7 +228,16 @@ export default function PreviewFile({ const { url, load } = urlState; // 加载状态处理 - if (!load && !url) return
预览失败
; + if (!load && !url) { + if (previewErrorMessage) { + return ( +
+

{previewErrorMessage}

+
+ ); + } + return
{t('previewFailed')}
; + } if (!url) return
; if (isRichKnowledgePreview(previewData)) { return ; diff --git a/src/frontend/platform/src/pages/KnowledgePage/components/PreviewResult.tsx b/src/frontend/platform/src/pages/KnowledgePage/components/PreviewResult.tsx index 0764e87d0..0012d4d7a 100644 --- a/src/frontend/platform/src/pages/KnowledgePage/components/PreviewResult.tsx +++ b/src/frontend/platform/src/pages/KnowledgePage/components/PreviewResult.tsx @@ -11,10 +11,12 @@ import { captureAndAlertRequestErrorHoc } from "@/controllers/request"; import { cn } from "@/util/utils"; import { useEffect, useMemo, useRef, useState } from "react"; import { useParams } from "react-router-dom"; +import { useTranslation } from "react-i18next"; import useKnowledgeStore from "../useKnowledgeStore"; import PreviewFile from "./PreviewFile"; import PreviewParagraph from "./PreviewParagraph"; import { buildMediaUploadPreviewData, isMediaUploadSuffix, type RichPreviewData } from "./RichPreviewFile"; +import { resolveKnowledgeParseFailure } from "../knowledgeParseFailureMessage"; interface IProps { rules: any; @@ -35,6 +37,7 @@ export default function PreviewResult({ showPreview, previewCount, rules, resultFiles, step, applyEachCell, cellGeneralConfig, kId, handlePreviewResult, onDeleteFile }: IProps) { const { id } = useParams() + const { t } = useTranslation(['knowledge', 'bs']); const [chunks, setChunks] = useState([]) // 当前文件分块 const [partitions, setPartitions] = useState(null) // 当前文件分区 @@ -42,6 +45,7 @@ export default function PreviewResult({ const [syncChunksSelectId, setSelectIdSyncChunks] = useState(''); // 当前选择文件id(与chunk更新保持同步) const [etl, setEtl] = useState('') const [mediaPreviewData, setMediaPreviewData] = useState(null) + const [previewErrorMessage, setPreviewErrorMessage] = useState('') useEffect(() => { const file = rules.fileList[0] setSelectId(file.id) @@ -77,6 +81,7 @@ export default function PreviewResult({ setFileViewUrl({ load: true, url: '' }); setChunks([]); setMediaPreviewData(null); + setPreviewErrorMessage(''); // 合并配置(与原逻辑一致) const { fileList, pageHeaderFooter, chunkOverlap, chunkSize, enableFormula, forceOcr, knowledgeId, retainImages, separator, separatorRule, splitMode, hierarchyLevel, appendTitle, maxChunkSize } = rules; @@ -122,6 +127,7 @@ export default function PreviewResult({ break; case 'completed': setEtl(data.parse_type) + setPreviewErrorMessage(''); // 记录该文件已在当前 previewCount 下解析过,下次切回时走缓存 prevPreviewCountMapRef.current[currentFile.id] = previewCount; // 解析完成:处理结果(对应原 .then(res) 逻辑) @@ -155,6 +161,9 @@ export default function PreviewResult({ handlePreviewResult(false); setMediaPreviewData(null); setFileViewUrl({ load: false, url: '' }); + setPreviewErrorMessage( + resolveKnowledgeParseFailure(data, t) ?? t('previewFailed', { ns: 'knowledge' }), + ); setLoading(false); // 原错误处理逻辑:支持的文件类型显示原文件预览 if (["pdf", "txt", "md", "html", "docx", "png", "jpg", "jpeg", "bmp"].includes(currentFile.suffix)) { @@ -223,6 +232,7 @@ export default function PreviewResult({ setChunks={setChunks} partitions={partitions} previewData={mediaPreviewData ?? undefined} + previewErrorMessage={previewErrorMessage} />}
{/* 下拉框 - 右上角 */} diff --git a/src/frontend/platform/src/pages/KnowledgePage/knowledgeParseFailureMessage.ts b/src/frontend/platform/src/pages/KnowledgePage/knowledgeParseFailureMessage.ts new file mode 100644 index 000000000..a9b2253a7 --- /dev/null +++ b/src/frontend/platform/src/pages/KnowledgePage/knowledgeParseFailureMessage.ts @@ -0,0 +1,121 @@ +type TranslateFn = (key: string, options?: Record) => string; + +const ASR_ERROR_CODES = new Set([10014, 10015, 10016, 10017, 10018, 10019]); + +function getErrorData(input: Record): Record { + const nested = input.data; + if (nested && typeof nested === "object" && !Array.isArray(nested)) { + return { ...(nested as Record) }; + } + return { + exception: input.exception, + model_type: input.model_type, + server_name: input.server_name, + model_name: input.model_name, + }; +} + +function normalizeInput(input: unknown): Record | null { + if (!input) return null; + if (typeof input === "string") { + const trimmed = input.trim(); + if (!trimmed) return null; + if (!trimmed.startsWith("{")) { + return { message: trimmed }; + } + try { + return JSON.parse(trimmed) as Record; + } catch { + return { message: trimmed }; + } + } + if (typeof input === "object") { + return input as Record; + } + return null; +} + +function resolve10954Message( + parsed: Record, + errorData: Record, + t: TranslateFn, +): string { + const statusMessage = String( + parsed.status_message ?? parsed.message ?? errorData.exception ?? "", + ).toLowerCase(); + + if (statusMessage.includes("api key is missing")) { + return t("mediaAsrApiKeyMissing", { ns: "knowledge" }); + } + if ( + statusMessage.includes("only supports aliyun/qwen") + || statusMessage.includes("knowledge media transcription only supports") + ) { + return t("mediaAsrProviderUnsupported", { ns: "knowledge" }); + } + if (statusMessage.includes("asr request failed")) { + return t("mediaAsrRequestFailed", { ns: "knowledge" }); + } + return t("bs:errors.10954", errorData); +} + +/** Resolve knowledge file parse failure messages, including knowledge-base ASR errors. */ +export function resolveKnowledgeParseFailure(input: unknown, t: TranslateFn): string | undefined { + const parsed = normalizeInput(input); + if (!parsed) return undefined; + + if (typeof parsed.message === "string" && !parsed.status_code && !parsed.code) { + const known = resolveKnownExceptionMessage(parsed.message, t); + if (known) return known; + } + + const statusCode = Number(parsed.status_code ?? parsed.code); + if (!Number.isFinite(statusCode)) { + return typeof parsed.message === "string" ? parsed.message : undefined; + } + + const errorData = getErrorData(parsed); + + if (statusCode === 10956) { + return t("mediaNoRecognizableAudio", { ns: "knowledge" }); + } + + const exception = String(errorData.exception ?? "").trim().toLowerCase(); + if (exception === "media audio extraction failed" || exception === "asr returned empty text") { + return t("mediaNoRecognizableAudio", { ns: "knowledge" }); + } + + if (ASR_ERROR_CODES.has(statusCode)) { + return t(`bs:errors.${statusCode}`, errorData); + } + + if (statusCode === 10954) { + return resolve10954Message(parsed, errorData, t); + } + + const translated = t(`bs:errors.${statusCode}`, errorData); + if (translated && translated !== `bs:errors.${statusCode}`) { + return translated; + } + + return undefined; +} + +function resolveKnownExceptionMessage(message: string, t: TranslateFn): string | undefined { + const normalized = message.trim().toLowerCase(); + if (!normalized) return undefined; + + if (normalized.includes("api key is missing")) { + return t("mediaAsrApiKeyMissing", { ns: "knowledge" }); + } + if (normalized.includes("knowledge media transcription only supports")) { + return t("mediaAsrProviderUnsupported", { ns: "knowledge" }); + } + if (normalized.includes("asr request failed")) { + return t("mediaAsrRequestFailed", { ns: "knowledge" }); + } + if (normalized === "media audio extraction failed" || normalized === "asr returned empty text") { + return t("mediaNoRecognizableAudio", { ns: "knowledge" }); + } + return undefined; +}