diff --git a/.gitignore b/.gitignore index 05d5936..b330cbe 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ # Generated output output/ +test/ # Python __pycache__/ diff --git a/SKILL.md b/SKILL.md index ec00378..92518c7 100644 --- a/SKILL.md +++ b/SKILL.md @@ -1,6 +1,6 @@ --- name: ai-image -description: Generate images via AI text-to-image API (GPT Image / Grok). Use when the user asks to draw, paint, generate, or create an image from a text description. +description: Generate images via AI text-to-image API (GPT Image / Grok / Agnes Image 2.1 Flash). Use when the user asks to draw, paint, generate, or create an image from a text description, or to colorize / edit / remix existing images. --- # AI 绘图工具 @@ -15,6 +15,7 @@ description: Generate images via AI text-to-image API (GPT Image / Grok). Use wh |------|------------|------------|------| | GPT / Grok 绘图 | `main.py` | `POST /images/generations` | `gpt-image-2-1K` / `gpt-image-2-2K` / `gpt-image-2-4K` / `grok-imagine-image-lite` | | Agnes 绘图(文生图 / 图生图 / 多图合成) | `main.py` | `POST /agnes/images/generations` | `agnes-image-2.1-flash` | +| Agnes 上传版(图生图 / 多图合成,multipart) | `main.py` | `POST /agnes/images/generations/upload` | `agnes-image-2.1-flash` | | 健康检查 | `main.py` | `GET /health` | — | | 图片访问 | `main.py` | `GET /output/{profile_name}/{filename}` | — | @@ -22,10 +23,16 @@ description: Generate images via AI text-to-image API (GPT Image / Grok). Use wh | 模型 | 输出尺寸 | 备注 | |------|----------|------| -| `gpt-image-2-1K` | ~1254×1254(1:1)或 1536×1024(横版) | **推荐**,线路稳定 | +| `gpt-image-2-1K` | 不固定 | 线路不稳定,可能 503 | | `gpt-image-2-2K` | 不固定 | 线路不稳定,可能 503 | | `gpt-image-2-4K` | 不固定 | 线路不稳定,可能 503 | +```text +注意, gpt-image-2 画图效果好, 但是有个能出现线路拥堵情况,如果报错,请重试 +可以尝试 gpt-image-2-1K, gpt-image-2-2K, gpt-image-2-4K, 三个模型都尝试一下 +可以生成 1:1, 4:3, 16:9 的图片 +``` + > Grok 模型的 size 参数不可靠,API 返回尺寸不受控。 ## 使用方式 @@ -134,6 +141,43 @@ curl -X POST http://localhost:8765/agnes/images/generations \ 返回的 `size` 是根据 `size` 档位 + `ratio` 解析出的实际像素尺寸,便于拼接访问地址。 +### 上传版(multipart/form-data) + +如果不想手动 base64,可以用 `POST /agnes/images/generations/upload`,直接传本地图片文件: + +单张图: + +```bash +curl -X POST http://localhost:8765/agnes/images/generations/upload \ + -F "prompt=color this line art with watercolor style, soft palette, preserve composition" \ + -F "profile_name=tester" \ + -F "size=1K" \ + -F "ratio=16:9" \ + -F "images=@/path/to/your-image.png" +``` + +多张图(多图合成,`images` 字段可以传多次): + +```bash +curl -X POST http://localhost:8765/agnes/images/generations/upload \ + -F "prompt=combine the two characters into an intense fantasy battle scene" \ + -F "profile_name=tester" \ + -F "size=1K" \ + -F "ratio=16:9" \ + -F "images=@/path/to/character-1.png" \ + -F "images=@/path/to/character-2.png" +``` + +Form 字段: + +- `prompt`:必填,文本提示词 +- `profile_name`:必填,调用方 agent 的标识 +- `size`:可选,默认 `1K` +- `ratio`:可选,宽高比;支持 `1:1` / `3:4` / `4:3` / `16:9` / `9:16` / `2:3` / `3:2` / `21:9`;默认 `1:1` +- `images`:必填,至少上传 1 张,支持 PNG / JPEG / WebP;多张传多个 `images` 字段即可 + +响应格式与 JSON 版完全一致。 + ### 请求参数 请求体为 JSON,发送到 `POST /agnes/images/generations`: diff --git a/main.py b/main.py index 2fd3ccc..44e5905 100644 --- a/main.py +++ b/main.py @@ -4,8 +4,9 @@ from dotenv import load_dotenv load_dotenv() - -from fastapi import FastAPI +import base64 +import re +from fastapi import FastAPI, File, Form, HTTPException, UploadFile from fastapi.staticfiles import StaticFiles from pydantic import BaseModel, Field, model_validator @@ -97,6 +98,46 @@ def agnes_generate_images_endpoint(request: AgnesImageGenerationRequest): return AgnesImageGenerationResponse(**result) +_ALLOWED_IMAGE_TYPES = {"image/png", "image/jpeg", "image/jpg", "image/webp"} + + +@app.post("/agnes/images/generations/upload", response_model=AgnesImageGenerationResponse) +def agnes_generate_images_upload_endpoint( + prompt: str = Form(..., description="图片提示词"), + profile_name: str = Form(..., description="调用方标识(必填)"), + size: str = Form(default="1K", description="输出尺寸档位: 1K / 2K / 3K / 4K"), + ratio: str | None = Form(default=None, description=f"宽高比,支持 {sorted(VALID_RATIOS)}"), + images: list[UploadFile] = File(default=[], description="参考图像文件,可传多个"), +): + """multipart/form-data 版图生图:上传本地图片文件直接测试,无需 base64。""" + if not prompt.strip(): + raise HTTPException(status_code=422, detail="prompt 不能为空") + if not re.match(r"^[\w-]+$", profile_name): + raise HTTPException(status_code=422, detail="profile_name 只能包含字母数字下划线和短横线") + if not images or all(not f.filename for f in images): + raise HTTPException(status_code=422, detail="必须至少上传一张参考图 images") + + data_uris: list[str] = [] + for f in images: + if f.content_type and f.content_type not in _ALLOWED_IMAGE_TYPES: + raise HTTPException(status_code=400, detail=f"不支持的图片类型: {f.content_type}") + raw = f.file.read() + if not raw: + raise HTTPException(status_code=400, detail=f"上传的图片 {f.filename} 为空") + mime = f.content_type or "image/png" + b64 = base64.b64encode(raw).decode("ascii") + data_uris.append(f"data:{mime};base64,{b64}") + + result = agnes_generate_images( + prompt=prompt, + size=size, + ratio=ratio, + images=data_uris, + profile_name=profile_name, + ) + return AgnesImageGenerationResponse(**result) + + if __name__ == "__main__": import uvicorn diff --git a/requirements.txt b/requirements.txt index a03e0d0..7ccb0f3 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,5 @@ fastapi openai python-dotenv +python-multipart uvicorn[standard]