图片上传识别功能

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