fix(models): support Zhipu OpenAI Responses model lists (#7330)

Zhipu's Responses endpoint (open.bigmodel.cn/api/v1) serves its model list in the Codex remote catalog shape (models[].slug) instead of the OpenAI-compatible data[].id shape, so the Codex form's model fetch came back empty. The parser now falls back to models[].slug when data is absent.
This commit is contained in:
雨奇晴好 authored and GitHub committed 2026-09-13 23:06:50 +08:00
1 parent 556bb2cab6
commit f49c7d68ad
3 files changed
+36 -11

No files matched your search

+2 -1
View File
@@ -1478,7 +1478,8 @@ pub fn run() {
commands::delete_profile,
commands::clear_current_profile,
commands::apply_profile,
// model list fetch (OpenAI-compatible /v1/models)
// Fetch OpenAI-compatible and Anthropic model lists. Response data structure:
// data[].id, data[]?.owned_by. Special: supports Zhipu OpenAI Responses models[].slug.
commands::fetch_models_for_config,
commands::get_opencode_models,
// ours: endpoint speed test + custom endpoint management
+27 -10
View File
@@ -18,10 +18,14 @@ pub struct FetchedModel {
pub owned_by: Option<String>,
}
/// OpenAI 兼容的 /v1/models 响应格式
/// 模型列表响应的兼容格式。
///
/// OpenAI 兼容接口和 Anthropic 接口使用 `data` 字段,智谱 OpenAI Responses
/// 接口使用 `models` 字段,此结构同时兼容这两种格式。
#[derive(Debug, Deserialize)]
struct ModelsResponse {
data: Option<Vec<ModelEntry>>,
models: Option<Vec<ZhipuModelEntry>>,
}
#[derive(Debug, Deserialize)]
@@ -30,6 +34,11 @@ struct ModelEntry {
owned_by: Option<String>,
}
#[derive(Debug, Deserialize)]
struct ZhipuModelEntry {
slug: String,
}
const FETCH_TIMEOUT_SECS: u64 = 15;
const MAX_REQUEST_HEADERS: usize = 64;
const MAX_HEADER_NAME_BYTES: usize = 256;
@@ -98,15 +107,23 @@ pub async fn fetch_models(
.await
.map_err(|e| format!("Failed to parse response: {e}"))?;
let mut models: Vec<FetchedModel> = resp
.data
.unwrap_or_default()
.into_iter()
.map(|m| FetchedModel {
id: m.id,
owned_by: m.owned_by,
})
.collect();
let mut models: Vec<FetchedModel> = if let Some(data) = resp.data {
data.into_iter()
.map(|m| FetchedModel {
id: m.id,
owned_by: m.owned_by,
})
.collect()
} else {
resp.models
.unwrap_or_default()
.into_iter()
.map(|m| FetchedModel {
id: m.slug,
owned_by: None,
})
.collect()
};
models.sort_by(|a, b| a.id.cmp(&b.id));
return Ok(models);
+7
View File
@@ -17,6 +17,13 @@ export interface ModelFetchOptions {
*
* 使用 OpenAI 兼容的 GET /v1/models 端点。优先用 `modelsUrl` 精确覆写;
* 否则后端会对 baseURL 生成候选列表并按序尝试(含"剥离 /anthropic 等兼容子路径"兜底)。
*
* 该接口可以获取数据格式为
* OpenAI Chat Completion {"data": [{"id": "xxx","owned_by": "xxx"}]} 文档:https://developers.openai.com/api/reference/resources/models/methods/list
* 和
* Anthropic兼容 {"data":[{"id":"xxx"}]} 文档:https://platform.claude.com/docs/en/api/models/list
* 这两种端点的模型列表
* 特别支持智谱 OpenAI Responses {"models":[{"slug":"xxx"}]} 端点:https://open.bigmodel.cn/api/v1/models
*/
export async function fetchModelsForConfig(
baseUrl: string,