1
0
Files
nex/scripts
lanyuanxiaoyao e1af978c56 feat: 完善 API 兼容性测试用例
- 修复 Anthropic Count Tokens 响应验证器,检查嵌套结构
- 补充 OpenAI service_tier: default 测试
- 补充 Anthropic output_config 带 effort 字段测试
- 补充 OpenAI reasoning_effort: low/high 测试
- 补充 Anthropic service_tier: standard_only 测试
- 修复流式响应 choices 数量验证逻辑,跳过空数组
2026-04-21 14:00:39 +08:00
..
2026-04-21 12:50:49 +08:00
2026-04-21 12:50:49 +08:00

兼容性检测脚本

概述

本目录包含一组用于检测 LLM API 网关对 OpenAIAnthropic 协议兼容性的测试脚本。通过向目标服务发送一系列结构化请求,验证响应格式、字段类型、错误处理等是否符合协议规范。

脚本结构

scripts/
├── core.py              # 公共基础设施
├── detect_openai.py     # OpenAI 兼容协议测试
└── detect_anthropic.py  # Anthropic 兼容协议测试

core.py — 公共模块

提供所有检测脚本共享的基础功能:

函数/类 说明
TestCase 测试用例数据类URL、方法、请求头、请求体、验证器
TestResult 测试结果数据类(状态码、耗时、错误类型、响应内容)
http_request() 普通 HTTP 请求(支持重试、自动 JSON 序列化)
http_stream_request() 流式 HTTP 请求SSE支持重试
create_ssl_context() 创建不验证证书的 SSL 上下文(测试环境用)
run_test() 执行单个用例并打印结构化输出
run_test_suite() 执行完整测试套件并打印统计摘要
check_required_fields() 检查必需字段(通用验证辅助)
check_field_type() 检查字段类型(通用验证辅助)
check_enum_value() 检查枚举值(通用验证辅助)
check_array_items_type() 检查数组元素类型(通用验证辅助)
validate_response_structure() 组合上述函数的通用验证器

注意core.py 只包含协议无关的通用功能。每个协议独有的响应验证函数应定义在各自的检测脚本中(如 validate_openai_chat_completion_responsedetect_openai.py 中)。

detect_openai.py — OpenAI 兼容测试

检测目标服务对 OpenAI Chat Completions API 的兼容程度。

覆盖的 API 端点:

  • GET /models — 模型列表
  • GET /models/{model} — 模型详情
  • POST /chat/completions — 对话补全

测试类别:

  • 正面用例基本对话、system/developer 角色、多轮对话、参数组合temperature、top_p、seed、penalty、stop、n、max_tokens、max_completion_tokens、logit_bias、reasoning_effort、service_tier、verbosity、response_format
  • 扩展功能--vision(图片输入)、--stream(流式响应)、--tools(工具调用)、--logprobs(对数概率)、json_schema(结构化输出)
  • 负面用例:缺参数、空消息、无效认证、不存在的模型、畸形 JSON、max_tokens 负数/0、temperature 越界

响应验证:

  • Models List检查 object: "list"data 数组中每个模型的 idobjectcreatedowned_by
  • Model Retrieve检查 idobject: "model"createdowned_by
  • Chat Completion检查 idobject: "chat.completion"createdmodelchoices 数组结构、usage 对象

detect_anthropic.py — Anthropic 兼容测试

检测目标服务对 Anthropic Messages API 的兼容程度。

覆盖的 API 端点:

  • GET /v1/models — 模型列表
  • GET /v1/models/{model} — 模型详情
  • POST /v1/messages — 消息对话
  • POST /v1/messages/count_tokens — Token 计数

测试类别:

  • 正面用例基本对话、system prompt字符串/数组格式、多轮对话、assistant prefill、content 数组格式、参数组合temperature、top_p、top_k、max_tokens、stop_sequences、metadata
  • 扩展功能--vision(图片输入)、--stream(流式响应)、--tools(工具调用)、--thinking(扩展思维)
  • 负面用例:缺 header、无效认证、缺参数、空消息、畸形 JSON、非法 role、max_tokens 负数/0、temperature 越界

响应验证:

  • Models List检查 datahas_more、每个模型的 idtype: "model"display_namecreated_at
  • Model Retrieve检查 idtype: "model"display_namecreated_at
  • Messages检查 idtype: "message"role: "assistant"content 数组、modelusage
  • Count Tokens检查 input_tokens 为数字

使用方式

基本用法

# OpenAI 兼容测试
python3 scripts/detect_openai.py --base_url http://localhost:9826/v1

# Anthropic 兼容测试
python3 scripts/detect_anthropic.py --base_url http://localhost:9826

带认证

python3 scripts/detect_openai.py --base_url http://localhost:9826/v1 --api_key sk-xxx --model gpt-4o

python3 scripts/detect_anthropic.py --base_url http://localhost:9826 --api_key sk-xxx --model claude-sonnet-4-5

扩展测试

# 开启所有扩展测试
python3 scripts/detect_openai.py --base_url http://localhost:9826/v1 --all

python3 scripts/detect_anthropic.py --base_url http://localhost:9826 --all

# 单独开启某项
python3 scripts/detect_openai.py --base_url http://localhost:9826/v1 --stream --tools

python3 scripts/detect_anthropic.py --base_url http://localhost:9826 --stream --tools --thinking

命令行参数

参数 说明 默认值
--base_url API 基础地址(必填)
--api_key API 密钥
--model 测试使用的模型名称 gpt-4o / claude-sonnet-4-5
--vision 执行视觉相关测试 关闭
--stream 执行流式响应测试 关闭
--tools 执行工具调用测试 关闭
--logprobs 执行 logprobs 测试(仅 OpenAI 关闭
--json_schema 执行 Structured Output 测试(仅 OpenAI 关闭
--thinking 执行扩展思维测试(仅 Anthropic 关闭
--all 开启所有扩展测试 关闭

输出示例

Anthropic 兼容性测试
目标: http://localhost:9826
模型: claude-sonnet-4-5
时间: 2026-04-21 10:30:00
用例: 35 个 | 扩展: stream, tools

[1/35] 获取模型列表 (GET /v1/models)

URL: GET http://localhost:9826/v1/models

Headers:
  x-api-key: sk-xxx
  anthropic-version: 2023-06-01

响应 (200, 0.12s):
{
  "data": [...],
  "has_more": false
}
✓ 响应验证通过

[5/35] 基本对话(仅 user

URL: POST http://localhost:9826/v1/messages

Headers:
  x-api-key: sk-xxx
  Content-Type: application/json

入参:
{
  "model": "claude-sonnet-4-5",
  "max_tokens": 5,
  "messages": [{"role": "user", "content": "Hi"}]
}

响应 (200, 0.23s):
{
  "id": "msg_xxx",
  "type": "message",
  "role": "assistant",
  "content": [...],
  "model": "claude-sonnet-4-5",
  "usage": {"input_tokens": 10, "output_tokens": 5}
}
✓ 响应验证通过

测试完成 | 总计: 35 | 成功: 33 | 客户端错误: 2 | 服务端错误: 0 | 网络错误: 0

测试设计原则

  1. 所有正面用例都启用响应验证器 — 任何响应结构偏差都会立即暴露,避免掩盖错误
  2. 负面用例覆盖常见错误场景 — 缺参数、类型错误、范围越界、认证失败
  3. 扩展功能通过 flag 按需开启 — 避免在基础测试中引入不必要的依赖
  4. 验证器基于协议规范编写 — 严格检查必需字段、类型、枚举值

新增检测脚本开发流程

如需为新的协议(如 Google Gemini、Cohere 等)开发检测脚本,遵循以下流程:

1. 在新脚本中定义协议专用的验证函数

每个协议的响应结构是独特的,验证函数应定义在各自的脚本中,不要放入 core.py。例如:

# 在 detect_gemini.py 中
def validate_gemini_generate_content_response(response_text: str) -> Tuple[bool, List[str]]:
    """验证 Gemini GenerateContent 响应"""
    errors = []
    try:
        data = json.loads(response_text)
    except json.JSONDecodeError as e:
        return False, [f"响应不是有效的JSON: {e}"]

    # 检查 Gemini 特有的字段
    required_fields = ["candidates", "usageMetadata"]
    for field in required_fields:
        if field not in data:
            errors.append(f"缺少必需字段: {field}")
    ...
    return len(errors) == 0, errors

2. 在 core.py 中只添加通用验证辅助

只有当多个协议都需要相同的验证逻辑时,才将函数提取到 core.py。目前已有的通用函数:

函数 说明
check_required_fields() 检查必需字段是否存在
check_field_type() 检查字段类型
check_enum_value() 检查枚举值
check_array_items_type() 检查数组元素类型
validate_response_structure() 组合上述函数的通用验证器

3. 创建检测脚本模板

#!/usr/bin/env python3
"""新协议兼容性接口测试脚本"""

import json
import argparse
from typing import Dict, List, Tuple, Any
from core import (
    create_ssl_context,
    TestCase,
    run_test_suite,
    validate_response_structure,
)

def build_headers(api_key: str) -> Dict[str, str]:
    """构建请求头"""
    ...

def validate_xxx_response(response_text: str) -> Tuple[bool, List[str]]:
    """验证响应结构(协议专用)"""
    ...

def main():
    parser = argparse.ArgumentParser(...)
    parser.add_argument("--base_url", required=True, ...)
    parser.add_argument("--api_key", default="", ...)
    parser.add_argument("--model", default="...", ...)
    parser.add_argument("--all", action="store_true", ...)
    args = parser.parse_args()

    cases: List[TestCase] = []

    # 正面用例(都添加 validator
    cases.append(TestCase(
        desc="...", method="...", url=..., headers=..., body=...,
        validator=validate_xxx_response
    ))

    # 负面用例(不添加 validator
    cases.append(TestCase(desc="...", method="...", url=..., headers=..., body=...))

    run_test_suite(cases=cases, ssl_ctx=ssl_ctx, title="...", base_url=..., model=..., flags=...)

if __name__ == "__main__":
    main()

关键要点

  • 协议专用验证函数放在各自的脚本中 — 不要污染 core.py
  • 只有多协议通用的验证逻辑才提取到 core.py — 遵循 DRY 原则但不过度抽象
  • 所有正面用例必须添加 validator — 确保响应结构正确
  • 负面用例不添加 validator — 预期返回错误响应
  • 扩展功能用 flag 控制 — 保持基础测试轻量
  • 遵循现有命名和代码风格 — 中文注释、类型注解、dataclass 使用

许可证

MIT