初始化:多比比Android客户端(占坑版)
This commit is contained in:
@@ -0,0 +1,73 @@
|
||||
"""POST /api/v1/parse-image —— iOS 端的截图识别接口。
|
||||
|
||||
multipart/form-data:
|
||||
- image: file (JPEG/PNG/HEIC,客户端已压到 < 5MB)
|
||||
- clusters: str (JSON 序列化的 [{"id":"<uuid>","title":"..."}, ...])
|
||||
- package_name: str (可选,iOS 通常不传或传 null;Mock 实现不使用)
|
||||
|
||||
占坑期实现接 mock_extractor;后续接真模型时只改这里调 extractor_vision。
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import logging
|
||||
from typing import Optional
|
||||
|
||||
from fastapi import APIRouter, File, Form, HTTPException, UploadFile
|
||||
from pydantic import ValidationError
|
||||
|
||||
from app.mock_extractor import mock_extract_image
|
||||
from app.schemas import ClusterDtoStr, ParseStrResponse
|
||||
|
||||
logger = logging.getLogger("shagua.parse_image")
|
||||
|
||||
router = APIRouter(prefix="/api/v1")
|
||||
|
||||
# 单张图片上限 5MB。客户端正常压完短边 1024 的 JPEG 通常 < 500KB,
|
||||
# 超过 5MB 是异常情况(原图 / 客户端压缩 bug),直接拒掉省 LLM 钱。
|
||||
MAX_IMAGE_BYTES = 5 * 1024 * 1024
|
||||
|
||||
|
||||
@router.post("/parse-image", response_model=ParseStrResponse)
|
||||
async def parse_image(
|
||||
image: UploadFile = File(...),
|
||||
clusters: str = Form("[]"),
|
||||
package_name: Optional[str] = Form(None),
|
||||
) -> ParseStrResponse:
|
||||
image_bytes = await image.read()
|
||||
if not image_bytes:
|
||||
logger.info("parse_image rejected: no_image bytes=0")
|
||||
raise HTTPException(status_code=422, detail="no_image")
|
||||
if len(image_bytes) > MAX_IMAGE_BYTES:
|
||||
logger.info("parse_image rejected: image_too_large bytes=%d", len(image_bytes))
|
||||
raise HTTPException(status_code=422, detail="image_too_large")
|
||||
|
||||
try:
|
||||
clusters_raw = json.loads(clusters) if clusters else []
|
||||
if not isinstance(clusters_raw, list):
|
||||
raise ValueError("clusters must be a JSON array")
|
||||
cluster_dtos = [ClusterDtoStr(**c) for c in clusters_raw]
|
||||
except (json.JSONDecodeError, ValueError, TypeError, ValidationError) as e:
|
||||
# ValidationError 不是 ValueError 子类(pydantic v2),必须显式 catch,
|
||||
# 否则客户端发字段不全的 cluster 会让接口冒 500
|
||||
logger.info("parse_image rejected: invalid_clusters err=%s", e)
|
||||
raise HTTPException(status_code=422, detail="invalid_clusters")
|
||||
|
||||
logger.info(
|
||||
"parse_image request bytes=%d package=%s clusters=%d",
|
||||
len(image_bytes),
|
||||
package_name,
|
||||
len(cluster_dtos),
|
||||
)
|
||||
|
||||
result = await mock_extract_image(image_bytes, cluster_dtos)
|
||||
if not result.get("success"):
|
||||
raise HTTPException(status_code=422, detail=result.get("reason", "extract_failed"))
|
||||
|
||||
return ParseStrResponse(
|
||||
title=result["title"],
|
||||
price=result["price"],
|
||||
source_app=result["source_app"],
|
||||
cluster_id=result.get("cluster_id"),
|
||||
typical_price=result["typical_price"],
|
||||
)
|
||||
Reference in New Issue
Block a user