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;