Spaces:
Paused
Paused
| from __future__ import annotations | |
| from fastapi import APIRouter, File, Form, Header, HTTPException, Query, Request, UploadFile | |
| from fastapi.concurrency import run_in_threadpool | |
| from pydantic import BaseModel, Field | |
| from api.support import require_identity, resolve_image_base_url | |
| from services.content_filter import check_request | |
| from services.image_task_service import image_task_service | |
| from services.log_service import LoggedCall | |
| class ImageGenerationTaskRequest(BaseModel): | |
| client_task_id: str = Field(..., min_length=1) | |
| prompt: str = Field(..., min_length=1) | |
| model: str = "gpt-image-2" | |
| size: str | None = None | |
| def _parse_task_ids(value: str) -> list[str]: | |
| return [item.strip() for item in value.split(",") if item.strip()] | |
| async def filter_or_log(call: LoggedCall, text: str) -> None: | |
| try: | |
| await run_in_threadpool(check_request, text) | |
| except HTTPException as exc: | |
| call.log("调用失败", status="failed", error=str(exc.detail)) | |
| raise | |
| def create_router() -> APIRouter: | |
| router = APIRouter() | |
| async def list_image_tasks( | |
| ids: str = Query(default=""), | |
| authorization: str | None = Header(default=None), | |
| ): | |
| identity = require_identity(authorization) | |
| return await run_in_threadpool(image_task_service.list_tasks, identity, _parse_task_ids(ids)) | |
| async def create_generation_task( | |
| body: ImageGenerationTaskRequest, | |
| request: Request, | |
| authorization: str | None = Header(default=None), | |
| ): | |
| identity = require_identity(authorization) | |
| await filter_or_log(LoggedCall(identity, "/api/image-tasks/generations", body.model, "文生图任务", request_text=body.prompt), body.prompt) | |
| try: | |
| return await run_in_threadpool( | |
| image_task_service.submit_generation, | |
| identity, | |
| client_task_id=body.client_task_id, | |
| prompt=body.prompt, | |
| model=body.model, | |
| size=body.size, | |
| base_url=resolve_image_base_url(request), | |
| ) | |
| except ValueError as exc: | |
| raise HTTPException(status_code=400, detail={"error": str(exc)}) from exc | |
| async def create_edit_task( | |
| request: Request, | |
| authorization: str | None = Header(default=None), | |
| image: list[UploadFile] | None = File(default=None), | |
| image_list: list[UploadFile] | None = File(default=None, alias="image[]"), | |
| client_task_id: str = Form(...), | |
| prompt: str = Form(...), | |
| model: str = Form(default="gpt-image-2"), | |
| size: str | None = Form(default=None), | |
| ): | |
| identity = require_identity(authorization) | |
| await filter_or_log(LoggedCall(identity, "/api/image-tasks/edits", model, "图生图任务", request_text=prompt), prompt) | |
| uploads = [*(image or []), *(image_list or [])] | |
| if not uploads: | |
| raise HTTPException(status_code=400, detail={"error": "image file is required"}) | |
| images: list[tuple[bytes, str, str]] = [] | |
| for upload in uploads: | |
| image_data = await upload.read() | |
| if not image_data: | |
| raise HTTPException(status_code=400, detail={"error": "image file is empty"}) | |
| images.append((image_data, upload.filename or "image.png", upload.content_type or "image/png")) | |
| try: | |
| return await run_in_threadpool( | |
| image_task_service.submit_edit, | |
| identity, | |
| client_task_id=client_task_id, | |
| prompt=prompt, | |
| model=model, | |
| size=size, | |
| base_url=resolve_image_base_url(request), | |
| images=images, | |
| ) | |
| except ValueError as exc: | |
| raise HTTPException(status_code=400, detail={"error": str(exc)}) from exc | |
| return router | |