fix(wechat/token/market): 修复微信对话时间、Token数据重置、技能市场复活、媒体内联

1. 微信Bot对话记录时间按北京时间渲染(ImBotHub/WechatHistory 的 formatTime 显式 +08:00 + Asia/Shanghai);
2. 新增 /app/api/chat/media/relay 媒体中转端点(白名单 agnES 域名, 经香港代理代取),
   改写 _wechat_send_media 用平台中转地址,微信/iLink 可内联播放图片/视频;
3. 删除 014_clean_test_data.sql:该迁移每次重启重跑并 DELETE token_usage_logs/
   messages/conversations/knowledge 等,导致 Token 驾驶舱数据每次重启被清空;
4. 002_init_data.sql 移除 market_apps 自动播种(原 WHERE COUNT=0 在用户删空后重启复活),
   技能市场交由用户后台维护,重启不再复活。
This commit is contained in:
QihuAI Design committed 2026-07-16 09:19:23 +08:00
1 parent da89a27bca
commit 82afe14a23
6 files changed
+85 -108

No files matched your search

+35 -1
View File
@@ -3,7 +3,7 @@
包含: Skill ID统一(MarketApp映射), 真实Skill执行, Agent工作流注入,
多轮上下文保持, BM25 KB检索, Function Calling循环增强
"""
from fastapi import APIRouter, Depends, HTTPException, Query
from fastapi import APIRouter, Depends, HTTPException, Query, Response
from sqlalchemy.orm import Session
from sqlalchemy import and_, desc, asc, func
from pydantic import BaseModel, Field
@@ -173,6 +173,40 @@ def _proxy_for_url(url: str) -> str | None:
return _outbound_proxy()
# ============================================================
# 媒体中转(relay)端点:让微信/iLink 能内联渲染 AGNES 生成的图片/视频
# 背景:AGNES 媒体 URL 位于境外(agnes-ai.com / agnes-ai.space),
# iLink 服务器(中国大陆)直连常被墙/超时,导致微信里只看到"链接"无法内联播放。
# 此端点由平台服务器(经香港代理可达 AGNES)代取媒体并以平台域名回传,
# 微信即可内联显示。仅允许白名单域名,杜绝 SSRF。
# ============================================================
_MEDIA_RELAY_ALLOWED = ("agnes-ai.com", "agnes-ai.space", "qihuyun.com")
@router.get("/media/relay")
async def media_relay(url: str = Query(...)):
"""中转 AGNES 媒体(图片/视频)到平台域名,供微信/iLink 内联渲染。
仅允许白名单域名;否则 400。返回的媒体流保持原 content-type。
"""
import urllib.parse as _up
host = (_up.urlparse(url).hostname or "").lower()
if not any(allowed in host for allowed in _MEDIA_RELAY_ALLOWED):
raise HTTPException(status_code=400, detail="unsupported media host")
try:
async with httpx.AsyncClient(timeout=180, proxy=_proxy_for_url(url), trust_env=False) as client:
r = await client.get(url)
if r.status_code != 200:
raise HTTPException(status_code=r.status_code, detail="upstream error")
content_type = r.headers.get("content-type", "application/octet-stream")
data = await r.aread()
return Response(content=data, media_type=content_type)
except HTTPException:
raise
except Exception as e:
raise HTTPException(status_code=502, detail=f"relay failed: {e}")
async def _optimize_media_prompt(raw_prompt: str, media_type: str, db=None) -> str:
"""使用平台文本模型对用户原始输入做「媒体提示词增强优化」。