Files
cloudpods/docs/aiproxy/functional-test.md
T
Zexi Li cdc8ab78b5 feat(aiproxy): move visual provider to model columns and support streaming responses (#25144)
Promote visual_provider_id/visual_model_key to dedicated ai_model columns,
force non-stream upstream orchestration for visual paths, and synthesize SSE
chunks for streaming Responses requests that include images.
2026-07-14 18:39:30 +08:00

20 KiB
Raw Blame History

aiproxy 功能测试(climc

本文用 climc 配置 aiproxy 资源,并通过 climc ai-test-* 子命令或 curl 做端到端验证。

子命令 数据面路径 用途
ai-test-chat POST /ai/openai/v1/chat/completions OpenAI 兼容 chat(非流式 + 流式)
ai-test-anthropic POST /ai/anthropic/v1/messages Anthropic Messages API
ai-test-provider-create 创建自定义 ai_provider 并校验
ai-codex-config GET /ai/openai/v1/modelsPOST /ai/openai/v1/responses 生成 Codex CLI 的 config.toml / models_catalog.json / aiproxy.env

安全:请勿将上游 API Key 写入文档或提交到 Git。使用环境变量传入;若 Key 曾泄露,请到对应云平台控制台轮换。

前置条件

说明
服务 aiproxy 主节点已部署,Keystone 中已注册 aiproxy 服务及 public endpoint
数据库 主节点已执行 InitDB(初始化表结构;不再自动插入内置 SaaS ai_provider
客户端 source /etc/yunion/rcadmin(或等价 rc 文件),climc 能正常 list
网络 aiproxy 节点能访问目标上游(DashScope、MiMo、Anthropic 等)

一键 E2E(交互式,推荐)

从已有 ai_provider 列表选择 模型提供商model_key(或命令行指定 --provider / --model),终端输入 API Key(或使用环境变量跳过输入),自动完成 ai_key / ai_virtual_key / ai_routing 配置及 chat 校验:

source /etc/yunion/rcadmin
climc ai-test-chat

非交互(CI):

export AIPROXY_TEST_NONINTERACTIVE=1
export AIPROXY_TEST_PROVIDER=aliyun
export AIPROXY_TEST_MODEL=qwen-turbo
export AIPROXY_TEST_API_KEY='...'
export AIPROXY_TEST_SKIP_STREAM=1   # 可选,跳过流式
climc ai-test-chat

环境变量

变量 说明
AIPROXY_TEST_PROVIDER provider_key(如 aliyunxiaomi
AIPROXY_TEST_MODEL model_key(如 qwen-turbo
AIPROXY_TEST_API_KEY 上游 API Key(通用)
AIPROXY_FT_* 同上(兼容旧变量名)
DASHSCOPE_API_KEY 通义千问(provider=aliyun
MIMO_API_KEY 小米 MiMoprovider=xiaomi
MOONSHOT_API_KEY Moonshot / Kimiprovider=moonshot
ZHIPU_API_KEY / ZAI_API_KEY Z.AI / 智谱(provider=zhipu
ANTHROPIC_API_KEY Anthropic 直通
DEEPSEEK_API_KEY DeepSeekAnthropic 兼容场景)
AIPROXY_TEST_SKIP_STREAM 1 跳过流式;0 强制流式
AIPROXY_TEST_KEEP_RESOURCES 1 测试结束后保留本次创建的资源(默认自动清理)
AIPROXY_URL 留空则从 endpoint-list 解析

ai-test-* 会在测试过程中自动创建缺失的依赖(ai_modelai_keyai_virtual_keyai_routing 等),不会自动创建 ai_provider;交互式 ai-test-chat 需环境中至少有一个 ai_provider,非交互请先用 climc ai-provider-create 创建对应 provider_key 的供应商(或使用 ai-test-provider-create)。测试结束(成功或失败)后自动删除本次创建的资源。临时修改的 ai_provider.config.base_url 会还原。仅删除本次新建项,测试前已存在的同名资源不会被删。

保留资源以便排查:climc ai-test-chat --keep-resourcesexport AIPROXY_TEST_KEEP_RESOURCES=1

ai-test-chat 按 provider 自动命名资源(可用 --key-name--vk-name--routing-name 覆盖),默认形如 aiproxy-test-{provider}

按模型提供商快速开始

通义千问(DashScope / aliyun

需存在 provider_key=aliyunai_provider(可 climc ai-provider-create);ai-test-chat 会按需创建 ai_model 等测试资源。上游 https://dashscope.aliyuncs.com/compatible-mode

export DASHSCOPE_API_KEY='你的 DashScope API Key'
climc ai-test-chat --provider aliyun --model qwen-turbo --api-key "$DASHSCOPE_API_KEY"

小米 MiMoxiaomi

需存在 provider_key=xiaomiai_provider;上游 https://api.xiaomimimo.com

export MIMO_API_KEY='你的 MiMo API Key'
climc ai-test-chat --provider xiaomi --model mimo-v2-flash --api-key "$MIMO_API_KEY"

其它 catalog 模型:mimo-v2.5-promimo-v2-promimo-v2.5mimo-v2-omniid 形如 xiaomi-mimo-v2.5-pro)。

export AIPROXY_TEST_PROVIDER=xiaomi AIPROXY_TEST_MODEL=mimo-v2.5-pro
climc ai-test-chat --api-key "$MIMO_API_KEY"

MiMo 与 DashScope 测试应使用独立的 vk/routing/key 名称,避免混用同一 routing 的 model 列表。

Moonshot / Kimimoonshot

provider_key 固定为 moonshot;通过 config.base_url 区分国内/国际(控制台创建时选择区域会自动写入):

区域 config.base_url
国内 https://api.moonshot.cn
国际 https://api.moonshot.ai

上游 OpenAI 兼容(SDK base_urlhttps://api.moonshot.cn/v1https://api.moonshot.ai/v1)。未设置 base_url 时默认国内。

export MOONSHOT_API_KEY='你的 Moonshot API Key'
climc ai-test-chat --provider moonshot --model kimi-k2.6 --api-key "$MOONSHOT_API_KEY"
# 国际区(临时覆盖 base_url):
climc ai-test-chat --provider moonshot --model kimi-k2.6 --api-key "$MOONSHOT_API_KEY" --base-url https://api.moonshot.ai

其它 catalog 模型:kimi-k2.7-codekimi-k2.5moonshot-v1-8k 等(id 形如 moonshot-kimi-k2.6)。

Z.AI / 智谱(zhipu

控制台展示为 Z.AIprovider_key 固定为 zhipu。上游 OpenAI 兼容 base 为 https://open.bigmodel.cn/api/paas/v4Anthropic 兼容 base 为 https://open.bigmodel.cn/api/anthropic。支持 config.api_mode=openai|anthropic(与 DeepSeek 类似的双 API 模式)。

OpenAI 兼容(默认 api_mode=openai

export ZHIPU_API_KEY='你的智谱 API Key'
climc ai-test-chat --provider zhipu --model glm-5.2 --api-key "$ZHIPU_API_KEY"

Anthropic 兼容(config.api_mode=anthropicaiproxy 将 Anthropic SDK 请求直通 https://open.bigmodel.cn/api/anthropic/v1/messagesbase_url 可仍填 OpenAI 默认,由 aiproxy 自动切换)。

climc ai-test-anthropic --provider zhipu --model glm-5.2 \
  --api-key "$ZHIPU_API_KEY" --upstream-base-url https://open.bigmodel.cn/api/anthropic

创建 provider 示例:

{
  "generate_name": "my-zhipu",
  "provider_key": "zhipu",
  "secret": "<zhipu-api-key>",
  "config": {
    "base_url": "https://open.bigmodel.cn/api/paas/v4",
    "api_mode": "openai"
  }
}

其它 catalog 模型:glm-5.1glm-5-turboglm-4.7glm-4.7-flash 等(id 形如 zhipu-glm-5.2)。

非交互:

export AIPROXY_TEST_PROVIDER=zhipu AIPROXY_TEST_MODEL=glm-5.2
export AIPROXY_TEST_API_KEY="$ZHIPU_API_KEY"
climc ai-test-chat

Anthropic Messages API

数据面 POST /ai/anthropic/v1/messages,认证为 Authorization: Bearer <virtual_key>不是上游 Anthropic/DeepSeek API Key)。

Claude Code / Anthropic SDK 在正式请求前会对 base URL 发 HEAD /ai/anthropic/ 做连通性探测;aiproxy 已返回 204。对 HEAD /ai/anthropic/v1/messages 无 virtual key 时返回 401(表示路由存在、需鉴权)。

Anthropic 直通catalog provider_key=anthropic):

export ANTHROPIC_API_KEY='sk-ant-...'
climc ai-test-anthropic --provider anthropic --model claude-sonnet-4-5 --api-key "$ANTHROPIC_API_KEY"

OpenAI 兼容后端(DeepSeek,翻译模式)config.api_mode=openai(默认);客户端仍用 Anthropic SDKaiproxy 转换为 OpenAI chat/completions 转发。

资源 示例
ai_provider.provider_key deepseekopenai
ai_provider.config.base_url https://api.deepseek.com
ai_provider.config.api_mode openai(可省略)
ai_model.model_key deepseek-chat
export DEEPSEEK_API_KEY='...'
climc ai-test-anthropic --provider deepseek --model deepseek-chat \
  --api-key "$DEEPSEEK_API_KEY" --upstream-base-url https://api.deepseek.com

DeepSeek 原生 Anthropic 模式provider_key=deepseekconfig.api_mode=anthropicaiproxy 将 Anthropic SDK 请求直通 DeepSeek https://api.deepseek.com/anthropic/v1/messagesbase_url 可仍填 https://api.deepseek.com,由 aiproxy 自动补 /anthropic)。

创建 provider 时在顶层 secret 写入上游密钥(PostCreate 自动创建关联 ai_key);config 仅保留 base_url / api_mode

{
  "generate_name": "my-deepseek",
  "provider_key": "deepseek",
  "secret": "<deepseek-api-key>",
  "config": {
    "base_url": "https://api.deepseek.com",
    "api_mode": "anthropic"
  }
}

config.api_key 已不再支持;请在「供应商密钥」Tab 或独立 ai_key 资源中管理密钥。

OpenAI SDK 经 /ai/openai/v1/chat/completions 访问同一 provider 时,也会按 api_mode=anthropic 转为 Anthropic Messages 上游。

Anthropic SDK / Claude Code 配置(base_url 指向 aiproxy不要/v1api_keyvirtual_key):

import anthropic
client = anthropic.Anthropic(
    base_url=f"{AIPROXY_URL}/ai/anthropic",  # 正确:SDK 自行拼 /v1/messages
    api_key=VIRTUAL_KEY,                     # aiproxy virtual_key,不是上游 Key
)
client.messages.create(model="claude-sonnet-4-5", max_tokens=128, messages=[...])

环境变量等价配置:

export ANTHROPIC_BASE_URL="${AIPROXY_URL}/ai/anthropic"   # 勿写成 .../ai/anthropic/v1
export ANTHROPIC_API_KEY="${VIRTUAL_KEY}"
配置项 正确 错误
ANTHROPIC_BASE_URL https://host/ai/anthropic .../ai/anthropic/v1(会变成 /v1/v1/messages
API Key aiproxy virtual_key 上游 Anthropic / DeepSeek key

Codex CLI 接入(ai-codex-config

Codex CLI 经 OpenAI Responses API 访问模型。climc ai-codex-config 根据已有 ai_virtual_key 生成 Codex 配置(对齐 moon-bridge-print-codex-config 方式):config.tomlmodels_catalog.jsonaiproxy.envbase_url 指向 {网关根 URL}/ai/openai/v1(指定 --routing 时优先使用该 route 绑定 ai_proxy_nodeaccess_address 作为网关根 URL),wire_api = "responses"。Codex 通过环境变量 OPENAI_API_KEY 携带 virtual_key(不是上游 Key,也不使用 auth.json),启动前需 source aiproxy.env

ai-test-chat 的关系:ai-test-chat 验证 chat completions 链路;ai-codex-config 为 Codex 客户端生成 Responses API 配置。二者共用同一套 ai_virtual_key / ai_routing 资源。

参数

参数 说明
--virtual-key 必填ai_virtual_key 名称或 id
--model 客户端 model id(扁平或 route/catalog);省略时从 --routingmodel_keyGET /ai/openai/v1/models 推断
--routing 可选,ai_routing 名称/id;指定后 base_url 优先取该 route 绑定 ai_proxy_node.access_address,且 models_catalog.json 仅含该 route 模型(与 GET /models 结果取交集)
--aiproxy-url 可选,网关根 URL 覆盖;未指定且带 --routing 时用 route 接入地址,否则 AIPROXY_URLendpoint-list
--codex-home 可选,写入 config.tomlmodels_catalog.jsonaiproxy.envaiproxy.env 权限 0600);建议使用独立目录(如 $HOME/.codex-aiproxy),避免覆盖已有 ~/.codex
--list-models 仅列出该 vk 可见的 model id 后退出
--provider-name config.tomlmodel_provider 段名,默认 aiproxy

指定 --routing 时,models_catalog.json 的模型列表为 GET /ai/openai/v1/modelsvirtual key 可见)与该 route 绑定 catalog 推导出的 client model id 的交集。可用 climc ai-model-list --ai-routing-id <routing> 查看该 route 绑定的 catalog 模型。

示例

打印到 stdout(默认):

climc ai-codex-config --virtual-key aiproxy-test-aliyun-vk --model qwen-turbo

列出该 vk 可见模型:

climc ai-codex-config --virtual-key aiproxy-test-aliyun-vk --list-models

写入独立目录后启动 Codex(不覆盖已有 ~/.codex):

# 1. 指定独立配置目录(默认 ~/.codex-aiproxy,不覆盖 ~/.codex
CODEX_HOME_DIR="${CODEX_HOME_DIR:-$HOME/.codex-aiproxy}"

# 2. 创建目录;若失败,后续写入 config.toml 会报错
mkdir -p "$CODEX_HOME_DIR"

# 3. 生成 config.toml、models_catalog.json、aiproxy.env(需已 source rc 且 climc 可用)
climc ai-codex-config \
  --virtual-key aiproxy-test-aliyun-vk \
  --routing aiproxy-test-aliyun-routing \
  --codex-home "$CODEX_HOME_DIR"

# 4. 加载 OPENAI_API_KEYvirtual_key)并以独立 CODEX_HOME 启动 Codex,工作目录为当前项目
source "$CODEX_HOME_DIR/aiproxy.env" && CODEX_HOME="$CODEX_HOME_DIR" codex --cd "$PWD"

生成内容示例:

config.toml

model = "qwen-turbo"
model_provider = "aiproxy"
model_catalog_json = "/home/user/.codex-aiproxy/models_catalog.json"

[model_providers.aiproxy]
name = "Cloudpods AI Gateway"
base_url = "https://<host>/ai/openai/v1"
wire_api = "responses"
env_key = "OPENAI_API_KEY"
env_key_instructions = "Run: source /home/user/.codex-aiproxy/aiproxy.env"

[mcp_servers.deepwiki]
url = "https://mcp.deepwiki.com/mcp"
startup_timeout_sec = 3600
tool_timeout_sec = 3600

models_catalog.json(节选):包含该 virtual key 可见模型的 Codex 元数据(base_instructionstruncation_policyshell_typeinput_modalities 等),避免 Codex 回退到内置 preset。若绑定 ai_model 已启用 Visualvisual_activeconfig.extensions.visual.enabled + visual_provider_id + visual_model_key),对应条目会写入 input_modalities: ["text","image"],使 Codex 按多模态发图。

aiproxy.env

export OPENAI_API_KEY="<virtual_key>"

测试流程概览

flowchart LR
  VK[ai_virtual_key] --> RT[ai_routing]
  RT --> RM[ai_routing_model]
  RM --> P[ai_provider]
  RM --> M[ai_model]
  P --> K[ai_key secret]
  K --> UP[上游 API]

ai_provider 创建测试

自定义供应商(provider_key=custom

用户自建网关,需填写完整 base_url、顶层 secretapi_modeopenai / anthropic):

{
  "generate_name": "my-gateway",
  "provider_key": "custom",
  "secret": "sk-xxx",
  "config": {
    "base_url": "https://llm.example.com/v1",
    "api_mode": "openai"
  }
}

Anthropic Messages 上游示例:

{
  "generate_name": "my-anthropic-gateway",
  "provider_key": "custom",
  "secret": "sk-ant-xxx",
  "config": {
    "base_url": "https://llm.example.com/anthropic",
    "api_mode": "anthropic"
  }
}

创建后不会自动注入 catalog 模型;须手动创建 ai_model 并配置路由。

自托管 provider

climc ai-test-provider-create

非交互示例:

export AIPROXY_PROVIDER_TEST_NONINTERACTIVE=1
climc ai-test-provider-create \
  --name my-vllm --provider-key my-vllm \
  --base-url http://127.0.0.1:8000/v1 --enabled

provider_key 须全局唯一。完整 config 可用 --config '{"base_url":"..."}'AIPROXY_PROVIDER_TEST_CONFIG

ai_proxy_node(多副本 / 路由绑定)

climc ai-proxy-node-list
climc ai-proxy-node-show primary
climc ai-proxy-node-register --address https://standby-host:30938 --hb-timeout 120

ai_routing 绑定到指定节点(chat 须走该节点 public endpoint):

climc ai-routing-update aiproxy-test-routing --ai-proxy-node-id primary

创建 ai_routing 时若省略 --ai-proxy-node-id,默认绑定 primary 节点。

手动步骤(以 aliyun / qwen-turbo 为例)

以下步骤与 climc ai-test-chat 等价,便于理解各资源关系;其它 provider 替换 aliyunqwen-turbo 及对应 API Key 即可。

1. 检查 Keystone endpoint

climc endpoint-list --service aiproxy --interface public

2. 检查 catalog

climc ai-provider-show aliyun
climc ai-model-show aliyun-qwen-turbo

小米 MiMoclimc ai-provider-show xiaomiclimc ai-model-show xiaomi-mimo-v2-flash

3. 注册上游 API Keyai_key

climc ai-key-create qwen-dashscope-test \
  --ai-provider-id aliyun \
  --secret "${DASHSCOPE_API_KEY}" \
  --weight 10 \
  --enabled

ai_key 默认 disabled,创建时需 --enabled

4. 创建 Virtual Key

climc ai-virtual-key-create aiproxy-test-vk
climc ai-virtual-key-show aiproxy-test-vk

Virtual key 归属当前 climc 用户的 项目ai_routing 须在同一项目(或共享到该项目)下。

5. 创建项目路由

climc ai-routing-create aiproxy-test-routing \
  --priority 10 \
  --model-key qwen-turbo \
  --models '[{"ai_provider_id":"aliyun","ai_model_id":"qwen-turbo","priority":1}]'

6. Chat completionscurl

AIPROXY_URL="${AIPROXY_URL:-$(climc endpoint-list --service aiproxy --interface public --limit 1 \
  --output-format json | jq -r '.data[0].url // empty')}"

VK="$(climc ai-virtual-key-show aiproxy-test-vk --output-format json | jq -r '.virtual_key')"

curl -k -sS "${AIPROXY_URL%/}/ai/openai/v1/chat/completions" \
  -H "Authorization: Bearer ${VK}" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "qwen-turbo",
    "messages": [{"role": "user", "content": "用一句话介绍通义千问"}],
    "max_tokens": 128
  }' | jq .

期望HTTP 200JSON 含 choices[0].message.contentusage

6b. 流式 Chat

climc ai-test-chat 默认在非流式成功后继续流式校验。跳过:climc ai-test-chat --skip-stream

curl -k -sS -N "${AIPROXY_URL%/}/ai/openai/v1/chat/completions" \
  -H "Authorization: Bearer ${VK}" \
  -H "Content-Type: application/json" \
  -d '{"model":"qwen-turbo","stream":true,"messages":[{"role":"user","content":"hi"}],"max_tokens":64}'

Anthropic 流式与非流式均走 /ai/anthropic/v1/messages,请求体设置 "stream": true 即可。

负向用例(可选)

场景 操作 期望
错误 virtual key Authorization: Bearer sk-invalid 4xx
无路由 disable 或删除 routing 后再 chat 404
禁用 virtual key climc ai-virtual-key-disable aiproxy-test-vk 4xx
provider 限制 vk --limits '{"allowed_ai_provider_ids":["openai"]}' 4xx

清理

ai-test-* 默认在结束时自动清理(见上文 AIPROXY_TEST_KEEP_RESOURCES)。手动清理示例(仅在使用 --keep-resources 或清理失败时需要):

DashScope

climc ai-routing-delete aiproxy-test-aliyun-routing
climc ai-virtual-key-delete aiproxy-test-aliyun-vk
climc ai-key-delete aiproxy-test-aliyun

MiMo 示例(若使用独立资源名):

climc ai-routing-delete aiproxy-test-xiaomi-routing
climc ai-virtual-key-delete aiproxy-test-xiaomi-vk
climc ai-key-delete aiproxy-test-xiaomi

常见问题

no ai_routing matched for virtual key project
Virtual key 与 routing 的项目不一致,或 routing 未 enabled、未共享到该项目。

add an enabled ai_key with secret for this provider
未创建启用的 ai_key,或密钥为空。创建 provider 时使用顶层 secret,或在「供应商密钥」Tab 手动添加。

DashScope / MiMo 401/403
检查对应环境变量中的 API Key 是否有效、模型是否已开通。

多副本 ai_routing 绑定其它节点
若 routing 指定了 ai_proxy_node_id,须访问该节点的 public endpoint,或去掉绑定。

MiMo 与 DashScope 资源冲突
各 provider 使用独立的 vk/routing/key 名称,勿共用同一 routing 的 model 列表。