From 3f8dc842419670b54c2e44730e9559f7999add41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=B9=8F=E5=AE=87?= Date: Fri, 12 Jun 2026 16:39:20 +0800 Subject: [PATCH] =?UTF-8?q?=E5=9B=BE=E7=89=87=E8=AF=86=E5=88=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- yuthon_ai_agent/controllers/main.py | 6 ++--- yuthon_ai_agent/models/ocr_provider.py | 25 ++++++++++++------- .../static/src/js/ai_chat_action.js | 12 +-------- yuthon_ai_agent/static/src/js/ai_floating.js | 17 +------------ 4 files changed, 21 insertions(+), 39 deletions(-) diff --git a/yuthon_ai_agent/controllers/main.py b/yuthon_ai_agent/controllers/main.py index 237717d71..18fb9fbb9 100644 --- a/yuthon_ai_agent/controllers/main.py +++ b/yuthon_ai_agent/controllers/main.py @@ -249,10 +249,10 @@ class AiChatController(http.Controller): ocr_error = '' if provider and provider.ocr_enabled: from ..models.ocr_provider import get_ocr_instance - ocr = get_ocr_instance(provider) + ocr, init_err = get_ocr_instance(provider) if ocr is None: - ocr_error = 'OCR 实例创建失败,请检查 SecretId/SecretKey 是否完整' - _logger.error('[AI-OCR] get_ocr_instance 返回 None') + ocr_error = init_err or 'OCR 实例创建失败' + _logger.error('[AI-OCR] get_ocr_instance 失败: %s', ocr_error) else: result = ocr.smart_recognize(raw_bytes, mimetype) if result.get('success'): diff --git a/yuthon_ai_agent/models/ocr_provider.py b/yuthon_ai_agent/models/ocr_provider.py index c7df34e47..7ecb3f5d4 100644 --- a/yuthon_ai_agent/models/ocr_provider.py +++ b/yuthon_ai_agent/models/ocr_provider.py @@ -146,21 +146,28 @@ def get_ocr_instance(provider): """从 AiProvider 记录中创建 OCR 实例。 :param provider: ai.provider 记录 - :return: TencentOCR 实例或 None + :return: (TencentOCR 实例 | None, str 错误原因) """ if not provider.ocr_enabled: - return None - if not provider.ocr_secret_id or not provider.ocr_secret_key: - _logger.warning('OCR 已启用但 SecretId/SecretKey 未配置 (provider=%s)', provider.name) - return None + return None, 'OCR 未启用' + + # 逐项校验,给出精确的错误信息 + if not provider.ocr_secret_id: + return None, '未配置 SecretId(请到服务商配置 → OCR配置 填写)' + if not provider.ocr_secret_key: + return None, '未配置 SecretKey(请到服务商配置 → OCR配置 填写)' if not HAS_TENCENT_SDK: - return None + return None, 'tencentcloud-sdk-python 未安装(pip install tencentcloud-sdk-python)' + try: return TencentOCR( secret_id=provider.ocr_secret_id, secret_key=provider.ocr_secret_key, region=provider.ocr_region or 'ap-guangzhou', - ) + ), '' + except ValueError as ve: + _logger.warning('[AI-OCR] 参数错误: %s', ve) + return None, str(ve) except Exception as e: - _logger.exception('创建 OCR 实例失败') - return None + _logger.exception('[AI-OCR] 创建 OCR 实例异常') + return None, f'初始化异常: {e}' diff --git a/yuthon_ai_agent/static/src/js/ai_chat_action.js b/yuthon_ai_agent/static/src/js/ai_chat_action.js index 5e836be38..c0f784c49 100644 --- a/yuthon_ai_agent/static/src/js/ai_chat_action.js +++ b/yuthon_ai_agent/static/src/js/ai_chat_action.js @@ -35,17 +35,7 @@ export class AiChatAction extends Component { .replace(/【附件:[^】]+】[\s\S]*?--- 附件内容结束 ---/g, '') .replace(/\[附件[::]\s*[^\]]+\]\n?/g, '') .trim() || safe; - // URL → 精美卡片(还原 escapeHtml 把 & 变回 &,保证 href 正确) - display = display.replace( - /(https?:\/\/[^\s<>"'\u4e00-\u9fff\u3000-\u303f\uff00-\uffef]+)/g, - (url) => { - const rawUrl = url.replace(/&/g, '&'); - let domain = ''; - try { domain = new URL(rawUrl).hostname; } catch (_) { domain = rawUrl.substring(0, 50); } - const displayUrl = rawUrl.length > 60 ? rawUrl.substring(0, 57) + '...' : rawUrl; - return `${this._escapeHtml(domain)}${this._escapeHtml(displayUrl)}`; - } - ); + // 不做额外处理,URL 保持纯文本显示 return display.replace(/\n/g, '
'); } diff --git a/yuthon_ai_agent/static/src/js/ai_floating.js b/yuthon_ai_agent/static/src/js/ai_floating.js index 1af429ac3..e0f36358b 100644 --- a/yuthon_ai_agent/static/src/js/ai_floating.js +++ b/yuthon_ai_agent/static/src/js/ai_floating.js @@ -235,22 +235,7 @@ function renderContent(text) { .replace(/【附件:[^】]+】[\s\S]*?--- 附件内容结束 ---/g, '') .replace(/\[附件[::]\s*[^\]]+\]\n?/g, '') .trim() || safe; // 若 strip 后为空,回退到 safe 继续处理 URL - // URL 渲染为精美卡片(先还原 escapeHtml 把 & 变回 &,保证 href 正确) - display = display.replace( - /(https?:\/\/[^\s<>"'\u4e00-\u9fff\u3000-\u303f\uff00-\uffef]+)/g, - (url) => { - // 还原 HTML 实体(escapeHtml 会把 & 变成 &,URL 参数中的 & 需要还原) - const rawUrl = url.replace(/&/g, '&'); - let domain = ''; - try { - domain = new URL(rawUrl).hostname; - } catch (_) { - domain = rawUrl.substring(0, 50); - } - const displayUrl = rawUrl.length > 60 ? rawUrl.substring(0, 57) + '...' : rawUrl; - return `${escapeHtml(domain)}${escapeHtml(displayUrl)}`; - } - ); + // 不做额外处理,URL 保持纯文本显示 // 换行转
display = display.replace(/\n/g, '
'); return display;