From 283835eb26789ad6fb54b54cf29afb48a0316cb2 Mon Sep 17 00:00:00 2001 From: ZhenYi <434836402@qq.com> Date: Sun, 26 Apr 2026 23:58:25 +0800 Subject: [PATCH] fix(agent/sync): avoid double /v1/ prefix in model sync URL When APP_AI_BASIC_URL already ends with /v1 (e.g. openrouter.ai/api/v1), appending /v1/models produces /v1/v1/models. Detect trailing /v1 and only append /models in that case. --- libs/agent/sync.rs | 7 ++++++- libs/service/agent/sync.rs | 40 +++++++++++++++++++++----------------- 2 files changed, 28 insertions(+), 19 deletions(-) diff --git a/libs/agent/sync.rs b/libs/agent/sync.rs index bdaf02f..49757af 100644 --- a/libs/agent/sync.rs +++ b/libs/agent/sync.rs @@ -19,7 +19,12 @@ pub async fn list_accessible_models( base_url: &str, api_key: &str, ) -> Result, AgentError> { - let url = format!("{}/v1/models", base_url.trim_end_matches('/')); + let base = base_url.trim_end_matches('/'); + let url = if base.ends_with("/v1") { + format!("{}/models", base) + } else { + format!("{}/v1/models", base) + }; let resp = client .get(&url) .header("Authorization", format!("Bearer {}", api_key)) diff --git a/libs/service/agent/sync.rs b/libs/service/agent/sync.rs index 60f4496..17b2acb 100644 --- a/libs/service/agent/sync.rs +++ b/libs/service/agent/sync.rs @@ -1,3 +1,4 @@ +#![allow(dead_code)] //! Synchronizes AI model metadata from the upstream AI endpoint //! (`GET /v1/models`) into the local database. //! @@ -495,10 +496,9 @@ async fn sync_models_from_upstream( pricing_created += 1; } - capabilities_created += - upsert_capabilities(db, version_record.id, &model) - .await - .unwrap_or(0); + capabilities_created += upsert_capabilities(db, version_record.id, &model) + .await + .unwrap_or(0); if upsert_parameter_profile(db, version_record.id, &model) .await @@ -526,7 +526,12 @@ async fn list_upstream_models( base_url: &str, api_key: &str, ) -> Result, AppError> { - let url = format!("{}/v1/models", base_url.trim_end_matches('/')); + let base = base_url.trim_end_matches('/'); + let url = if base.ends_with("/v1") { + format!("{}/models", base) + } else { + format!("{}/v1/models", base) + }; let resp = client .get(&url) .header("Authorization", format!("Bearer {}", api_key)) @@ -559,7 +564,9 @@ async fn list_upstream_models( ))) } -fn build_ai_client(config: &config::AppConfig) -> Result<(reqwest::Client, String, String), AppError> { +fn build_ai_client( + config: &config::AppConfig, +) -> Result<(reqwest::Client, String, String), AppError> { let api_key = config .ai_api_key() .map_err(|e| AppError::InternalServerError(format!("AI API key not configured: {}", e)))?; @@ -638,18 +645,15 @@ impl AppService { /// Perform a single sync pass. Errors are logged and silently swallowed /// so the periodic task never stops. - async fn sync_once( - db: &AppDatabase, - ai_api_key: Option, - ai_base_url: Option, - ) { - let (http_client, base_url, api_key) = match build_ai_client_from_parts(ai_api_key, ai_base_url) { - Ok(c) => c, - Err(msg) => { - tracing::warn!(error = %msg, "Model sync: AI client config error"); - return; - } - }; + async fn sync_once(db: &AppDatabase, ai_api_key: Option, ai_base_url: Option) { + let (http_client, base_url, api_key) = + match build_ai_client_from_parts(ai_api_key, ai_base_url) { + Ok(c) => c, + Err(msg) => { + tracing::warn!(error = %msg, "Model sync: AI client config error"); + return; + } + }; let upstream_models = match list_upstream_models(&http_client, &base_url, &api_key).await { Ok(models) => models,