Merge branch 'feat/2.6.0' into release

This commit is contained in:
GuoQing Zhang
2026-07-09 14:26:19 +08:00
16 changed files with 304 additions and 79 deletions
@@ -95,11 +95,11 @@ from bisheng.knowledge.domain.services.knowledge_service import KnowledgeService
from bisheng.knowledge.domain.services.knowledge_space_tag_library_service import (
KnowledgeSpaceTagLibraryService,
)
from bisheng.knowledge.domain.services.knowledge_utils import KnowledgeUtils
from bisheng.knowledge.domain.services.web_link_import_service import (
KnowledgeWebLinkImportService,
is_web_link_non_dedup_markdown,
)
from bisheng.knowledge.domain.services.knowledge_utils import KnowledgeUtils
from bisheng.llm.domain import LLMService
from bisheng.message.domain.services.notification_content import build_notify_content
from bisheng.permission.domain.knowledge_space_permission_template import (
@@ -3270,7 +3270,9 @@ class KnowledgeSpaceService(KnowledgeUtils):
user_id=db_file.user_id,
file_id=db_file.id,
file_name=db_file.file_name,
preview_cache_key=self.get_preview_cache_key(knowledge_id, import_result.final_url, import_result.content_hash),
preview_cache_key=self.get_preview_cache_key(
knowledge_id, import_result.final_url, import_result.content_hash
),
callback_url=None,
)
await self.update_folder_update_time(file_level_path)
@@ -3335,7 +3337,9 @@ class KnowledgeSpaceService(KnowledgeUtils):
user_id=updated_file.user_id,
file_id=updated_file.id,
file_name=updated_file.file_name,
preview_cache_key=self.get_preview_cache_key(updated_file.knowledge_id, user_metadata.get("final_url", ""), content_hash),
preview_cache_key=self.get_preview_cache_key(
updated_file.knowledge_id, user_metadata.get("final_url", ""), content_hash
),
callback_url=None,
)
await self.update_folder_update_time(file_level_path)
+2 -10
View File
@@ -1,4 +1,5 @@
import request from "./request";
import { resolveKnowledgeParseFailureMessage } from "./knowledgeParseFailureMessage";
// Standard backend response wrapper
interface ApiResponse<T> {
@@ -463,16 +464,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 {
@@ -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<number, string> = {
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, unknown>): 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, unknown>): 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<string, unknown>, errorData: Record<string, unknown>): 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, unknown>): 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<string, unknown>;
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;
}
@@ -1274,12 +1274,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",
@@ -1198,12 +1198,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を再設定してください",
@@ -1201,12 +1201,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模型",
@@ -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",
@@ -1767,12 +1767,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",
@@ -1712,12 +1712,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を再設定してください",
@@ -1712,12 +1712,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模型",
@@ -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,
});
};
@@ -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, unknown>) => 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 }) {
@@ -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, any>) => 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<StatusIndicatorProps> = ({ status, remark }) => {
@@ -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 <div className="flex justify-center items-center h-full text-gray-400"></div>;
if (!load && !url) {
if (previewErrorMessage) {
return (
<div className="flex justify-center items-center h-full px-6 text-center text-gray-500">
<p className="text-sm leading-6 whitespace-pre-line">{previewErrorMessage}</p>
</div>
);
}
return <div className="flex justify-center items-center h-full text-gray-400">{t('previewFailed')}</div>;
}
if (!url) return <div className="flex justify-center items-center h-full text-gray-400"><LoadingIcon /></div>;
if (isRichKnowledgePreview(previewData)) {
return <RichPreviewFile file={file} previewData={previewData} />;
@@ -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<Partition>(null) // 当前文件分区
@@ -42,6 +45,7 @@ export default function PreviewResult({
const [syncChunksSelectId, setSelectIdSyncChunks] = useState(''); // 当前选择文件id(与chunk更新保持同步)
const [etl, setEtl] = useState<string>('')
const [mediaPreviewData, setMediaPreviewData] = useState<RichPreviewData | null>(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}
/>}
<div className={cn('relative', "w-full")}>
{/* 下拉框 - 右上角 */}
@@ -0,0 +1,121 @@
type TranslateFn = (key: string, options?: Record<string, unknown>) => string;
const ASR_ERROR_CODES = new Set([10014, 10015, 10016, 10017, 10018, 10019]);
function getErrorData(input: Record<string, unknown>): Record<string, unknown> {
const nested = input.data;
if (nested && typeof nested === "object" && !Array.isArray(nested)) {
return { ...(nested as Record<string, unknown>) };
}
return {
exception: input.exception,
model_type: input.model_type,
server_name: input.server_name,
model_name: input.model_name,
};
}
function normalizeInput(input: unknown): Record<string, unknown> | 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<string, unknown>;
} catch {
return { message: trimmed };
}
}
if (typeof input === "object") {
return input as Record<string, unknown>;
}
return null;
}
function resolve10954Message(
parsed: Record<string, unknown>,
errorData: Record<string, unknown>,
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;
}