feat: 添加 Antigravity 图像生成 API (OpenAI 兼容)

- 实现 /v1/images/generations 端点
- 添加 ImageGenerationRequest/Response 数据模型
- 实现 OpenAI 到 Antigravity 请求/响应转换器
- 支持 dall-e-3/dall-e-2 模型名映射到 gemini-3-pro-image
- 支持 b64_json 和 url 两种响应格式
- 添加 Python SDK 集成测试脚本
This commit is contained in:
liubu
2026-01-03 23:34:03 +08:00
parent a793de37d9
commit 3ff24e060e
7 changed files with 1491 additions and 24 deletions
+369
View File
@@ -0,0 +1,369 @@
#!/usr/bin/env python3
"""
OpenAI 兼容图像生成 API 测试脚本
使用 OpenAI Python SDK 测试 Antigravity 图像生成 API。
使用方法:
# 安装依赖
pip install openai
# 运行测试(需要先启动 API Server)
python scripts/test_image_api.py
# 指定自定义 API 地址和密钥
python scripts/test_image_api.py --base-url http://localhost:8999 --api-key your-key
环境变量:
PROXYCAST_BASE_URL: API 服务器地址(默认: http://localhost:8999)
PROXYCAST_API_KEY: API 密钥(默认: pc_LXZbIv3o78WpHuQwqgmwC0U4G0cY5UtQ)
"""
import argparse
import base64
import os
import sys
from datetime import datetime
try:
from openai import OpenAI
except ImportError:
print("错误: 请先安装 openai 库")
print("运行: pip install openai")
sys.exit(1)
def test_image_generation_url(client: OpenAI, prompt: str) -> bool:
"""
测试 URL 响应格式的图像生成
Args:
client: OpenAI 客户端
prompt: 图像生成提示词
Returns:
测试是否通过
"""
print("\n" + "=" * 60)
print("测试 1: URL 响应格式")
print("=" * 60)
print(f"提示词: {prompt}")
try:
response = client.images.generate(
model="dall-e-3", # 会被映射到 gemini-3-pro-image
prompt=prompt,
n=1,
size="1024x1024",
response_format="url"
)
# 验证响应结构
print(f"\n响应时间戳: {response.created}")
print(f"生成图片数量: {len(response.data)}")
if len(response.data) == 0:
print("❌ 错误: 没有生成图片")
return False
image = response.data[0]
# 验证 URL 格式
if image.url:
print(f"URL 长度: {len(image.url)} 字符")
if image.url.startswith("data:image/"):
print("✅ URL 格式正确 (data URL)")
else:
print(f"⚠️ URL 格式: {image.url[:50]}...")
else:
print("❌ 错误: URL 为空")
return False
# 验证 revised_prompt
if image.revised_prompt:
print(f"修订提示词: {image.revised_prompt[:100]}...")
else:
print("ℹ️ 没有修订提示词")
print("\n✅ 测试 1 通过")
return True
except Exception as e:
print(f"\n❌ 测试 1 失败: {e}")
return False
def test_image_generation_b64(client: OpenAI, prompt: str) -> bool:
"""
测试 b64_json 响应格式的图像生成
Args:
client: OpenAI 客户端
prompt: 图像生成提示词
Returns:
测试是否通过
"""
print("\n" + "=" * 60)
print("测试 2: b64_json 响应格式")
print("=" * 60)
print(f"提示词: {prompt}")
try:
response = client.images.generate(
model="gemini-3-pro-image-preview", # 直接使用 Gemini 模型名
prompt=prompt,
n=1,
response_format="b64_json"
)
# 验证响应结构
print(f"\n响应时间戳: {response.created}")
print(f"生成图片数量: {len(response.data)}")
if len(response.data) == 0:
print("❌ 错误: 没有生成图片")
return False
image = response.data[0]
# 验证 b64_json 格式
if image.b64_json:
print(f"Base64 数据长度: {len(image.b64_json)} 字符")
# 尝试解码验证
try:
decoded = base64.b64decode(image.b64_json)
print(f"解码后大小: {len(decoded)} 字节")
# 检查图片魔数
if decoded[:8] == b'\x89PNG\r\n\x1a\n':
print("✅ 图片格式: PNG")
elif decoded[:2] == b'\xff\xd8':
print("✅ 图片格式: JPEG")
elif decoded[:4] == b'GIF8':
print("✅ 图片格式: GIF")
elif decoded[:4] == b'RIFF':
print("✅ 图片格式: WebP")
else:
print(f"⚠️ 未知图片格式: {decoded[:8].hex()}")
except Exception as e:
print(f"⚠️ Base64 解码失败: {e}")
else:
print("❌ 错误: b64_json 为空")
return False
# 验证 revised_prompt
if image.revised_prompt:
print(f"修订提示词: {image.revised_prompt[:100]}...")
else:
print("ℹ️ 没有修订提示词")
print("\n✅ 测试 2 通过")
return True
except Exception as e:
print(f"\n❌ 测试 2 失败: {e}")
return False
def test_error_handling(client: OpenAI) -> bool:
"""
测试错误处理
Args:
client: OpenAI 客户端
Returns:
测试是否通过
"""
print("\n" + "=" * 60)
print("测试 3: 错误处理")
print("=" * 60)
try:
# 测试空提示词
print("测试空提示词...")
try:
response = client.images.generate(
model="dall-e-3",
prompt="", # 空提示词
n=1
)
print("❌ 错误: 应该拒绝空提示词")
return False
except Exception as e:
error_msg = str(e).lower()
if "prompt" in error_msg or "empty" in error_msg or "required" in error_msg:
print(f"✅ 正确拒绝空提示词: {e}")
else:
print(f"⚠️ 收到错误但消息不明确: {e}")
print("\n✅ 测试 3 通过")
return True
except Exception as e:
print(f"\n❌ 测试 3 失败: {e}")
return False
def test_response_structure(client: OpenAI, prompt: str) -> bool:
"""
测试响应结构符合 OpenAI 规范
Args:
client: OpenAI 客户端
prompt: 图像生成提示词
Returns:
测试是否通过
"""
print("\n" + "=" * 60)
print("测试 4: 响应结构验证")
print("=" * 60)
print(f"提示词: {prompt}")
try:
response = client.images.generate(
model="dall-e-3",
prompt=prompt,
n=1,
response_format="url"
)
# 验证 created 字段
if response.created:
print(f"✅ created 字段存在: {response.created}")
# 验证是有效的 Unix 时间戳
if response.created > 0:
dt = datetime.fromtimestamp(response.created)
print(f" 时间: {dt}")
else:
print("❌ created 不是有效时间戳")
return False
else:
print("❌ created 字段缺失")
return False
# 验证 data 字段
if response.data is not None:
print(f"✅ data 字段存在: {len(response.data)} 项")
if len(response.data) > 0:
print("✅ data 数组非空")
else:
print("❌ data 数组为空")
return False
else:
print("❌ data 字段缺失")
return False
# 验证每个图片项
for i, image in enumerate(response.data):
print(f"\n图片 {i + 1}:")
has_url = image.url is not None
has_b64 = image.b64_json is not None
if has_url:
print(f" ✅ url 字段存在")
if has_b64:
print(f" ✅ b64_json 字段存在")
if not has_url and not has_b64:
print(f" ❌ 缺少 url 和 b64_json")
return False
if image.revised_prompt:
print(f" ✅ revised_prompt 字段存在")
else:
print(f" ℹ️ revised_prompt 字段为空")
print("\n✅ 测试 4 通过")
return True
except Exception as e:
print(f"\n❌ 测试 4 失败: {e}")
return False
def main():
parser = argparse.ArgumentParser(
description="测试 OpenAI 兼容图像生成 API"
)
parser.add_argument(
"--base-url",
default=os.environ.get("PROXYCAST_BASE_URL", "http://localhost:8999"),
help="API 服务器地址"
)
parser.add_argument(
"--api-key",
default=os.environ.get("PROXYCAST_API_KEY", "pc_LXZbIv3o78WpHuQwqgmwC0U4G0cY5UtQ"),
help="API 密钥"
)
parser.add_argument(
"--prompt",
default="A cute fluffy cat sitting on a windowsill, looking at the sunset",
help="测试用的图像生成提示词"
)
parser.add_argument(
"--skip-generation",
action="store_true",
help="跳过实际图像生成测试(仅测试错误处理)"
)
args = parser.parse_args()
print("=" * 60)
print("OpenAI 兼容图像生成 API 测试")
print("=" * 60)
print(f"API 地址: {args.base_url}")
print(f"API 密钥: {args.api_key[:8]}...")
print(f"测试提示词: {args.prompt[:50]}...")
# 创建 OpenAI 客户端
client = OpenAI(
base_url=f"{args.base_url}/v1",
api_key=args.api_key
)
results = []
if not args.skip_generation:
# 测试 1: URL 响应格式
results.append(("URL 响应格式", test_image_generation_url(client, args.prompt)))
# 测试 2: b64_json 响应格式
results.append(("b64_json 响应格式", test_image_generation_b64(client, args.prompt)))
# 测试 4: 响应结构验证
results.append(("响应结构验证", test_response_structure(client, args.prompt)))
# 测试 3: 错误处理
results.append(("错误处理", test_error_handling(client)))
# 打印总结
print("\n" + "=" * 60)
print("测试总结")
print("=" * 60)
passed = 0
failed = 0
for name, result in results:
status = "✅ 通过" if result else "❌ 失败"
print(f" {name}: {status}")
if result:
passed += 1
else:
failed += 1
print(f"\n总计: {passed} 通过, {failed} 失败")
if failed > 0:
sys.exit(1)
else:
print("\n🎉 所有测试通过!")
sys.exit(0)
if __name__ == "__main__":
main()