Skip to main content

codex_helper_core/proxy/
api_responses.rs

1use crate::config::{ProxyConfig, resolve_service_profile};
2use crate::dashboard_core::{
3    ApiV1OperatorSummary, ControlPlaneSurfaceCapabilities, ControlProfileOption,
4    HostLocalControlPlaneCapabilities, OperatorProfileSummary, OperatorRetrySummary,
5    OperatorRuntimeSummary, OperatorSummaryCounts, RemoteAdminAccessCapabilities,
6    SharedControlPlaneCapabilities, build_operator_health_summary, build_profile_options_from_mgr,
7    build_provider_options_from_view, build_station_options_from_mgr,
8    summarize_recent_retry_observations,
9};
10use crate::state::{SessionIdentityCardBuildInputs, build_session_identity_cards_from_parts};
11
12use super::ProxyService;
13use super::control_plane_manifest::api_v1_operator_summary_links;
14use super::control_plane_service::{load_persisted_proxy_settings_v2, service_view_v2};
15use super::profile_defaults::{
16    configured_active_station_name, effective_active_station_name, effective_default_profile_name,
17};
18
19#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
20pub struct ProfilesResponse {
21    pub default_profile: Option<String>,
22    pub configured_default_profile: Option<String>,
23    pub profiles: Vec<ControlProfileOption>,
24}
25
26#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
27pub struct RuntimeStatusResponse {
28    pub runtime_source_path: String,
29    pub config_path: String,
30    pub loaded_at_ms: u64,
31    pub source_mtime_ms: Option<u64>,
32    pub retry: crate::config::ResolvedRetryConfig,
33    #[serde(default)]
34    pub shutdown_available: bool,
35}
36
37#[derive(serde::Serialize)]
38pub(super) struct RetryConfigResponse {
39    configured: crate::config::RetryConfig,
40    resolved: crate::config::ResolvedRetryConfig,
41}
42
43#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
44pub struct ReloadResult {
45    pub reloaded: bool,
46    pub status: RuntimeStatusResponse,
47}
48
49pub(super) async fn build_operator_summary(
50    proxy: &ProxyService,
51    surface_capabilities: ControlPlaneSurfaceCapabilities,
52    shared_capabilities: SharedControlPlaneCapabilities,
53    host_local_capabilities: HostLocalControlPlaneCapabilities,
54    remote_admin_access: RemoteAdminAccessCapabilities,
55) -> ApiV1OperatorSummary {
56    let cfg = proxy.config.snapshot().await;
57    let mgr = proxy.service_manager(cfg.as_ref());
58    let configured_active_station = configured_active_station_name(mgr);
59    let effective_active_station = effective_active_station_name(mgr);
60    let configured_default_profile = mgr.default_profile.clone();
61    let configured_retry = cfg.retry.clone();
62    let resolved_retry = configured_retry.resolve();
63    let loaded_at_ms = proxy.config.last_loaded_at_ms();
64    let source_mtime_ms = proxy.config.last_mtime_ms().await;
65    let (
66        active,
67        recent,
68        global_station_override,
69        global_route_target_override,
70        session_model,
71        session_station,
72        session_effort,
73        session_service_tier,
74        session_bindings,
75        session_route_affinities,
76        session_stats,
77        default_profile,
78        station_meta_overrides,
79        station_state_overrides,
80        provider_upstream_overrides,
81        station_health,
82        health_checks,
83        lb_view,
84    ) = tokio::join!(
85        proxy.state.list_active_requests(),
86        proxy.state.list_recent_finished(200),
87        proxy.state.get_global_station_override(),
88        proxy.state.get_global_route_target_override(),
89        proxy.state.list_session_model_overrides(),
90        proxy.state.list_session_station_overrides(),
91        proxy.state.list_session_effort_overrides(),
92        proxy.state.list_session_service_tier_overrides(),
93        proxy.state.list_session_bindings(),
94        proxy.state.list_session_route_affinities(),
95        proxy.state.list_session_stats(),
96        effective_default_profile_name(proxy.state.as_ref(), proxy.service_name, mgr),
97        proxy.state.get_station_meta_overrides(proxy.service_name),
98        proxy
99            .state
100            .get_station_runtime_state_overrides(proxy.service_name),
101        proxy.state.get_upstream_meta_overrides(proxy.service_name),
102        proxy.state.get_station_health(proxy.service_name),
103        proxy.state.list_health_checks(proxy.service_name),
104        proxy.state.get_lb_view(),
105    );
106    let session_cards = build_session_identity_cards_from_parts(SessionIdentityCardBuildInputs {
107        active: &active,
108        recent: &recent,
109        overrides: &session_effort,
110        station_overrides: &session_station,
111        model_overrides: &session_model,
112        service_tier_overrides: &session_service_tier,
113        bindings: &session_bindings,
114        route_affinities: &session_route_affinities,
115        global_station_override: global_station_override.as_deref(),
116        stats: &session_stats,
117    });
118    let default_profile_summary = default_profile.as_deref().and_then(|profile_name| {
119        resolve_service_profile(mgr, profile_name)
120            .ok()
121            .map(|profile| OperatorProfileSummary {
122                name: profile_name.to_string(),
123                station: profile.station,
124                model: profile.model,
125                reasoning_effort: profile.reasoning_effort,
126                service_tier: profile.service_tier.clone(),
127                fast_mode: profile.service_tier.as_deref() == Some("priority"),
128            })
129    });
130    let stations =
131        build_station_options_from_mgr(mgr, &station_meta_overrides, &station_state_overrides);
132    let profiles = build_profile_options_from_mgr(mgr, default_profile.as_deref());
133    let health =
134        build_operator_health_summary(&stations, &station_health, &health_checks, &lb_view);
135    let providers = load_persisted_proxy_settings_v2()
136        .await
137        .ok()
138        .map(|persisted_cfg| {
139            build_provider_options_from_view(
140                proxy.service_name,
141                service_view_v2(&persisted_cfg, proxy.service_name),
142                &provider_upstream_overrides,
143            )
144        })
145        .unwrap_or_default();
146    let retry_observations = summarize_recent_retry_observations(&recent);
147
148    ApiV1OperatorSummary {
149        api_version: 1,
150        service_name: proxy.service_name.to_string(),
151        runtime: OperatorRuntimeSummary {
152            runtime_loaded_at_ms: Some(loaded_at_ms),
153            runtime_source_mtime_ms: source_mtime_ms,
154            configured_active_station,
155            effective_active_station,
156            global_station_override,
157            global_route_target_override,
158            configured_default_profile,
159            default_profile,
160            default_profile_summary,
161        },
162        counts: OperatorSummaryCounts {
163            active_requests: active.len(),
164            recent_requests: recent.len(),
165            sessions: session_cards.len(),
166            stations: mgr.stations().len(),
167            profiles: mgr.profiles.len(),
168            providers: providers.len(),
169        },
170        retry: OperatorRetrySummary {
171            configured_profile: configured_retry.profile,
172            supports_write: surface_capabilities.retry_config,
173            upstream_max_attempts: resolved_retry.upstream.max_attempts,
174            provider_max_attempts: resolved_retry.route.max_attempts,
175            allow_cross_station_before_first_output: resolved_retry
176                .allow_cross_station_before_first_output,
177            recent_retried_requests: retry_observations.recent_retried_requests,
178            recent_cross_station_failovers: retry_observations.recent_cross_station_failovers,
179            recent_same_station_retries: retry_observations.recent_same_station_retries,
180            recent_fast_mode_requests: retry_observations.recent_fast_mode_requests,
181        },
182        health: Some(health),
183        session_cards,
184        stations: stations.clone(),
185        profiles,
186        providers,
187        links: Some(api_v1_operator_summary_links()),
188        surface_capabilities,
189        shared_capabilities,
190        host_local_capabilities,
191        remote_admin_access,
192    }
193}
194
195pub(super) async fn make_profiles_response(proxy: &ProxyService) -> ProfilesResponse {
196    let cfg = proxy.config.snapshot().await;
197    let mgr = proxy.service_manager(cfg.as_ref());
198    let default_profile =
199        effective_default_profile_name(proxy.state.as_ref(), proxy.service_name, mgr).await;
200    ProfilesResponse {
201        default_profile: default_profile.clone(),
202        configured_default_profile: mgr.default_profile.clone(),
203        profiles: build_profile_options_from_mgr(mgr, default_profile.as_deref()),
204    }
205}
206
207pub(super) async fn build_runtime_status_response(proxy: &ProxyService) -> RuntimeStatusResponse {
208    let cfg = proxy.config.snapshot().await;
209    let runtime_source_path = crate::config::config_file_path().display().to_string();
210    RuntimeStatusResponse {
211        runtime_source_path: runtime_source_path.clone(),
212        config_path: runtime_source_path,
213        loaded_at_ms: proxy.config.last_loaded_at_ms(),
214        source_mtime_ms: proxy.config.last_mtime_ms().await,
215        retry: cfg.retry.resolve(),
216        shutdown_available: proxy.shutdown_available(),
217    }
218}
219
220pub(super) fn build_retry_config_response(cfg: &ProxyConfig) -> RetryConfigResponse {
221    RetryConfigResponse {
222        configured: cfg.retry.clone(),
223        resolved: cfg.retry.resolve(),
224    }
225}
226
227pub(super) fn build_reload_result(reloaded: bool, status: RuntimeStatusResponse) -> ReloadResult {
228    ReloadResult { reloaded, status }
229}