feat(agent/sync): sync non-OpenRouter models from upstream endpoint
When upstream /v1/models returns models not yet in OpenRouter's catalog (e.g. brand-new models like DeepSeek-V4), also upsert them through the same pipeline (provider → model → version → pricing → capabilities → parameter_profile) with inferred defaults, instead of silently dropping them. Previously the direct-sync fallback only triggered when *zero* OpenRouter matches existed.
This commit is contained in:
parent
27cd4ea83c
commit
638dfd7a6e
@ -5,7 +5,10 @@
|
|||||||
//! 2. Fetch full metadata (pricing, context_length, capabilities) for those
|
//! 2. Fetch full metadata (pricing, context_length, capabilities) for those
|
||||||
//! model IDs from OpenRouter's public `/api/v1/models` endpoint (no auth).
|
//! model IDs from OpenRouter's public `/api/v1/models` endpoint (no auth).
|
||||||
//! 3. Upsert provider / model / version / pricing / capability / profile
|
//! 3. Upsert provider / model / version / pricing / capability / profile
|
||||||
//! records only for models the client can actually call.
|
//! records for models the client can actually call.
|
||||||
|
//! 4. Models accessible from the user's endpoint but NOT in OpenRouter's
|
||||||
|
//! catalog ("stranger" models) are also upserted through the same
|
||||||
|
//! pipeline with inferred defaults (direct sync).
|
||||||
//!
|
//!
|
||||||
//! Usage: call `start_sync_task()` to launch a background task that syncs
|
//! Usage: call `start_sync_task()` to launch a background task that syncs
|
||||||
//! immediately and then every 10 minutes. On app startup, run it once
|
//! immediately and then every 10 minutes. On app startup, run it once
|
||||||
@ -704,7 +707,11 @@ impl AppService {
|
|||||||
/// Steps:
|
/// Steps:
|
||||||
/// 1. Call `client.models().list()` to get the set of accessible model IDs.
|
/// 1. Call `client.models().list()` to get the set of accessible model IDs.
|
||||||
/// 2. Fetch full model list from OpenRouter's public `/api/v1/models` endpoint.
|
/// 2. Fetch full model list from OpenRouter's public `/api/v1/models` endpoint.
|
||||||
/// 3. Keep only models whose ID appears in the accessible set, then upsert.
|
/// 3. Keep only models whose ID appears in the accessible set, then upsert
|
||||||
|
/// with full OpenRouter metadata.
|
||||||
|
/// 4. Models NOT in OpenRouter's catalog are also upserted through the
|
||||||
|
/// same pipeline (provider → model → version → pricing → capabilities
|
||||||
|
/// → parameter_profile) with inferred defaults.
|
||||||
pub async fn sync_upstream_models(
|
pub async fn sync_upstream_models(
|
||||||
&self,
|
&self,
|
||||||
_ctx: &Session,
|
_ctx: &Session,
|
||||||
@ -733,17 +740,21 @@ impl AppService {
|
|||||||
.filter(|m| m.id != "openrouter/auto")
|
.filter(|m| m.id != "openrouter/auto")
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let filtered_count = filtered.len();
|
// Identify "stranger" models: accessible from the user's endpoint but
|
||||||
|
// NOT present in OpenRouter's public catalog. These are also upserted
|
||||||
|
// through the same pipeline (provider → model → version → pricing →
|
||||||
|
// capabilities → parameter_profile) with inferred defaults.
|
||||||
|
let or_matched_ids: std::collections::HashSet<&str> = filtered
|
||||||
|
.iter()
|
||||||
|
.map(|m| m.id.as_str())
|
||||||
|
.collect();
|
||||||
|
let unknown_ids: Vec<&str> = available_ids
|
||||||
|
.iter()
|
||||||
|
.filter(|id| !or_matched_ids.contains(id.as_str()))
|
||||||
|
.map(|s| s.as_str())
|
||||||
|
.collect();
|
||||||
|
|
||||||
// Fallback: if no OpenRouter metadata matches, sync models directly from
|
let filtered_count = filtered.len();
|
||||||
// the user's endpoint (handles Bailian/MiniMax and other non-OpenRouter providers).
|
|
||||||
if filtered_count == 0 && !available_ids.is_empty() {
|
|
||||||
tracing::info!(
|
|
||||||
model_count = available_ids.len(),
|
|
||||||
"sync_upstream_models: no OpenRouter matches, falling back to direct sync"
|
|
||||||
);
|
|
||||||
return Ok(sync_models_direct(&self.db, &available_ids).await);
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut models_created = 0i64;
|
let mut models_created = 0i64;
|
||||||
let mut models_updated = 0i64;
|
let mut models_updated = 0i64;
|
||||||
@ -752,6 +763,37 @@ impl AppService {
|
|||||||
let mut capabilities_created = 0i64;
|
let mut capabilities_created = 0i64;
|
||||||
let mut profiles_created = 0i64;
|
let mut profiles_created = 0i64;
|
||||||
|
|
||||||
|
// Sync stranger models (non-OpenRouter) through the direct pipeline.
|
||||||
|
if !unknown_ids.is_empty() {
|
||||||
|
tracing::info!(
|
||||||
|
unknown_count = unknown_ids.len(),
|
||||||
|
"sync_upstream_models: {} models not in OpenRouter catalog, syncing directly",
|
||||||
|
unknown_ids.len()
|
||||||
|
);
|
||||||
|
let unknown_set: std::collections::HashSet<String> =
|
||||||
|
unknown_ids.iter().map(|s| ToString::to_string(s)).collect();
|
||||||
|
let direct_result = sync_models_direct(&self.db, &unknown_set).await;
|
||||||
|
models_created += direct_result.models_created;
|
||||||
|
models_updated += direct_result.models_updated;
|
||||||
|
versions_created += direct_result.versions_created;
|
||||||
|
pricing_created += direct_result.pricing_created;
|
||||||
|
capabilities_created += direct_result.capabilities_created;
|
||||||
|
profiles_created += direct_result.profiles_created;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If no OpenRouter metadata matched at all, the direct sync above
|
||||||
|
// already handled everything — return early.
|
||||||
|
if filtered_count == 0 {
|
||||||
|
return Ok(SyncModelsResponse {
|
||||||
|
models_created,
|
||||||
|
models_updated,
|
||||||
|
versions_created,
|
||||||
|
pricing_created,
|
||||||
|
capabilities_created,
|
||||||
|
profiles_created,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
for or_model in filtered {
|
for or_model in filtered {
|
||||||
let provider_slug = extract_provider(&or_model.id);
|
let provider_slug = extract_provider(&or_model.id);
|
||||||
let provider = match upsert_provider(&self.db, provider_slug).await {
|
let provider = match upsert_provider(&self.db, provider_slug).await {
|
||||||
@ -823,8 +865,10 @@ impl AppService {
|
|||||||
filtered_count,
|
filtered_count,
|
||||||
models_created,
|
models_created,
|
||||||
models_updated,
|
models_updated,
|
||||||
"sync_upstream_models: synced {} accessible models",
|
"sync_upstream_models: synced {} accessible models ({}) OpenRouter + ({}) direct",
|
||||||
filtered_count
|
filtered_count + unknown_ids.len(),
|
||||||
|
filtered_count,
|
||||||
|
unknown_ids.len()
|
||||||
);
|
);
|
||||||
|
|
||||||
Ok(SyncModelsResponse {
|
Ok(SyncModelsResponse {
|
||||||
@ -898,19 +942,21 @@ impl AppService {
|
|||||||
.filter(|m| m.id != "openrouter/auto")
|
.filter(|m| m.id != "openrouter/auto")
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
|
// Identify "stranger" models: accessible from the user's endpoint but
|
||||||
|
// NOT present in OpenRouter's public catalog.
|
||||||
|
let or_matched_ids: std::collections::HashSet<&str> = filtered
|
||||||
|
.iter()
|
||||||
|
.map(|m| m.id.as_str())
|
||||||
|
.collect();
|
||||||
|
let unknown_ids: Vec<&str> = available_ids
|
||||||
|
.iter()
|
||||||
|
.filter(|id| !or_matched_ids.contains(id.as_str()))
|
||||||
|
.map(|s| s.as_str())
|
||||||
|
.collect();
|
||||||
|
|
||||||
let filtered_count = filtered.len();
|
let filtered_count = filtered.len();
|
||||||
|
|
||||||
// Fallback: if no OpenRouter metadata matches, sync models directly from
|
// Sync stranger models (non-OpenRouter) through the direct pipeline.
|
||||||
// the user's endpoint (handles Bailian/MiniMax and other non-OpenRouter providers).
|
|
||||||
if filtered_count == 0 && !available_ids.is_empty() {
|
|
||||||
tracing::info!(
|
|
||||||
model_count = available_ids.len(),
|
|
||||||
"OpenRouter model sync: no matches, falling back to direct sync"
|
|
||||||
);
|
|
||||||
sync_models_direct(db, &available_ids).await;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut models_created = 0i64;
|
let mut models_created = 0i64;
|
||||||
let mut models_updated = 0i64;
|
let mut models_updated = 0i64;
|
||||||
let mut versions_created = 0i64;
|
let mut versions_created = 0i64;
|
||||||
@ -918,6 +964,39 @@ impl AppService {
|
|||||||
let mut capabilities_created = 0i64;
|
let mut capabilities_created = 0i64;
|
||||||
let mut profiles_created = 0i64;
|
let mut profiles_created = 0i64;
|
||||||
|
|
||||||
|
if !unknown_ids.is_empty() {
|
||||||
|
tracing::info!(
|
||||||
|
unknown_count = unknown_ids.len(),
|
||||||
|
"OpenRouter model sync: {} models not in OpenRouter catalog, syncing directly",
|
||||||
|
unknown_ids.len()
|
||||||
|
);
|
||||||
|
let unknown_set: std::collections::HashSet<String> =
|
||||||
|
unknown_ids.iter().map(|s| ToString::to_string(s)).collect();
|
||||||
|
let direct_result = sync_models_direct(db, &unknown_set).await;
|
||||||
|
models_created += direct_result.models_created;
|
||||||
|
models_updated += direct_result.models_updated;
|
||||||
|
versions_created += direct_result.versions_created;
|
||||||
|
pricing_created += direct_result.pricing_created;
|
||||||
|
capabilities_created += direct_result.capabilities_created;
|
||||||
|
profiles_created += direct_result.profiles_created;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If no OpenRouter metadata matched at all, the direct sync above
|
||||||
|
// already handled everything — return early.
|
||||||
|
if filtered_count == 0 {
|
||||||
|
tracing::info!(
|
||||||
|
matched = filtered_count,
|
||||||
|
models_created,
|
||||||
|
models_updated,
|
||||||
|
versions_created,
|
||||||
|
pricing_created,
|
||||||
|
capabilities_created,
|
||||||
|
profiles_created,
|
||||||
|
"OpenRouter model sync complete (direct only)"
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
for or_model in filtered {
|
for or_model in filtered {
|
||||||
let provider_slug = extract_provider(&or_model.id);
|
let provider_slug = extract_provider(&or_model.id);
|
||||||
let provider = match upsert_provider(db, provider_slug).await {
|
let provider = match upsert_provider(db, provider_slug).await {
|
||||||
@ -987,13 +1066,17 @@ impl AppService {
|
|||||||
|
|
||||||
tracing::info!(
|
tracing::info!(
|
||||||
matched = filtered_count,
|
matched = filtered_count,
|
||||||
|
unknown = unknown_ids.len(),
|
||||||
models_created,
|
models_created,
|
||||||
models_updated,
|
models_updated,
|
||||||
versions_created,
|
versions_created,
|
||||||
pricing_created,
|
pricing_created,
|
||||||
capabilities_created,
|
capabilities_created,
|
||||||
profiles_created,
|
profiles_created,
|
||||||
"OpenRouter model sync complete"
|
"OpenRouter model sync complete: {} total ({} OpenRouter + {} direct)",
|
||||||
|
filtered_count + unknown_ids.len(),
|
||||||
|
filtered_count,
|
||||||
|
unknown_ids.len()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user