Skip to main content

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}