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. The builder that derives every
7//! field from the resolved catalog lives with the services manifest
8//! (`systemprompt_manifest::bridge_profile`).
9//!
10//! Copyright (c) systemprompt.io — Business Source License 1.1.
11//! See <https://systemprompt.io> for licensing details.
12
13use std::collections::BTreeMap;
14
15use serde::{Deserialize, Serialize};
16use systemprompt_identifiers::ModelId;
17
18use crate::providers::ClientFamily;
19
20#[derive(Debug, Clone, Serialize, Deserialize)]
21pub struct BridgeProfileResponse {
22    pub inference_gateway_base_url: String,
23    pub auth_scheme: String,
24    #[serde(default)]
25    pub models: Vec<ModelId>,
26    #[serde(default, skip_serializing_if = "Option::is_none")]
27    pub default_model: Option<String>,
28    #[serde(default)]
29    pub organization_uuid: Option<String>,
30    #[serde(default)]
31    pub providers: Vec<ProviderHealth>,
32    // Why: hosts that take per-model limits (OpenCode) otherwise size every
33    // gateway model with their own default and cut a 1M model short. Absent
34    // from an older server, so it defaults to empty.
35    #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
36    pub model_limits: BTreeMap<ModelId, AdvertisedLimits>,
37    #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
38    pub model_names: BTreeMap<ModelId, String>,
39}
40
41#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
42pub struct AdvertisedLimits {
43    pub context_window: u32,
44    pub max_output_tokens: u32,
45}
46
47/// One provider with advertised models, and whether it can serve requests.
48///
49/// A provider whose credential secret is absent is flagged
50/// (`configured = false`) rather than dropped silently. A `personal_key`
51/// provider is served only from a key the user stores for themselves under
52/// `credential` (a personal subscription); `configured` then means this user
53/// has stored one. `clients` is the union of the client families its
54/// advertised models are listed to.
55#[derive(Debug, Clone, Serialize, Deserialize)]
56pub struct ProviderHealth {
57    pub name: String,
58    pub credential: String,
59    #[serde(default)]
60    pub clients: Vec<ClientFamily>,
61    pub configured: bool,
62    #[serde(default)]
63    pub models: Vec<String>,
64    #[serde(default, skip_serializing_if = "Option::is_none")]
65    pub config_issue: Option<String>,
66    #[serde(default, skip_serializing_if = "Option::is_none")]
67    pub display_name: Option<String>,
68    #[serde(default, skip_serializing_if = "std::ops::Not::not")]
69    pub personal_key: bool,
70}
71
72/// One row of `GET /v1/me/provider-credentials`: a personal credential, and
73/// what is known about the caller's stored key without opening it. The key
74/// itself is never returned.
75#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
76pub struct ProviderCredentialStatus {
77    pub credential: String,
78    #[serde(default, skip_serializing_if = "Option::is_none")]
79    pub display_name: Option<String>,
80    pub configured: bool,
81    #[serde(default, skip_serializing_if = "Option::is_none")]
82    pub label: Option<String>,
83    #[serde(default, skip_serializing_if = "Option::is_none")]
84    pub created_at: Option<chrono::DateTime<chrono::Utc>>,
85    #[serde(default, skip_serializing_if = "Option::is_none")]
86    pub verified_at: Option<chrono::DateTime<chrono::Utc>>,
87    #[serde(default, skip_serializing_if = "Option::is_none")]
88    pub last_used_at: Option<chrono::DateTime<chrono::Utc>>,
89}
90
91/// Body of `PUT /v1/me/provider-credentials/{credential}`.
92#[derive(Debug, Clone, Serialize, Deserialize)]
93pub struct SetProviderCredentialRequest {
94    pub api_key: String,
95    #[serde(default, skip_serializing_if = "Option::is_none")]
96    pub label: Option<String>,
97}