From a8e3b0f5a81c6a6052ce651b17acbdf7bffd3d3d Mon Sep 17 00:00:00 2001 From: ZhenYi <434836402@qq.com> Date: Sun, 26 Apr 2026 16:26:57 +0800 Subject: [PATCH] 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. --- libs/service/agent/sync.rs | 35 +++++++++++++++++++++++++++++++---- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/libs/service/agent/sync.rs b/libs/service/agent/sync.rs index db92a58..020a903 100644 --- a/libs/service/agent/sync.rs +++ b/libs/service/agent/sync.rs @@ -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::(&body) { + return Ok(parsed.data.into_iter().map(|m| m.id).collect()); + } + + // Try raw array: [{"id": "..."}, ...] + if let Ok(parsed) = serde_json::from_str::>(&body) { + return Ok(parsed.into_iter().map(|m| m.id).collect()); + } + + // Try { "models": [{"id": "..."}, ...] } + #[derive(Debug, Deserialize)] + struct ModelsAltResponse { + models: Vec, + } + if let Ok(parsed) = serde_json::from_str::(&body) { + return Ok(parsed.models.into_iter().map(|m| m.id).collect()); + } + + tracing::warn!(body = %body.chars().take(500).collect::(), "list_accessible_models: unknown response format"); + Err(AppError::InternalServerError(format!( + "unexpected /v1/models response format (first 200 chars): {}", + body.chars().take(200).collect::() + ))) } impl AppService {