项目功能较少,欢迎使用AI添加自己需要的功能后PR
一个基于 Nitro v3 的 OpenAI 兼容生图 API 服务。前端或第三方客户端可以按 OpenAI Images / Responses 的请求格式调用本服务,后端再按配置把统一后的图片任务分发给不同提供商。
- 兼容 OpenAI 风格的图片接口。
- 支持 JSON 和 multipart/form-data 请求。
- 客户端错误返回 OpenAI 风格 JSON 错误体,避免只有 404 没有原因。
- 支持 CORS 预检和跨域请求。
- 支持多 provider 配置,并按
model + action路由。 - 内置测试 provider,未配置真实 provider 时可直接本地调试。
| 接口 | 说明 | 统一 action |
|---|---|---|
POST /v1/images/generations |
文生图 | generate |
POST /v1/images/edits |
图像编辑 | edit |
POST /v1/images/variations |
基于图片生成变体 | variation |
POST /v1/responses |
Responses API 的 image_generation tool |
generate / edit |
GET /v1/models |
拉取当前已注册 provider 支持的模型列表 | - |
未知 /v1/** 路径和不支持的方法会返回 OpenAI 风格错误 JSON。
安装依赖:
pnpm install启动开发服务:
pnpm dev默认监听地址通常是:
http://localhost:3000
运行测试:
pnpm test类型检查:
npx tsc --noEmit构建:
pnpm build预览构建产物:
pnpm preview图片后端配置支持两种加载方式,优先级从高到低:
- 项目根目录的
image-providers.config.json - Nitro
runtimeConfig.imageProviders,通常通过环境变量NITRO_IMAGE_PROVIDERS覆盖
当前 nitro.config.ts 的默认值是:
runtimeConfig: {
apiKeys: "",
imageProviders: "[]",
}如果配置为空数组,服务会注册内置 test provider,用于本地开发和接口联调。
推荐在 image-providers.config.json 使用对象格式:
{
"processors": [],
"providers": []
}旧的 provider 数组格式仍然兼容。
如果没有 image-providers.config.json,可以用环境变量配置:
NITRO_IMAGE_PROVIDERS='{"processors":[],"providers":[]}' pnpm devPowerShell 示例:
$env:NITRO_IMAGE_PROVIDERS='{"processors":[],"providers":[]}'
pnpm dev为了防止接口被滥用,可以通过环境变量配置允许访问本服务的客户端 API Key:
NITRO_API_KEYS="client-key-1,client-key-2" pnpm devPowerShell 示例:
$env:NITRO_API_KEYS="client-key-1,client-key-2"
pnpm dev配置为空时不启用鉴权,方便本地开发。配置后,所有 /v1/** 非 OPTIONS 请求都必须携带以下任意一种请求头:
Authorization: Bearer client-key-1或:
x-api-key: client-key-1注意:NITRO_API_KEYS 是访问本服务的客户端密钥,provider 配置里的 apiKey 是本服务访问 OpenAI 的密钥,两者用途不同。
请求会先被解析成统一的 ImageInput,其中核心字段是:
type ImageAction = "generate" | "edit" | "variation";provider 声明自己支持的 action:
actionSupports: readonly ImageAction[];运行时按以下条件选择 provider:
model在 provider 的models列表中。action在 provider 的actionSupports列表中。- 多个 provider 同时匹配时,使用配置顺序中最先注册的 provider。
- 如果 provider 配置了
processor,会在调用 provider 前后执行对应 processor。
processor 用于在 provider 调用前后修改 ImageInput 和 ImageOutput。每个 provider 最多引用一个 processor:
{
"type": "test",
"models": ["test-image"],
"processor": "demo-processor"
}内置测试 processor,用于验证 processor 链路。它可以给 prompt 和 revised prompt 加前缀,也可以改输出图片的 MIME 类型。
{
"id": "demo-processor",
"type": "testprocessor",
"promptPrefix": "[input] ",
"revisedPromptPrefix": "[output] ",
"outputMimeType": "image/webp"
}字段说明:
| 字段 | 必填 | 说明 |
|---|---|---|
id |
是 | processor 唯一标识,provider 用 processor 字段引用 |
type |
是 | 当前支持 testprocessor |
enabled |
否 | 设置为 false 时跳过该 processor |
promptPrefix |
否 | 调用 provider 前给 ImageInput.prompt 加前缀 |
revisedPromptPrefix |
否 | provider 返回后给 ImageOutput.images[].revisedPrompt 加前缀 |
outputMimeType |
否 | 将输出图片 MIME 类型改为 image/png、image/jpeg 或 image/webp |
本地尺寸适配器。它会先判断用户请求的 size 是否超过配置的最大尺寸;如果超过,就把生成请求的 size 等比缩小到最大尺寸内。生成完成后,它会按 provider 实际返回图片的宽高,用 sharp 的 Lanczos3 kernel 等比放大,直到输出宽度和高度都不低于用户原始请求尺寸,不裁剪、不拉伸。
{
"id": "resize-local-4k",
"type": "size-adapter:local:sharp-lanczos3",
"maxWidth": 1920,
"maxHeight": 1920,
"maxPixels": 2073600
}字段说明:
| 字段 | 必填 | 说明 |
|---|---|---|
id |
是 | processor 唯一标识 |
type |
是 | size-adapter:local:sharp-lanczos3 |
maxWidth |
是 | 允许发送给生成 provider 的最大宽度 |
maxHeight |
是 | 允许发送给生成 provider 的最大高度 |
maxPixels |
否 | 允许发送给生成 provider 的最大像素数,例如 1920 * 1080 = 2073600 |
fit |
否 | 兼容旧配置;当前输出固定按比例缩放,不使用该字段 |
阿里云视觉智能开放平台「图像超分」云端处理器。生成前,它会把用户请求的 size 按原始比例缩到生成 provider 可接受的最大尺寸内。生成完成后,它会读取 provider 实际返回图片的宽高;如果实际图片已经满足客户端请求,直接返回;如果需要超分,则按阿里云硬限制和本 processor 配置等比缩到可上传的最大尺寸,再通过阿里云官方 SDK 调用 MakeSuperResolutionImage,最后下载返回的图片 URL。
阿里云版本不会在云端放大后再用本地 sharp 强制裁切或拉伸到客户端比例。输出阶段会按实际上传尺寸选择刚好满足客户端宽高的最小倍率;如果 4x 也无法满足,就用 4x 返回可达到的最大图片。生成已经成功后,如果阿里云后处理失败,会尽量返回 provider 原图,避免浪费生图成本。
{
"id": "resize-aliyun-auto",
"type": "size-adapter:aliyun:super-resolution",
"maxWidth": 1920,
"maxHeight": 1920,
"maxPixels": 2073600,
"accessKeyId": "aliyun-your-access-key-id",
"accessKeySecret": "aliyun-your-access-key-secret",
"regionId": "cn-shanghai",
"mode": "base",
"outputFormat": "png",
"outputQuality": 95,
"timeoutMs": 120000
}字段说明:
| 字段 | 必填 | 说明 |
|---|---|---|
id |
是 | processor 唯一标识 |
type |
是 | size-adapter:aliyun:super-resolution |
maxWidth |
是 | 允许发送给生成 provider 的最大宽度 |
maxHeight |
是 | 允许发送给生成 provider 的最大高度 |
maxPixels |
否 | 允许发送给生成 provider 的最大像素数,例如 1920 * 1080 = 2073600 |
accessKeyId |
是 | 阿里云 RAM 用户 AccessKey ID |
accessKeySecret |
是 | 阿里云 RAM 用户 AccessKey Secret |
regionId |
否 | 阿里云地域,默认 cn-shanghai |
endpoint |
否 | 自定义阿里云 imageenhan endpoint |
mode |
否 | 阿里云 Mode 参数,例如 base |
outputFormat |
否 | 输出格式,只能是 png、jpg 或 bmp |
outputQuality |
否 | 阿里云 OutputQuality 参数 |
timeoutMs |
否 | SDK 连接和读取超时时间,单位毫秒 |
Modelslab Real-ESRGAN 云端超分处理器。生成前,它会把用户请求的 size 按原始比例缩到生成 provider 可接受的最大尺寸内。生成完成后,它会读取 provider 实际返回图片的宽高;如果实际图片已经满足客户端请求,直接返回;如果需要超分,则把 provider 输出图片提交到 Modelslab super resolution API 放大,再下载返回的图片 URL。
Modelslab 版本不会在云端放大后再用本地 sharp 强制裁切或拉伸到客户端比例。输出阶段会按实际图片尺寸选择刚好满足客户端宽高的最小倍率;如果 4x 也无法满足,就用 4x 返回可达到的最大图片。生成已经成功后,如果 Modelslab 后处理失败,会尽量返回 provider 原图,避免浪费生图成本。
{
"id": "resize-modelslab-auto",
"type": "size-adapter:modelslab:real-esrgan",
"maxWidth": 1920,
"maxHeight": 1920,
"maxPixels": 2073600,
"apiKey": "modelslab-your-api-key",
"modelByScale": {
"2": "RealESRGAN_x2plus",
"3": "realesr-general-x4v3",
"4": "realesr-general-x4v3"
},
"faceEnhance": false
}字段说明:
| 字段 | 必填 | 说明 |
|---|---|---|
id |
是 | processor 唯一标识 |
type |
是 | size-adapter:modelslab:real-esrgan |
maxWidth |
是 | 允许发送给生成 provider 的最大宽度 |
maxHeight |
是 | 允许发送给生成 provider 的最大高度 |
maxPixels |
否 | 允许发送给生成 provider 的最大像素数,例如 1920 * 1080 = 2073600 |
apiKey |
是 | Modelslab API Key |
modelId |
否 | 全局强制使用的 Modelslab 模型。配置后会覆盖 modelByScale 和内置默认模型 |
modelByScale |
否 | 按倍率覆盖模型,例如 { "2": "RealESRGAN_x2plus", "4": "ultra_resolution" }。不填时,2x 使用 RealESRGAN_x2plus,3x/4x 使用 realesr-general-x4v3 |
faceEnhance |
否 | 是否启用人脸增强,默认 false |
baseURL |
否 | 自定义 Modelslab super resolution API 地址 |
Claid.ai 云端放大处理器。生成前,它会把用户请求的 size 按原始比例缩到生成 provider 可接受的最大尺寸内。生成完成后,它会读取 provider 实际返回图片的宽高;如果实际图片已经满足客户端请求,直接返回;如果需要放大,则通过 Claid POST /v1/image/edit/upload 上传图片,并使用 restorations.upscale + resizing 输出等比例放大结果。
Claid 版本不会裁剪或拉伸到客户端比例。输出阶段会按实际图片尺寸选择一个保持比例的目标边,并使用 fit: "bounds" 让 Claid 保持原图比例。生成已经成功后,如果 Claid 后处理失败,会尽量返回 provider 原图,避免浪费生图成本。
{
"id": "resize-claid-auto",
"type": "size-adapter:claid:upscale",
"maxWidth": 1920,
"maxHeight": 1920,
"maxPixels": 2073600,
"apiKey": "claid-your-api-key",
"upscaleType": "smart_enhance",
"timeoutMs": 120000
}字段说明:
| 字段 | 必填 | 说明 |
|---|---|---|
id |
是 | processor 唯一标识 |
type |
是 | size-adapter:claid:upscale |
maxWidth |
是 | 允许发送给生成 provider 的最大宽度 |
maxHeight |
是 | 允许发送给生成 provider 的最大高度 |
maxPixels |
否 | 允许发送给生成 provider 的最大像素数,例如 1920 * 1080 = 2073600 |
apiKey |
是 | Claid API Key |
upscaleType |
否 | Claid restorations.upscale 类型,可选 smart_enhance、smart_resize、faces、digital_art、photo,默认 smart_enhance |
baseURL |
否 | 自定义 Claid upload API 地址,默认 https://api.claid.ai/v1/image/edit/upload |
timeoutMs |
否 | Claid 请求和输出下载超时时间,单位毫秒 |
内置测试 provider,不需要 API Key。它会返回很小的测试图片字节,用于开发环境验证接口、响应格式和前端流程。
{
"providers": [
{
"type": "test",
"models": ["test-image", "gpt-image-1"]
}
]
}接入 OpenAI Images API,支持:
generate->client.images.generate(...)edit->client.images.edit(...)
{
"providers": [
{
"id": "openai-images",
"type": "openai-images",
"apiKey": "sk-...",
"models": ["gpt-image-1", "gpt-image-2"]
}
]
}接入 OpenAI Images variation API,支持:
variation->client.images.createVariation(...)
该能力通常用于 dall-e-2 这类支持 variation 的模型。
{
"providers": [
{
"id": "openai-variation",
"type": "openai-variation",
"apiKey": "sk-...",
"models": ["dall-e-2"]
}
]
}接入 OpenAI Responses API,通过 image_generation tool 生成或编辑图片,支持:
generate->client.responses.create(...)edit->client.responses.create(...)
{
"providers": [
{
"id": "openai-responses",
"type": "openai-responses",
"apiKey": "sk-...",
"models": ["gpt-image-1", "gpt-image-2"]
}
]
}| 字段 | 必填 | 说明 |
|---|---|---|
id |
是 | provider 唯一标识 |
type |
是 | openai-images、openai-variation 或 openai-responses |
apiKey |
是 | OpenAI API Key |
models |
是 | provider 支持的模型列表 |
enabled |
否 | 设置为 false 时跳过该 provider |
processor |
否 | 引用一个已配置的 processor |
baseURL |
否 | 自定义 OpenAI 兼容 API 地址 |
organization |
否 | OpenAI organization |
project |
否 | OpenAI project |
timeoutMs |
否 | SDK 请求超时时间 |
maxRetries |
否 | SDK 最大重试次数 |
userAgent |
否 | 覆盖 OpenAI SDK 请求的 User-Agent,默认 4k-image-api |
{
"processors": [
{
"id": "demo-processor",
"type": "testprocessor",
"promptPrefix": "[input] ",
"revisedPromptPrefix": "[output] "
},
{
"id": "resize-local-4k",
"type": "size-adapter:local:sharp-lanczos3",
"maxWidth": 1920,
"maxHeight": 1920,
"maxPixels": 2073600
},
{
"id": "resize-modelslab-auto",
"type": "size-adapter:modelslab:real-esrgan",
"maxWidth": 1920,
"maxHeight": 1920,
"maxPixels": 2073600,
"apiKey": "modelslab-your-api-key",
"modelByScale": {
"2": "RealESRGAN_x2plus",
"3": "realesr-general-x4v3",
"4": "realesr-general-x4v3"
}
},
{
"id": "resize-claid-auto",
"type": "size-adapter:claid:upscale",
"maxWidth": 1920,
"maxHeight": 1920,
"maxPixels": 2073600,
"apiKey": "claid-your-api-key",
"upscaleType": "smart_enhance",
"timeoutMs": 120000
},
{
"id": "resize-aliyun-auto",
"type": "size-adapter:aliyun:super-resolution",
"maxWidth": 1920,
"maxHeight": 1920,
"maxPixels": 2073600,
"accessKeyId": "aliyun-your-access-key-id",
"accessKeySecret": "aliyun-your-access-key-secret",
"regionId": "cn-shanghai",
"mode": "base",
"outputFormat": "png",
"outputQuality": 95,
"timeoutMs": 120000
}
],
"providers": [
{
"id": "openai-images-main",
"type": "openai-images",
"apiKey": "sk-...",
"models": ["gpt-image-1", "gpt-image-2"],
"processor": "resize-local-4k",
"userAgent": "4k-image-api",
"timeoutMs": 120000,
"maxRetries": 1
},
{
"id": "openai-variation-main",
"type": "openai-variation",
"apiKey": "sk-...",
"models": ["dall-e-2"]
},
{
"id": "openai-responses-main",
"type": "openai-responses",
"apiKey": "sk-...",
"models": ["gpt-image-1"]
}
]
}curl http://localhost:3000/v1/images/generations \
-H "authorization: Bearer client-key-1" \
-H "content-type: application/json" \
-d '{
"model": "gpt-image-1",
"prompt": "生成一张小猫图片",
"size": "1024x1024",
"output_format": "png"
}'curl http://localhost:3000/v1/responses \
-H "authorization: Bearer client-key-1" \
-H "content-type: application/json" \
-d '{
"model": "gpt-image-1",
"input": "生成一张小猫图片",
"tools": [
{
"type": "image_generation",
"size": "1024x1024",
"output_format": "png",
"quality": "auto"
}
],
"tool_choice": "required"
}'/v1/images/edits 支持 multipart/form-data,请用 image 上传输入图片,可选 mask:
curl http://localhost:3000/v1/images/edits \
-H "authorization: Bearer client-key-1" \
-F "model=gpt-image-1" \
-F "prompt=把背景换成海边" \
-F "image=@input.png" \
-F "mask=@mask.png"/v1/images/variations 支持 multipart/form-data,请用 image 上传输入图片:
curl http://localhost:3000/v1/images/variations \
-H "authorization: Bearer client-key-1" \
-F "model=dall-e-2" \
-F "image=@input.png" \
-F "response_format=b64_json"/v1/models 返回当前已注册 provider 的模型列表,并包含每个模型支持的 action:
curl http://localhost:3000/v1/models \
-H "authorization: Bearer client-key-1"Images API 风格接口返回:
{
"created": 1760000000,
"data": [
{
"b64_json": "..."
}
],
"usage": {}
}/v1/responses 返回 Responses 风格结构,图片结果位于 output[].result:
{
"id": "resp_xxx",
"object": "response",
"status": "completed",
"output": [
{
"type": "image_generation_call",
"status": "completed",
"result": "..."
}
],
"output_text": "",
"usage": {}
}客户端错误会返回 JSON:
{
"error": {
"message": "Image model is required.",
"type": "invalid_request_error",
"param": "model",
"code": "model_required"
}
}常见错误:
- 请求体不是合法 JSON。
Content-Type不是application/json或multipart/form-data。- 配置了
NITRO_API_KEYS但没有传有效客户端 API Key。 - 没有传
model。 - 没有 provider 支持该
model + action。 - 请求了不存在的
/v1/**接口。
app/ 前端入口
server/routes/v1/ OpenAI 兼容路由
server/utils/openai-image/ OpenAI 请求解析、响应格式化、错误处理
server/utils/image/ 统一图片输入输出、provider manager
server/utils/image/providers 后端 provider 实现
tests/ Vitest 测试
- 前端 OpenAI endpoint 不进入统一后端类型,后端只关心
ImageAction。 - provider 不通过 endpoint 分流,而是声明
actionSupports。 openai-images、openai-variation、openai-responses分开实现,避免在 provider 内部用隐式条件猜接口类型。createImageProviderManager保持私有,只导出全局imageProviderManager。