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:
parent
30713786bf
commit
a8e3b0f5a8
@ -680,6 +680,10 @@ struct ModelEntry {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// List accessible model IDs from the AI endpoint.
|
/// 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(
|
async fn list_accessible_models(
|
||||||
client: &reqwest::Client,
|
client: &reqwest::Client,
|
||||||
base_url: &str,
|
base_url: &str,
|
||||||
@ -693,12 +697,35 @@ async fn list_accessible_models(
|
|||||||
.await
|
.await
|
||||||
.map_err(|e| AppError::InternalServerError(format!("failed to list models: {}", e)))?;
|
.map_err(|e| AppError::InternalServerError(format!("failed to list models: {}", e)))?;
|
||||||
|
|
||||||
let body: ModelsListResponse = resp
|
let body = resp
|
||||||
.json()
|
.text()
|
||||||
.await
|
.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 {
|
impl AppService {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user