codetether_agent/search/model.rs
1//! Resolve a router `provider/model` string against the live `ProviderRegistry`.
2//!
3//! Lets the search command use `codetether models` discovery rather than a
4//! hardcoded provider binding.
5
6use std::sync::Arc;
7
8use anyhow::{Result, anyhow};
9
10use crate::provider::{Provider, ProviderRegistry};
11
12/// Default router model. Override with `--router-model` on the CLI or the
13/// `CODETETHER_SEARCH_ROUTER_MODEL` env var.
14pub const DEFAULT_ROUTER_MODEL: &str = "zai/glm-5.1";
15
16/// Resolve a `provider/model` reference into `(provider, model_id)`.
17///
18/// Falls back to the first registered provider when the reference has no
19/// `/`, which keeps the command usable when a user just types `glm-5.1`.
20///
21/// # Errors
22///
23/// Returns an error when the registry is empty or the named provider is
24/// missing.
25pub fn resolve_router_model(
26 registry: &ProviderRegistry,
27 model_ref: &str,
28) -> Result<(Arc<dyn Provider>, String)> {
29 if let Some((prov_name, model_name)) = model_ref.split_once('/') {
30 if let Some(prov) = registry.get(prov_name) {
31 return Ok((prov, model_name.to_string()));
32 }
33 // Requested provider is not configured. Rather than hard-fail — which
34 // makes the `search` tool unusable for any user who doesn't have the
35 // default `zai` provider set up — fall back to the first registered
36 // provider and log a warning so the caller can see what happened.
37 let first = registry
38 .list()
39 .first()
40 .copied()
41 .ok_or_else(|| anyhow!("router provider '{prov_name}' not available and no providers configured"))?;
42 let prov = registry
43 .get(first)
44 .ok_or_else(|| anyhow!("provider '{first}' missing"))?;
45 tracing::warn!(
46 requested = prov_name,
47 requested_model = model_name,
48 fallback_provider = first,
49 "router provider unavailable; falling back to first registered provider"
50 );
51 return Ok((prov, model_name.to_string()));
52 }
53 let first = registry
54 .list()
55 .first()
56 .copied()
57 .ok_or_else(|| anyhow!("no providers configured"))?;
58 let prov = registry
59 .get(first)
60 .ok_or_else(|| anyhow!("provider '{first}' missing"))?;
61 Ok((prov, model_ref.to_string()))
62}