mirror of
https://github.com/dataelement/bisheng.git
synced 2026-09-01 04:47:53 +08:00
Merge branch 'feat/2.6.0' into release
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
Reference in New Issue
Block a user