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 provider registry 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};
16
17use crate::providers::ApiSurface;
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, skip_serializing_if = "Option::is_none")]
26    pub default_model: Option<String>,
27    #[serde(default)]
28    pub organization_uuid: Option<String>,
29    #[serde(default)]
30    pub providers: Vec<ProviderHealth>,
31    // Why: hosts that take per-model limits (OpenCode) otherwise size every
32    // gateway model with their own default and cut a 1M model short. Absent
33    // from an older server, so it defaults to empty.
34    #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
35    pub model_limits: BTreeMap<String, AdvertisedLimits>,
36}
37
38#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
39pub struct AdvertisedLimits {
40    pub context_window: u32,
41    pub max_output_tokens: u32,
42}
43
44/// One advertised provider and whether it can serve requests.
45///
46/// A provider whose credential secret is absent is flagged
47/// (`configured = false`) rather than dropped silently. A `personal_key`
48/// provider is served only from a key the user stores for themselves (a
49/// personal subscription); `configured` then means this user has stored one.
50#[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    #[serde(default, skip_serializing_if = "Option::is_none")]
60    pub display_name: Option<String>,
61    #[serde(default, skip_serializing_if = "std::ops::Not::not")]
62    pub personal_key: bool,
63}
64
65/// One row of `GET /v1/me/provider-credentials`: a provider that takes a
66/// personal key, and what is known about the caller's stored key without
67/// opening it. The key itself is never returned.
68#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
69pub struct ProviderCredentialStatus {
70    pub provider: String,
71    #[serde(default, skip_serializing_if = "Option::is_none")]
72    pub display_name: Option<String>,
73    pub configured: bool,
74    #[serde(default, skip_serializing_if = "Option::is_none")]
75    pub label: Option<String>,
76    #[serde(default, skip_serializing_if = "Option::is_none")]
77    pub created_at: Option<chrono::DateTime<chrono::Utc>>,
78    #[serde(default, skip_serializing_if = "Option::is_none")]
79    pub verified_at: Option<chrono::DateTime<chrono::Utc>>,
80    #[serde(default, skip_serializing_if = "Option::is_none")]
81    pub last_used_at: Option<chrono::DateTime<chrono::Utc>>,
82}
83
84/// Body of `PUT /v1/me/provider-credentials/{provider}`.
85#[derive(Debug, Clone, Serialize, Deserialize)]
86pub struct SetProviderCredentialRequest {
87    pub api_key: String,
88    #[serde(default, skip_serializing_if = "Option::is_none")]
89    pub label: Option<String>,
90}