图片上传识别功能

This commit is contained in:
renjianbo
2026-04-13 22:52:36 +08:00
parent df4fab1e6e
commit 63b54116a5
13 changed files with 708 additions and 17 deletions

View File

@@ -76,6 +76,7 @@ class PreviewChatTurnResponse(BaseModel):
created_at: datetime
user_text: str
agent_text: str
attachments: List[Dict[str, Any]] = Field(default_factory=list)
class Config:
from_attributes = True

View File

@@ -6,15 +6,17 @@ from __future__ import annotations
import re
import uuid
import logging
import mimetypes
from pathlib import Path
from fastapi import APIRouter, Depends, File, HTTPException, UploadFile, status
from fastapi import APIRouter, Depends, File, HTTPException, UploadFile, status, Query
from fastapi.responses import FileResponse
from pydantic import BaseModel
from app.api.auth import get_current_user
from app.core.config import settings
from app.models.user import User
from app.services.builtin_tools import _local_file_workspace_root
from app.services.builtin_tools import _local_file_workspace_root, _resolve_path_under_workspace
logger = logging.getLogger(__name__)
@@ -110,3 +112,32 @@ async def upload_preview_file(
size=total,
content_type=file.content_type,
)
@router.get("/preview/file")
async def get_preview_file(
file_path: str = Query(..., description="上传后返回的 relative_path"),
current_user: User = Depends(get_current_user),
):
"""
读取当前用户预览附件(用于前端历史回显图片缩略图)。
仅允许访问 uploads/preview/<current_user.id>/ 下文件。
"""
path, err = _resolve_path_under_workspace(file_path)
if err or path is None:
raise HTTPException(status_code=400, detail=f"无效文件路径: {err or file_path}")
if not path.is_file():
raise HTTPException(status_code=404, detail="文件不存在")
root = _local_file_workspace_root()
try:
rel = path.relative_to(root).as_posix()
except ValueError:
raise HTTPException(status_code=403, detail="不允许访问该文件")
prefix = f"uploads/preview/{current_user.id}/"
if not rel.startswith(prefix):
raise HTTPException(status_code=403, detail="无权访问该文件")
media_type, _ = mimetypes.guess_type(str(path))
return FileResponse(path=str(path), media_type=media_type or "application/octet-stream")

View File

@@ -97,6 +97,31 @@ def _user_id_from_input(input_data: Optional[Dict[str, Any]]) -> Optional[str]:
return None
def _extract_attachments(input_data: Optional[Dict[str, Any]]) -> List[Dict[str, Any]]:
if not input_data:
return []
raw = input_data.get("attachments")
if not isinstance(raw, list):
return []
out: List[Dict[str, Any]] = []
for item in raw:
if not isinstance(item, dict):
continue
rel = str(item.get("relative_path") or "").strip()
name = str(item.get("filename") or "").strip()
if not rel:
continue
content_type = str(item.get("content_type") or "").strip() or None
out.append(
{
"relative_path": rel,
"filename": name or rel.rsplit("/", 1)[-1],
"content_type": content_type,
}
)
return out
def fetch_agent_preview_chat_turns(
db: Session,
agent_id: str,
@@ -149,6 +174,7 @@ def fetch_agent_preview_chat_turns(
"created_at": ex.created_at,
"user_text": user_text or "(无文本)",
"agent_text": agent_text or "(无输出)",
"attachments": _extract_attachments(inp),
}
)
return out

View File

@@ -0,0 +1,58 @@
"""检查图片 OCR 环境Pillow、pytesseract、Tesseract 可执行文件、chi_sim 语言包。
在 backend 目录执行:
.\\venv\\Scripts\\python scripts\\check_ocr_env.py
"""
from __future__ import annotations
import sys
from pathlib import Path
# 保证能加载 app 配置
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
from app.core.config import settings # noqa: E402
from app.services.builtin_tools import _local_file_workspace_root, _tessdata_dir_for_ocr # noqa: E402
def main() -> int:
print("TESSERACT_CMD (settings):", settings.TESSERACT_CMD or "(空,将尝试 PATH)")
print("TESSERACT_TESSDATA_DIR (settings):", settings.TESSERACT_TESSDATA_DIR or "(空,将尝试仓库 tessdata/)")
try:
import PIL # noqa: F401
print("Pillow: OK")
except ImportError as e:
print("Pillow: 缺失 —", e)
print(" 请执行: pip install Pillow")
return 2
try:
import pytesseract as pt
print("pytesseract: OK")
except ImportError as e:
print("pytesseract: 缺失 —", e)
print(" 请执行: pip install pytesseract")
return 3
cmd = (settings.TESSERACT_CMD or "").strip()
if cmd:
pt.pytesseract.tesseract_cmd = cmd
try:
ver = pt.get_tesseract_version()
print("Tesseract 版本:", ver)
except Exception as e:
print("Tesseract 可执行文件: 不可用 —", e)
print(" Windows 请安装 Tesseract并在 .env 设置 TESSERACT_CMD=.../tesseract.exe")
return 4
td = _tessdata_dir_for_ocr()
print("解析到的 tessdata 目录:", td or "(未找到)")
root = _local_file_workspace_root()
loc = root / "tessdata"
if loc.is_dir():
has_chi = any(loc.glob("chi_sim.traineddata"))
print("仓库 tessdata/chi_sim.traineddata:", "存在" if has_chi else "缺失(中文识别差)")
return 0
if __name__ == "__main__":
raise SystemExit(main())