Skip to main content

systemprompt_models/bridge/
profile.rs

1//! Wire contract for `GET /v1/bridge/profile`.
2//!
3//! The desktop bridge (`bin/bridge`) fetches this to render host configuration
4//! and to decide which provider models each host advertises. The server
5//! (`crates/entry/api`) produces it and the bridge consumes it through these
6//! exact types, so the two sides cannot drift.
7//!
8//! Every field is derived in [`build`] from
9//! [`ProviderRegistry::advertised_providers`], the single bearer of the
10//! advertisement rule ([`ApiSurface::is_advertised`]). A `surface: backend`
11//! provider is therefore structurally absent from both `providers` and the
12//! flat `models` front door — the flat list is a projection of the same
13//! advertised set, so it can never disagree with `providers`.
14
15use serde::{Deserialize, Serialize};
16
17use crate::profile::{ApiSurface, ProviderRegistry};
18
19#[derive(Debug, Clone, Serialize, Deserialize)]
20pub struct BridgeProfileResponse {
21    pub inference_gateway_base_url: String,
22    pub auth_scheme: String,
23    #[serde(default)]
24    pub models: Vec<String>,
25    #[serde(default)]
26    pub organization_uuid: Option<String>,
27    #[serde(default)]
28    pub providers: Vec<ProviderHealth>,
29}
30
31/// A provider whose credential secret is absent is flagged
32/// (`configured = false`) rather than dropped silently.
33#[derive(Debug, Clone, Serialize, Deserialize)]
34pub struct ProviderHealth {
35    pub name: String,
36    pub surface: ApiSurface,
37    pub configured: bool,
38    #[serde(default)]
39    pub models: Vec<String>,
40    #[serde(default, skip_serializing_if = "Option::is_none")]
41    pub config_issue: Option<String>,
42}
43
44pub fn provider_health(
45    registry: &ProviderRegistry,
46    secret_present: impl Fn(&str) -> bool,
47) -> Vec<ProviderHealth> {
48    registry
49        .advertised_providers()
50        .map(|entry| {
51            let secret = entry.api_key_secret.as_str();
52            let configured = secret_present(secret);
53            ProviderHealth {
54                name: entry.name.as_str().to_owned(),
55                surface: entry.surface,
56                configured,
57                models: entry
58                    .models
59                    .iter()
60                    .flat_map(|m| {
61                        std::iter::once(m.id.as_str().to_owned())
62                            .chain(m.aliases.iter().map(|a| a.as_str().to_owned()))
63                    })
64                    .collect(),
65                config_issue: (!configured)
66                    .then(|| format!("API key secret '{secret}' is not configured")),
67            }
68        })
69        .collect()
70}
71
72#[must_use]
73pub fn build(
74    inference_gateway_base_url: String,
75    auth_scheme: String,
76    organization_uuid: Option<String>,
77    registry: &ProviderRegistry,
78    secret_present: impl Fn(&str) -> bool,
79) -> BridgeProfileResponse {
80    BridgeProfileResponse {
81        inference_gateway_base_url,
82        auth_scheme,
83        models: registry.advertised_model_ids(&[ApiSurface::Anthropic]),
84        organization_uuid,
85        providers: provider_health(registry, secret_present),
86    }
87}