图片上传识别功能
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user