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