fix(agent/sync): handle multiple /v1/models response formats

The upstream AI endpoint returns an OpenAI-compatible format, but the
response body parsing was fragile. Make it resilient:
1. Try standard OpenAI format: { "data": [{id}, ...] }
2. Try raw array: [{id}, ...]
3. Try alternate format: { "models": [{id}, ...] }
4. Log actual response body (first 500 chars) when all formats fail

Also adds a warning log with the raw response on parse failure so
future debugging is straightforward.
This commit is contained in:
ZhenYi 2026-04-26 16:26:57 +08:00
parent 30713786bf
commit a8e3b0f5a8

View File

@ -680,6 +680,10 @@ struct ModelEntry {
}
/// List accessible model IDs from the AI endpoint.
/// Supports multiple response formats:
/// - OpenAI: `{ "data": [{"id": "..."}, ...] }`
/// - Array: `[{"id": "..."}, ...]`
/// - Custom: `{ "models": [{"id": "..."}, ...] }`
async fn list_accessible_models(
client: &reqwest::Client,
base_url: &str,
@ -693,12 +697,35 @@ async fn list_accessible_models(
.await
.map_err(|e| AppError::InternalServerError(format!("failed to list models: {}", e)))?;
let body: ModelsListResponse = resp
.json()
let body = resp
.text()
.await
.map_err(|e| AppError::InternalServerError(format!("failed to parse models response: {}", e)))?;
.map_err(|e| AppError::InternalServerError(format!("failed to read models body: {}", e)))?;
Ok(body.data.into_iter().map(|m| m.id).collect())
// Try OpenAI format: { "data": [{"id": "..."}, ...] }
if let Ok(parsed) = serde_json::from_str::<ModelsListResponse>(&body) {
return Ok(parsed.data.into_iter().map(|m| m.id).collect());
}
// Try raw array: [{"id": "..."}, ...]
if let Ok(parsed) = serde_json::from_str::<Vec<ModelEntry>>(&body) {
return Ok(parsed.into_iter().map(|m| m.id).collect());
}
// Try { "models": [{"id": "..."}, ...] }
#[derive(Debug, Deserialize)]
struct ModelsAltResponse {
models: Vec<ModelEntry>,
}
if let Ok(parsed) = serde_json::from_str::<ModelsAltResponse>(&body) {
return Ok(parsed.models.into_iter().map(|m| m.id).collect());
}
tracing::warn!(body = %body.chars().take(500).collect::<String>(), "list_accessible_models: unknown response format");
Err(AppError::InternalServerError(format!(
"unexpected /v1/models response format (first 200 chars): {}",
body.chars().take(200).collect::<String>()
)))
}
impl AppService {