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.
This commit is contained in:
ZhenYi 2026-04-26 23:58:25 +08:00
parent c7a8bc0458
commit 283835eb26
2 changed files with 28 additions and 19 deletions

View File

@ -19,7 +19,12 @@ pub async fn list_accessible_models(
base_url: &str,
api_key: &str,
) -> Result<std::collections::HashSet<String>, 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))

View File

@ -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<Vec<UpstreamModel>, 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<String>,
ai_base_url: Option<String>,
) {
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<String>, ai_base_url: Option<String>) {
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,