Skip to main content

bobby_browser_client/
state.rs

1//! Session and page runtime state returned by `/v1` endpoints.
2
3use chrono::{DateTime, Utc};
4use serde::{Deserialize, Serialize};
5
6use crate::{ExecutionPolicy, PageId, SessionId};
7
8/// Page rendering / interaction mode.
9#[derive(Debug, Clone, Serialize, Deserialize)]
10#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
11pub enum PageMode {
12    Document,
13    Interactive,
14    Render,
15}
16
17/// `GET /v1/runtime` body.
18#[derive(Debug, Clone, Serialize, Deserialize)]
19#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
20pub struct RuntimeInfo {
21    pub version: String,
22    pub capabilities: Vec<String>,
23    pub active_sessions: usize,
24    pub queued_jobs: usize,
25    pub uptime_ms: u64,
26    /// Operator-set health budget for one vision propose round-trip
27    /// (`[vision].propose_budget_ms`); absent when no budget is configured.
28    #[serde(
29        default,
30        rename = "visionProposeBudgetMs",
31        skip_serializing_if = "Option::is_none"
32    )]
33    pub vision_propose_budget_ms: Option<u64>,
34    #[serde(
35        default,
36        rename = "operationalMetrics",
37        skip_serializing_if = "Option::is_none"
38    )]
39    pub operational_metrics: Option<OperationalMetricsSnapshot>,
40}
41
42#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
43#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
44#[serde(rename_all = "camelCase")]
45pub struct OperationalMetricsSnapshot {
46    pub observation_window_ms: u64,
47    pub intent: IntentMetricsSnapshot,
48    pub context: ContextMetricsSnapshot,
49    pub prefill: PrefillMetricsSnapshot,
50    pub vision: VisionMetricsSnapshot,
51    pub verification: VerificationMetricsSnapshot,
52    pub retries: RetryMetricsSnapshot,
53    pub reconciliation: ReconciliationMetricsSnapshot,
54    pub workflow_calls: WorkflowCallMetricsSnapshot,
55}
56
57macro_rules! metric_snapshot {
58    ($name:ident { $($field:ident),+ $(,)? }) => {
59        #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
60        #[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
61        #[serde(rename_all = "camelCase")]
62        pub struct $name { $(pub $field: u64,)+ }
63    };
64}
65
66metric_snapshot!(IntentMetricsSnapshot {
67    total,
68    locate,
69    fill,
70    complete_form,
71    extract,
72    submit,
73    wait_for_state,
74    follow,
75    dismiss,
76    solve_challenge,
77    detect_challenge,
78    deterministic,
79    context,
80    vision_prefill,
81    vision_fallback,
82});
83metric_snapshot!(ContextMetricsSnapshot {
84    hit,
85    miss,
86    ambiguous_refusal,
87    stale_rejection,
88    error,
89});
90metric_snapshot!(PrefillMetricsSnapshot {
91    hit,
92    miss,
93    dropped_entry,
94    policy_denied,
95    provider_failure,
96});
97metric_snapshot!(ConfidenceMetricsSnapshot {
98    below_acceptance,
99    accepted,
100    high,
101    unreported,
102});
103metric_snapshot!(VerificationMetricsSnapshot {
104    accepted,
105    target_not_found,
106    target_ambiguous,
107    obstruction_persisted,
108    value_mismatch,
109    other_rejected,
110});
111metric_snapshot!(RetryMetricsSnapshot {
112    transport,
113    timeout,
114    target_detached,
115    state_conflict,
116    other,
117});
118metric_snapshot!(ReconciliationMetricsSnapshot {
119    resumed,
120    restarted,
121    needs_reconciliation,
122    failed,
123});
124metric_snapshot!(WorkflowCallMetricsSnapshot {
125    lifecycle,
126    discovery,
127    read,
128    mutation,
129    composite_workflow,
130    recovery,
131    artifact,
132    job,
133});
134
135#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
136#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
137#[serde(rename_all = "camelCase")]
138pub struct VisionMetricsSnapshot {
139    pub attempted: u64,
140    pub accepted: u64,
141    pub rejected: u64,
142    pub abstained: u64,
143    pub timed_out: u64,
144    pub failed: u64,
145    pub provider_http: u64,
146    pub provider_acp: u64,
147    pub provider_direct_local: u64,
148    pub latency_ms: LatencyHistogramSnapshot,
149    pub confidence: ConfidenceMetricsSnapshot,
150}
151
152#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
153#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
154#[serde(rename_all = "camelCase")]
155pub struct LatencyHistogramSnapshot {
156    pub buckets: Vec<LatencyBucketSnapshot>,
157    pub overflow: u64,
158}
159
160#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
161#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
162#[serde(rename_all = "camelCase")]
163pub struct LatencyBucketSnapshot {
164    pub upper_bound_ms: u64,
165    pub count: u64,
166}
167
168/// Browser session returned by session create/list endpoints.
169#[derive(Debug, Clone, Serialize, Deserialize)]
170#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
171pub struct SessionState {
172    pub id: SessionId,
173    pub profile: String,
174    pub proxy: Option<String>,
175    pub page_ids: Vec<PageId>,
176    pub created_at: DateTime<Utc>,
177    pub last_used_at: DateTime<Utc>,
178    pub execution_policy: ExecutionPolicy,
179    /// Set at creation by `CreateSessionRequest::zigzagzig`: page-bound
180    /// commands run under the ZigZagZig recovery ladder. Recorded
181    /// separately from the policy — a hand-assembled everything-on policy
182    /// does not opt into the ladder. Absent from the wire when off, so
183    /// plain sessions carry no godmode baggage.
184    #[serde(default, skip_serializing_if = "std::ops::Not::not")]
185    pub zigzagzig: bool,
186}
187
188/// Page within a session.
189#[derive(Debug, Clone, Serialize, Deserialize)]
190#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
191pub struct PageState {
192    pub id: PageId,
193    pub session_id: SessionId,
194    pub url: Option<String>,
195    pub mode: PageMode,
196    pub ready_state: String,
197    pub pending_requests: usize,
198}
199
200/// Result of a navigation command.
201#[derive(Debug, Clone, Serialize, Deserialize)]
202#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
203pub struct NavigationResult {
204    pub page_id: PageId,
205    pub url: String,
206    pub ready_state: String,
207}
208
209/// Result of a structured extract command.
210#[derive(Debug, Clone, Serialize, Deserialize)]
211#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
212pub struct ExtractResult {
213    pub page_id: PageId,
214    pub data: serde_json::Value,
215}