在 Chatbox 里添加自定义 OpenAI 兼容服务后,最容易混在一起的其实是三个不同问题:点击Fetch后列表为空、手动新增模型后出现model_not_found、点击Check后又报图像或工具调用错误。它们不是同一层故障,也不应该靠反复换 Key 解决。
本文按 Chatbox 官方v1.22.1的当前源码说明真实调用链,并用一个只监听127.0.0.1的 Python 夹具复现成功和失败分支。先给结论:Fetch成功不只要求 HTTP 200,响应还必须有顶层data数组;真正用于请求的值是data[].id,不是你看到的友好名称。
先准备这组最小配置
进入 Chatbox 设置,添加自定义提供商并选择OpenAI API Compatible。当前界面会显示API Host、API Path、Model,模型区域有New、Reset、Fetch,配置满足条件后还可以使用Check。
如果服务使用标准/v1前缀,先把职责拆开:
| 字段 | 示例值 | 作用 |
|---|---|---|
| API Host | https://your-gateway.example/v1 | 模型列表和聊天请求的共同基址 |
| API Path | /chat/completions | 对话资源路径 |
| API Key | 当前服务要求的 Key | 只保存在客户端,不进入文章或截图 |
| Model ID | 从/models的data[].id复制 | 真正发送给服务端的模型值 |
先不要急着点Check。用下面的命令确认模型列表契约:
BASE_URL='https://your-gateway.example/v1'curl-sS-i"$BASE_URL/models"\-H"Authorization: Bearer$OPENAI_API_KEY"合格的最小响应应同时满足三项:状态码是 200、正文是 JSON、顶层存在非空data数组。例如:
{"object":"list","data":[{"id":"provider-model-exact-id","object":"model"}]}这里真正要复制的是provider-model-exact-id。不要把网站商品名、控制台展示名、自己起的中文别名或另一个客户端的模型名当成服务器 ID。
为什么 HTTP 200 仍然可能拉不到模型
Chatboxv1.22.1的模型拉取实现会请求:
<normalized API Host>/models拿到 JSON 后,它直接检查顶层data。没有data就抛出错误;存在data时,每一项的id被映射为 Chatbox 的modelId。因此下面这个响应虽然是 200,仍不满足当前契约:
{"object":"list","models":[{"name":"provider-model-exact-id"}]}这类情况常见于“接口看起来像 OpenAI,但模型列表字段并不兼容”的服务。此时不要把问题归因成网络失败。你需要查服务方文档,确认它是否提供 OpenAI 风格的/models;如果模型列表被禁用,只能使用 Chatbox 的New手动添加精确 ID。
Fetch、New、Check分别做什么
Fetch:读取服务器模型目录
它适合确认当前 Base URL、认证和模型列表数据契约是否同时成立。列表拉取失败时,先保存完整状态码和脱敏响应体,再判断是 401、404、200 但结构不兼容,还是网络错误。
New:手动保存一个模型 ID
New能绕过“服务没有开放/models”这一限制,但它不证明模型存在。手动输入后,Chatbox 只是把这个值加入当前提供商的模型列表。下一次真实请求仍会把该modelId发给服务端,所以大小写、连字符、日期后缀和区域后缀都必须逐字一致。
Check:执行能力测试,不是一次 ping
当前v1.22.1的检查流程先发送基础文本请求;基础请求成功后,还会继续尝试一张 1x1 图片和一次工具调用。也就是说,一次Check最多可能产生三次模型请求。
如果你的服务按请求计费、模型不支持图像或工具调用,后两项失败不等于基础文本模型不可用。为了把问题收窄,建议先用一个最小非流式文本请求确认精确 ID,再决定是否运行完整Check。
用最小聊天请求验证精确 ID
把模型 ID 从/models响应中复制出来:
MODEL_ID='provider-model-exact-id'curl-sS-i"$BASE_URL/chat/completions"\-H'Content-Type: application/json'\-H"Authorization: Bearer$OPENAI_API_KEY"\-d"{\"model\":\"$MODEL_ID\",\"messages\": [{\"role\":\"user\",\"content\":\"reply with OK\"}],\"stream\": false }"成功信号是 HTTP 200,并且响应中能读到choices[0].message.content。如果同一个请求把model换成友好名称后出现model_not_found,就已经把故障定位到模型值,而不是 Base URL、Key 或 Chatbox UI。
本地实测:200、结构错误、别名错误和精确 ID
本次夹具只承认两个模型 ID,并故意准备四条分支:
GET /v1/models -> 200 + data[].id GET /malformed/v1/models -> 200 + models[].name,无 data POST /v1/chat/completions + 别名 -> 404 model_not_found POST /v1/chat/completions + 精确ID -> 200 CHATBOX_MODEL_OK执行命令:
python3 06-evidence/probe_chatbox_model_fetch.py脱敏结果:
CHATBOX_VERSION=1.22.1 GOOD_MODELS_HTTP=200 GOOD_MODEL_IDS=fixture-chat-v2,fixture-reasoning-v1 MALFORMED_MODELS_HTTP=200 MALFORMED_HAS_DATA=NO WRONG_MODEL_HTTP=404 WRONG_MODEL_ERROR=model_not_found EXACT_MODEL_HTTP=200 EXACT_MODEL_TEXT=CHATBOX_MODEL_OK ONLINE_PROVIDER_REQUEST=NO FULL_CHATBOX_RUNTIME=NO这个实测证明的是 Chatbox 当前源码所需的数据形状,以及精确 ID 与友好名称在本地夹具中的差异。它没有启动完整 Chatbox,也没有访问真实模型服务,因此不能据此推断线上价格、延迟、稳定性或模型能力。
按信号判断下一步
| 观察结果 | 优先检查 | 不要先做 |
|---|---|---|
/models返回 401/403 | Key、权限、请求头 | 反复改模型名 |
/models返回 404 | API Host、版本前缀、服务是否提供模型目录 | 直接点完整Check |
/models返回 200,但没有data | 响应契约或服务方文档 | 把 200 写成“模型拉取成功” |
data[].id可见,但 Chatbox 列表为空 | 当前版本、保存回显、客户端日志 | 自己猜一个友好名称 |
| 精确 ID 的文本请求 200 | 再决定是否做视觉和工具测试 | 把后续能力失败归因成 Key 失效 |
友好名称报model_not_found | 逐字对比data[].id | 删除版本/区域后缀 |
保存后的复核清单
- 重新打开自定义提供商,确认 API Host 和 API Path 回显正确。
- 在模型列表中确认保存的是服务器精确 ID;昵称只用于显示。
- 先跑一个最小文本请求,记录状态码和可读正文。
- 需要能力测试时再点
Check,并区分基础、图像、工具三项结果。 - 截图和日志只保留状态码、路径、模型 ID 和错误类型,不保留 Authorization 值。
- 如果服务没有
/models,在记录中明确写“手动添加”,不要写成“自动拉取成功”。
适用边界
本文依据 Chatbox 官方v1.22.1和对应提交7450ab2d。后续版本可能调整按钮、测试顺序或数据适配;不同 OpenAI 兼容服务也可能隐藏模型列表、使用其他路径或返回不同错误码。最终以你当前版本的设置回显、服务方公开文档和实际脱敏请求为准。
不要把真实 Key 写进命令历史、文章、截图或录屏。示例中的域名和模型名都是占位符,本地夹具使用固定的fixture-only请求头且只监听回环地址。
总结
Chatbox 获取不到模型时,先把问题拆成“模型目录”“模型 ID”“能力检查”三层。Fetch要求/models返回顶层data,真正请求值来自data[].id;New只是手动保存,不代表服务端支持;Check在基础成功后还可能继续发送图像和工具调用请求。
最稳的顺序是:先验/models的状态码和结构,再复制精确 ID,最后只用一个最小文本请求确认 200。把这三步跑通后,再处理图像、工具调用或流式能力,定位会清楚得多。