Provide containerized GPT and Grok image generation with environment-based configuration.main
commit
cf8b6c067d
@ -0,0 +1,3 @@ |
|||||||
|
GROK_API_KEY=your_grok_api_key |
||||||
|
GPT_API_KEY=your_gpt_api_key |
||||||
|
IMAGE_API_BASE_URL=https://api.slomerex.xyz/v1 |
||||||
@ -0,0 +1,40 @@ |
|||||||
|
# Environment and secrets |
||||||
|
.env |
||||||
|
.env.* |
||||||
|
!.env.example |
||||||
|
|
||||||
|
# Generated output |
||||||
|
output/ |
||||||
|
|
||||||
|
# Python |
||||||
|
__pycache__/ |
||||||
|
*.py[cod] |
||||||
|
*$py.class |
||||||
|
*.so |
||||||
|
.Python |
||||||
|
.pytest_cache/ |
||||||
|
.mypy_cache/ |
||||||
|
.ruff_cache/ |
||||||
|
.coverage |
||||||
|
htmlcov/ |
||||||
|
|
||||||
|
# Virtual environments |
||||||
|
.venv/ |
||||||
|
venv/ |
||||||
|
env/ |
||||||
|
|
||||||
|
# Packaging |
||||||
|
build/ |
||||||
|
dist/ |
||||||
|
*.egg-info/ |
||||||
|
|
||||||
|
# IDE and OS |
||||||
|
.vscode/ |
||||||
|
.idea/ |
||||||
|
*.iml |
||||||
|
.DS_Store |
||||||
|
Thumbs.db |
||||||
|
|
||||||
|
# Logs and local databases |
||||||
|
*.log |
||||||
|
*.sqlite3 |
||||||
@ -0,0 +1,13 @@ |
|||||||
|
FROM python:3.12-slim |
||||||
|
|
||||||
|
WORKDIR /app |
||||||
|
|
||||||
|
COPY requirements.txt . |
||||||
|
RUN pip install --no-cache-dir -r requirements.txt |
||||||
|
|
||||||
|
COPY agent-image-t2i.py . |
||||||
|
RUN mkdir -p /app/output |
||||||
|
|
||||||
|
EXPOSE 8000 |
||||||
|
|
||||||
|
CMD ["uvicorn", "agent-image-t2i:app", "--host", "0.0.0.0", "--port", "8000"] |
||||||
@ -0,0 +1,115 @@ |
|||||||
|
--- |
||||||
|
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. |
||||||
|
--- |
||||||
|
|
||||||
|
# AI 绘图工具 |
||||||
|
|
||||||
|
通过 FastAPI 文生图服务生成图片,供 Agent 通过 HTTP/cURL 直接调用,无需自己实现模型请求逻辑。 |
||||||
|
|
||||||
|
服务端使用 OpenAI Python SDK 请求 GPT Image / Grok Image 上游接口,生成的图片保存在服务端 `output/` 目录,并通过 HTTP 返回访问地址。 |
||||||
|
|
||||||
|
- 服务端 API 不要求调用方提供上游 API Key;上游鉴权通过项目根目录 `.env` 配置 |
||||||
|
- `.env` 不应提交到 Git,复制 `.env.example` 为 `.env` 后填写真实 Key |
||||||
|
- 使用 Docker Compose 时会自动读取 `.env` 并注入容器 |
||||||
|
|
||||||
|
## 可用脚本 |
||||||
|
|
||||||
|
| 能力 | 服务端文件 | HTTP 端点 | 模型 | |
||||||
|
|------|------------|------------|------| |
||||||
|
| GPT / Grok 绘图 | `agent-image-t2i.py` | `POST /images/generations` | `gpt-image-2-1K` / `gpt-image-2-2K` / `gpt-image-2-4K` / `grok-imagine-image-lite` | |
||||||
|
| 健康检查 | `agent-image-t2i.py` | `GET /health` | — | |
||||||
|
| 图片访问 | `agent-image-t2i.py` | `GET /output/{filename}` | — | |
||||||
|
|
||||||
|
## GPT 模型说明 |
||||||
|
|
||||||
|
| 模型 | 输出尺寸 | 备注 | |
||||||
|
|------|----------|------| |
||||||
|
| `gpt-image-2-1K` | ~1254×1254(1:1)或 1536×1024(横版) | **推荐**,线路稳定 | |
||||||
|
| `gpt-image-2-2K` | 不固定 | 线路不稳定,可能 503 | |
||||||
|
| `gpt-image-2-4K` | 不固定 | 线路不稳定,可能 503 | |
||||||
|
|
||||||
|
> Grok 模型的 size 参数不可靠,API 返回尺寸不受控。 |
||||||
|
|
||||||
|
## 使用方式 |
||||||
|
|
||||||
|
先启动 FastAPI 服务: |
||||||
|
|
||||||
|
```bash |
||||||
|
uvicorn agent-image-t2i:app --host 0.0.0.0 --port 8000 |
||||||
|
``` |
||||||
|
|
||||||
|
默认服务地址为 `http://localhost:8000`。 |
||||||
|
|
||||||
|
健康检查: |
||||||
|
|
||||||
|
```bash |
||||||
|
curl http://localhost:8000/health |
||||||
|
``` |
||||||
|
|
||||||
|
使用 Grok 绘图: |
||||||
|
|
||||||
|
```bash |
||||||
|
curl -X POST http://localhost:8000/images/generations \ |
||||||
|
-H "Content-Type: application/json" \ |
||||||
|
-d '{ |
||||||
|
"prompt": "a cute cat", |
||||||
|
"provider": "grok", |
||||||
|
"size": "1024x1024", |
||||||
|
"n": 1 |
||||||
|
}' |
||||||
|
``` |
||||||
|
|
||||||
|
使用 GPT 绘图: |
||||||
|
|
||||||
|
```bash |
||||||
|
curl -X POST http://localhost:8000/images/generations \ |
||||||
|
-H "Content-Type: application/json" \ |
||||||
|
-d '{ |
||||||
|
"prompt": "a cute cat", |
||||||
|
"provider": "gpt", |
||||||
|
"model": "gpt-image-2-1K", |
||||||
|
"size": "1536x1024", |
||||||
|
"n": 1 |
||||||
|
}' |
||||||
|
``` |
||||||
|
|
||||||
|
生成结果中的 `url` 为图片访问地址,例如 `http://localhost:8000/output/{filename}`。 |
||||||
|
|
||||||
|
## 调用建议 |
||||||
|
|
||||||
|
- **优先用 Grok**:稳定出图,适合日常使用 |
||||||
|
- **追求画质用 GPT 1K**:请求中设置 `"provider": "gpt"` |
||||||
|
- **GPT 2K/4K 线路可能不稳定**:不推荐作为默认模型 |
||||||
|
- **Prompt 用英文**:效果通常比中文好 |
||||||
|
- 使用 Docker Compose 时,项目的 `output/` 会映射到容器的 `/app/output` |
||||||
|
|
||||||
|
## 请求参数 |
||||||
|
|
||||||
|
请求体为 JSON,发送到 `POST /images/generations`: |
||||||
|
|
||||||
|
- `prompt`:必填,图片提示词 |
||||||
|
- `provider`:可选,`grok` 或 `gpt`,默认 `grok` |
||||||
|
- `model`:可选,覆盖 provider 的默认模型 |
||||||
|
- `size`:可选,默认 `1024x1024` |
||||||
|
- `n`:可选,生成数量,范围 `1 ~ 10`,默认 `1` |
||||||
|
|
||||||
|
## 输出规范 |
||||||
|
|
||||||
|
- 图片默认保存在服务端 `output/` 目录 |
||||||
|
- 响应返回 `filename`、`url` 和文件大小 `bytes` |
||||||
|
- 图片通过 `GET /output/{filename}` 访问 |
||||||
|
- 服务启动后,响应中的相对路径 `url` 需要拼接服务地址,例如 `http://localhost:8000/output/{filename}` |
||||||
|
|
||||||
|
## Prompt 技巧 |
||||||
|
|
||||||
|
- 使用英文 prompt 效果更好 |
||||||
|
- 描述要具体:主体 + 环境 + 光线 + 风格 + 情绪 |
||||||
|
- 横版图用 `--size 1536x1024`,正方形用 `--size 1024x1024` |
||||||
|
- 加上 `cinematic lighting, highly detailed, 4K` 等后缀提升质感 |
||||||
|
|
||||||
|
## 配置与注意事项 |
||||||
|
|
||||||
|
- 单次上游请求超时为 120 秒 |
||||||
|
- 上游请求遇到网络错误或 408/429/500/502/503/504 时,最多自动重试 3 次,并使用指数退避 |
||||||
|
- FastAPI 自动文档地址:`http://localhost:8000/docs` |
||||||
@ -0,0 +1,114 @@ |
|||||||
|
#!/usr/bin/env python3 |
||||||
|
"""通过 FastAPI 提供 GPT Image 和 Grok Image 文生图服务。""" |
||||||
|
|
||||||
|
import base64 |
||||||
|
import os |
||||||
|
import time |
||||||
|
from pathlib import Path |
||||||
|
from uuid import uuid4 |
||||||
|
|
||||||
|
from dotenv import load_dotenv |
||||||
|
from fastapi import FastAPI, HTTPException |
||||||
|
from fastapi.staticfiles import StaticFiles |
||||||
|
from openai import APIConnectionError, APIStatusError, APITimeoutError, OpenAI |
||||||
|
from pydantic import BaseModel, Field |
||||||
|
|
||||||
|
load_dotenv() |
||||||
|
|
||||||
|
API_BASE_URL = os.getenv("IMAGE_API_BASE_URL", "https://api.slomerex.xyz/v1") |
||||||
|
OUTPUT_DIR = Path("output") |
||||||
|
MAX_RETRIES = 3 |
||||||
|
RETRYABLE_STATUS_CODES = {408, 429, 500, 502, 503, 504} |
||||||
|
PROVIDERS = { |
||||||
|
"grok": { |
||||||
|
"api_key_env": "GROK_API_KEY", |
||||||
|
"model": "grok-imagine-image-lite", |
||||||
|
}, |
||||||
|
"gpt": { |
||||||
|
"api_key_env": "GPT_API_KEY", |
||||||
|
"model": "gpt-image-2-1K", |
||||||
|
}, |
||||||
|
} |
||||||
|
|
||||||
|
app = FastAPI(title="AI Image T2I API", version="1.0.0") |
||||||
|
OUTPUT_DIR.mkdir(parents=True, exist_ok=True) |
||||||
|
app.mount("/output", StaticFiles(directory=OUTPUT_DIR), name="output") |
||||||
|
|
||||||
|
|
||||||
|
class ImageGenerationRequest(BaseModel): |
||||||
|
prompt: str = Field(min_length=1, description="图片提示词") |
||||||
|
provider: str = Field(default="grok", pattern="^(grok|gpt)$", description="模型提供方") |
||||||
|
model: str | None = Field(default=None, description="覆盖提供方默认模型") |
||||||
|
size: str = Field(default="1024x1024", description="图片尺寸") |
||||||
|
n: int = Field(default=1, ge=1, le=10, description="生成数量") |
||||||
|
|
||||||
|
|
||||||
|
class GeneratedImage(BaseModel): |
||||||
|
filename: str |
||||||
|
url: str |
||||||
|
bytes: int |
||||||
|
|
||||||
|
|
||||||
|
class ImageGenerationResponse(BaseModel): |
||||||
|
provider: str |
||||||
|
model: str |
||||||
|
images: list[GeneratedImage] |
||||||
|
|
||||||
|
|
||||||
|
def generate_with_retry(client: OpenAI, *, model: str, prompt: str, size: str, n: int): |
||||||
|
for attempt in range(1, MAX_RETRIES + 1): |
||||||
|
try: |
||||||
|
return client.images.generate(model=model, prompt=prompt, size=size, n=n) |
||||||
|
except APIStatusError as e: |
||||||
|
if e.status_code not in RETRYABLE_STATUS_CODES or attempt == MAX_RETRIES: |
||||||
|
raise HTTPException(status_code=502, detail=f"上游图片服务错误: HTTP {e.status_code}") from e |
||||||
|
time.sleep(2 ** (attempt - 1)) |
||||||
|
except (APIConnectionError, APITimeoutError) as e: |
||||||
|
if attempt == MAX_RETRIES: |
||||||
|
raise HTTPException(status_code=504, detail="上游图片服务连接或请求超时") from e |
||||||
|
time.sleep(2 ** (attempt - 1)) |
||||||
|
|
||||||
|
|
||||||
|
@app.get("/health") |
||||||
|
def health_check(): |
||||||
|
return {"status": "ok"} |
||||||
|
|
||||||
|
|
||||||
|
@app.post("/images/generations", response_model=ImageGenerationResponse) |
||||||
|
def generate_images(request: ImageGenerationRequest): |
||||||
|
config = PROVIDERS[request.provider] |
||||||
|
api_key = os.getenv(config["api_key_env"]) |
||||||
|
if not api_key: |
||||||
|
raise HTTPException(status_code=500, detail=f"未配置环境变量 {config['api_key_env']}") |
||||||
|
|
||||||
|
model = request.model or config["model"] |
||||||
|
client = OpenAI(base_url=API_BASE_URL, api_key=api_key, timeout=120.0, max_retries=0) |
||||||
|
response = generate_with_retry( |
||||||
|
client, |
||||||
|
model=model, |
||||||
|
prompt=request.prompt, |
||||||
|
size=request.size, |
||||||
|
n=request.n, |
||||||
|
) |
||||||
|
|
||||||
|
images = [] |
||||||
|
for item in response.data: |
||||||
|
if not item.b64_json: |
||||||
|
raise HTTPException(status_code=502, detail="上游图片服务未返回 Base64 图片数据") |
||||||
|
|
||||||
|
try: |
||||||
|
raw = base64.b64decode(item.b64_json) |
||||||
|
except ValueError as e: |
||||||
|
raise HTTPException(status_code=502, detail="上游图片数据格式无效") from e |
||||||
|
|
||||||
|
filename = f"{model}-{uuid4().hex}.png" |
||||||
|
(OUTPUT_DIR / filename).write_bytes(raw) |
||||||
|
images.append(GeneratedImage(filename=filename, url=f"/output/{filename}", bytes=len(raw))) |
||||||
|
|
||||||
|
return ImageGenerationResponse(provider=request.provider, model=model, images=images) |
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__": |
||||||
|
import uvicorn |
||||||
|
|
||||||
|
uvicorn.run(app, host="0.0.0.0", port=8000) |
||||||
@ -0,0 +1,12 @@ |
|||||||
|
services: |
||||||
|
ai-image-t2i: |
||||||
|
build: |
||||||
|
context: . |
||||||
|
dockerfile: Dockerfile |
||||||
|
ports: |
||||||
|
- "8000:8000" |
||||||
|
env_file: |
||||||
|
- .env |
||||||
|
volumes: |
||||||
|
- ./output:/app/output |
||||||
|
restart: unless-stopped |
||||||
@ -0,0 +1,191 @@ |
|||||||
|
# 图像模型 API 文档 |
||||||
|
|
||||||
|
本文档整理 GPT Image 和 Grok Image 模型的 API 信息、调用示例、支持参数及速率限制。 |
||||||
|
|
||||||
|
## 通用信息 |
||||||
|
|
||||||
|
- API 基础地址:`https://api.slomerex.xyz/v1` |
||||||
|
- 认证方式:`Authorization: Bearer <TOKEN>` |
||||||
|
- Anthropic 格式的端点也接受 `x-api-key` 请求头。 |
||||||
|
- 可在「令牌」页面生成 API Key,并按模型、分组、IP、速率等维度进行授权。 |
||||||
|
- 示例中的 `<YOUR_API_KEY>` 请替换为令牌设置中的 API Key。 |
||||||
|
|
||||||
|
## 模型列表 |
||||||
|
|
||||||
|
| 模型 | 提供方 | 计费方式 | |
||||||
|
|---|---|---| |
||||||
|
| `gpt-image-2-1K` | OpenAI | 按次计费 | |
||||||
|
| `gpt-image-2-2K` | OpenAI | 按次计费 | |
||||||
|
| `gpt-image-2-4K` | OpenAI | 按次计费 | |
||||||
|
| `grok-imagine-image-lite` | xAI | 按次计费 | |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## gpt-image-2-1K |
||||||
|
|
||||||
|
### 调用示例 |
||||||
|
|
||||||
|
```python |
||||||
|
from openai import OpenAI |
||||||
|
|
||||||
|
client = OpenAI( |
||||||
|
base_url="https://api.slomerex.xyz/v1", |
||||||
|
api_key="<YOUR_API_KEY>", |
||||||
|
) |
||||||
|
|
||||||
|
completion = client.chat.completions.create( |
||||||
|
model="gpt-image-2-1K", |
||||||
|
messages=[ |
||||||
|
{"role": "user", "content": "Explain quantum entanglement in one paragraph."} |
||||||
|
], |
||||||
|
) |
||||||
|
|
||||||
|
print(completion.choices[0].message.content) |
||||||
|
``` |
||||||
|
|
||||||
|
### 支持的参数 |
||||||
|
|
||||||
|
| 参数 | 类型 | 默认值 / 范围 | 说明 | |
||||||
|
|---|---|---|---| |
||||||
|
| `prompt` | `string` | 必填 | 想要生成图像的文字描述 | |
||||||
|
| `size` | `enum` | `1024x1024` | 输出图像尺寸 | |
||||||
|
| `quality` | `enum` | `standard` | 生成质量预设 | |
||||||
|
| `style` | `enum` | `vivid` | 画风 | |
||||||
|
| `n` | `integer` | `1`,范围 `1 ~ 10` | 生成的图像数量 | |
||||||
|
| `response_format` | `enum` | `url` | 图像结果的返回方式 | |
||||||
|
|
||||||
|
### 速率限制 |
||||||
|
|
||||||
|
| 分组 | RPM | TPM | RPD | |
||||||
|
|---|---:|---:|---:| |
||||||
|
| Gpt Image | 50 | — | 800 | |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## gpt-image-2-2K |
||||||
|
|
||||||
|
### 调用示例 |
||||||
|
|
||||||
|
```python |
||||||
|
from openai import OpenAI |
||||||
|
|
||||||
|
client = OpenAI( |
||||||
|
base_url="https://api.slomerex.xyz/v1", |
||||||
|
api_key="<YOUR_API_KEY>", |
||||||
|
) |
||||||
|
|
||||||
|
completion = client.chat.completions.create( |
||||||
|
model="gpt-image-2-2K", |
||||||
|
messages=[ |
||||||
|
{"role": "user", "content": "Explain quantum entanglement in one paragraph."} |
||||||
|
], |
||||||
|
) |
||||||
|
|
||||||
|
print(completion.choices[0].message.content) |
||||||
|
``` |
||||||
|
|
||||||
|
### 支持的参数 |
||||||
|
|
||||||
|
| 参数 | 类型 | 默认值 / 范围 | 说明 | |
||||||
|
|---|---|---|---| |
||||||
|
| `prompt` | `string` | 必填 | 想要生成图像的文字描述 | |
||||||
|
| `size` | `enum` | `1024x1024` | 输出图像尺寸 | |
||||||
|
| `quality` | `enum` | `standard` | 生成质量预设 | |
||||||
|
| `style` | `enum` | `vivid` | 画风 | |
||||||
|
| `n` | `integer` | `1`,范围 `1 ~ 10` | 生成的图像数量 | |
||||||
|
| `response_format` | `enum` | `url` | 图像结果的返回方式 | |
||||||
|
|
||||||
|
### 速率限制 |
||||||
|
|
||||||
|
| 分组 | RPM | TPM | RPD | |
||||||
|
|---|---:|---:|---:| |
||||||
|
| Gpt Image | 70 | — | 1.2K | |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## gpt-image-2-4K |
||||||
|
|
||||||
|
### 调用示例 |
||||||
|
|
||||||
|
```bash |
||||||
|
curl https://api.slomerex.xyz/v1/chat/completions \ |
||||||
|
-H "Authorization: Bearer $NEW_API_KEY" \ |
||||||
|
-H "Content-Type: application/json" \ |
||||||
|
-d '{ |
||||||
|
"model": "gpt-image-2-4K", |
||||||
|
"messages": [ |
||||||
|
{ |
||||||
|
"role": "user", |
||||||
|
"content": "Explain quantum entanglement in one paragraph." |
||||||
|
} |
||||||
|
], |
||||||
|
"temperature": 0.7 |
||||||
|
}' |
||||||
|
``` |
||||||
|
|
||||||
|
### 支持的参数 |
||||||
|
|
||||||
|
| 参数 | 类型 | 默认值 / 范围 | 说明 | |
||||||
|
|---|---|---|---| |
||||||
|
| `prompt` | `string` | 必填 | 想要生成图像的文字描述 | |
||||||
|
| `size` | `enum` | `1024x1024` | 输出图像尺寸 | |
||||||
|
| `quality` | `enum` | `standard` | 生成质量预设 | |
||||||
|
| `style` | `enum` | `vivid` | 画风 | |
||||||
|
| `n` | `integer` | `1`,范围 `1 ~ 10` | 生成的图像数量 | |
||||||
|
| `response_format` | `enum` | `url` | 图像结果的返回方式 | |
||||||
|
|
||||||
|
### 速率限制 |
||||||
|
|
||||||
|
| 分组 | RPM | TPM | RPD | |
||||||
|
|---|---:|---:|---:| |
||||||
|
| Gpt Image | 60 | — | 900 | |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## grok-imagine-image-lite |
||||||
|
|
||||||
|
### 调用示例 |
||||||
|
|
||||||
|
```python |
||||||
|
from openai import OpenAI |
||||||
|
|
||||||
|
client = OpenAI( |
||||||
|
base_url="https://api.slomerex.xyz/v1", |
||||||
|
api_key="<YOUR_API_KEY>", |
||||||
|
) |
||||||
|
|
||||||
|
completion = client.chat.completions.create( |
||||||
|
model="grok-imagine-image-lite", |
||||||
|
messages=[ |
||||||
|
{"role": "user", "content": "Explain quantum entanglement in one paragraph."} |
||||||
|
], |
||||||
|
) |
||||||
|
|
||||||
|
print(completion.choices[0].message.content) |
||||||
|
``` |
||||||
|
|
||||||
|
### 支持的参数 |
||||||
|
|
||||||
|
| 参数 | 类型 | 默认值 / 范围 | 说明 | |
||||||
|
|---|---|---|---| |
||||||
|
| `prompt` | `string` | 必填 | 想要生成图像的文字描述 | |
||||||
|
| `size` | `enum` | `1024x1024` | 输出图像尺寸 | |
||||||
|
| `quality` | `enum` | `standard` | 生成质量预设 | |
||||||
|
| `style` | `enum` | `vivid` | 画风 | |
||||||
|
| `n` | `integer` | `1`,范围 `1 ~ 10` | 生成的图像数量 | |
||||||
|
| `response_format` | `enum` | `url` | 图像结果的返回方式 | |
||||||
|
|
||||||
|
### 速率限制 |
||||||
|
|
||||||
|
| 分组 | RPM | TPM | RPD | |
||||||
|
|---|---:|---:|---:| |
||||||
|
| Grok | 40 | — | 700 | |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
## 速率限制说明 |
||||||
|
|
||||||
|
- **RPM**:每分钟请求数(Requests Per Minute) |
||||||
|
- **TPM**:每分钟 token 数(Tokens Per Minute) |
||||||
|
- **RPD**:每日请求数(Requests Per Day) |
||||||
|
- 限制按令牌分组生效。 |
||||||
@ -0,0 +1,4 @@ |
|||||||
|
fastapi |
||||||
|
openai |
||||||
|
python-dotenv |
||||||
|
uvicorn[standard] |
||||||
Loading…
Reference in new issue