systemprompt_models/bridge/
profile.rs1use serde::{Deserialize, Serialize};
23
24use crate::services::{ApiSurface, ProviderRegistry};
25
26#[derive(Debug, Clone, Serialize, Deserialize)]
27pub struct BridgeProfileResponse {
28 pub inference_gateway_base_url: String,
29 pub auth_scheme: String,
30 #[serde(default)]
31 pub models: Vec<String>,
32 #[serde(default, skip_serializing_if = "Option::is_none")]
33 pub default_model: Option<String>,
34 #[serde(default)]
35 pub organization_uuid: Option<String>,
36 #[serde(default)]
37 pub providers: Vec<ProviderHealth>,
38}
39
40pub const KNOWN_HOSTS: &[&str] = &[
41 "claude-code",
42 "claude-desktop",
43 "codex-cli",
44 "hermes",
45 "opencode",
46];
47
48#[derive(Debug, Clone, Serialize, Deserialize)]
51pub struct ProviderHealth {
52 pub name: String,
53 pub surface: ApiSurface,
54 pub configured: bool,
55 #[serde(default)]
56 pub models: Vec<String>,
57 #[serde(default, skip_serializing_if = "Option::is_none")]
58 pub config_issue: Option<String>,
59}
60
61pub fn provider_health(
62 registry: &ProviderRegistry,
63 secret_present: impl Fn(&str) -> bool,
64) -> Vec<ProviderHealth> {
65 registry
66 .advertised_providers()
67 .map(|entry| {
68 let secret = entry.api_key_secret.as_str();
69 let configured = secret_present(secret);
70 ProviderHealth {
71 name: entry.name.as_str().to_owned(),
72 surface: entry.surface,
73 configured,
74 models: entry
75 .models
76 .iter()
77 .flat_map(|m| {
78 std::iter::once(m.id.as_str().to_owned())
79 .chain(m.aliases.iter().map(|a| a.as_str().to_owned()))
80 })
81 .collect(),
82 config_issue: (!configured)
83 .then(|| format!("API key secret '{secret}' is not configured")),
84 }
85 })
86 .collect()
87}
88
89#[derive(Debug, Clone)]
90pub struct BridgeProfileParams<'a> {
91 pub inference_gateway_base_url: String,
92 pub auth_scheme: String,
93 pub organization_uuid: Option<String>,
94 pub default_model: Option<String>,
95 pub registry: &'a ProviderRegistry,
96}
97
98#[must_use]
99pub fn build(
100 params: BridgeProfileParams<'_>,
101 secret_present: impl Fn(&str) -> bool,
102) -> BridgeProfileResponse {
103 let BridgeProfileParams {
104 inference_gateway_base_url,
105 auth_scheme,
106 organization_uuid,
107 default_model,
108 registry,
109 } = params;
110 BridgeProfileResponse {
111 inference_gateway_base_url,
112 auth_scheme,
113 models: registry.advertised_model_ids(&[]),
114 default_model,
115 organization_uuid,
116 providers: provider_health(registry, secret_present),
117 }
118}