1use chrono::{DateTime, Utc};
4use serde::{Deserialize, Serialize};
5
6use crate::{ExecutionPolicy, PageId, SessionId};
7
8#[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#[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 #[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 #[serde(
45 default,
46 rename = "providerHealth",
47 skip_serializing_if = "Option::is_none"
48 )]
49 pub provider_health: Option<Vec<ProviderHealthSnapshot>>,
50}
51
52#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
56#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
57#[serde(rename_all = "camelCase")]
58pub enum ProviderHealthStatus {
59 Healthy,
60 Degraded,
61 Unhealthy,
62}
63
64#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
67#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
68#[serde(rename_all = "camelCase")]
69pub struct ProviderHealthSnapshot {
70 pub provider_mode: String,
71 pub status: ProviderHealthStatus,
72 pub successes: u64,
73 pub failures: u64,
74 pub consecutive_failures: u64,
75 pub budget_violations: u64,
76 #[serde(default, skip_serializing_if = "Option::is_none")]
77 pub last_latency_ms: Option<u64>,
78 #[serde(default, skip_serializing_if = "Option::is_none")]
79 pub latency_budget_ms: Option<u64>,
80 pub failure_threshold: u64,
81}
82
83#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
84#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
85#[serde(rename_all = "camelCase")]
86pub struct OperationalMetricsSnapshot {
87 pub observation_window_ms: u64,
88 pub intent: IntentMetricsSnapshot,
89 pub context: ContextMetricsSnapshot,
90 #[serde(default)]
91 pub context_ranked_vision: ContextRankedVisionMetricsSnapshot,
92 pub prefill: PrefillMetricsSnapshot,
93 pub vision: VisionMetricsSnapshot,
94 pub verification: VerificationMetricsSnapshot,
95 pub retries: RetryMetricsSnapshot,
96 pub reconciliation: ReconciliationMetricsSnapshot,
97 pub workflow_calls: WorkflowCallMetricsSnapshot,
98}
99
100macro_rules! metric_snapshot {
101 ($name:ident { $($field:ident),+ $(,)? }) => {
102 #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
103 #[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
104 #[serde(rename_all = "camelCase")]
105 pub struct $name { $(pub $field: u64,)+ }
106 };
107}
108
109metric_snapshot!(IntentMetricsSnapshot {
110 total,
111 locate,
112 fill,
113 complete_form,
114 extract,
115 submit,
116 wait_for_state,
117 follow,
118 dismiss,
119 solve_challenge,
120 detect_challenge,
121 deterministic,
122 context,
123 vision_prefill,
124 vision_fallback,
125});
126metric_snapshot!(ContextMetricsSnapshot {
127 hit,
128 miss,
129 ambiguous_refusal,
130 stale_rejection,
131 error,
132});
133metric_snapshot!(PrefillMetricsSnapshot {
134 hit,
135 miss,
136 dropped_entry,
137 policy_denied,
138 provider_failure,
139});
140metric_snapshot!(ConfidenceMetricsSnapshot {
141 below_acceptance,
142 accepted,
143 high,
144 unreported,
145});
146metric_snapshot!(VerificationMetricsSnapshot {
147 accepted,
148 target_not_found,
149 target_ambiguous,
150 obstruction_persisted,
151 value_mismatch,
152 other_rejected,
153});
154metric_snapshot!(RetryMetricsSnapshot {
155 transport,
156 timeout,
157 target_detached,
158 state_conflict,
159 other,
160});
161metric_snapshot!(ReconciliationMetricsSnapshot {
162 resumed,
163 restarted,
164 needs_reconciliation,
165 failed,
166});
167metric_snapshot!(WorkflowCallMetricsSnapshot {
168 lifecycle,
169 discovery,
170 read,
171 mutation,
172 composite_workflow,
173 recovery,
174 artifact,
175 job,
176});
177
178#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
179#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
180#[serde(rename_all = "camelCase")]
181pub struct VisionMetricsSnapshot {
182 pub attempted: u64,
183 pub accepted: u64,
184 pub rejected: u64,
185 pub abstained: u64,
186 pub timed_out: u64,
187 pub failed: u64,
188 pub provider_http: u64,
189 pub provider_acp: u64,
190 pub provider_direct_local: u64,
191 pub latency_ms: LatencyHistogramSnapshot,
192 pub confidence: ConfidenceMetricsSnapshot,
193}
194
195#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
196#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
197#[serde(rename_all = "camelCase")]
198pub struct LatencyHistogramSnapshot {
199 pub buckets: Vec<LatencyBucketSnapshot>,
200 pub overflow: u64,
201}
202
203#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
204#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
205#[serde(rename_all = "camelCase")]
206pub struct LatencyBucketSnapshot {
207 pub upper_bound_ms: u64,
208 pub count: u64,
209}
210
211#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
212#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
213#[serde(rename_all = "camelCase")]
214pub struct ContextRankedVisionMetricsSnapshot {
215 pub attempted: u64,
216 pub source_observed: u64,
217 pub source_vision_promoted: u64,
218 pub source_unreported: u64,
219 pub hit: u64,
220 pub miss: u64,
221 pub ambiguous_refusal: u64,
222 pub stale_rejection: u64,
223 pub error: u64,
224 pub provider_escalations: u64,
225 pub provider_http: u64,
226 pub provider_acp: u64,
227 pub provider_direct_local: u64,
228 pub candidate_ranking_latency_ms: LatencyHistogramSnapshot,
229 pub confidence: ConfidenceMetricsSnapshot,
230 pub verification_accepted: u64,
231 pub verification_rejected: u64,
232}
233
234impl Default for ContextRankedVisionMetricsSnapshot {
235 fn default() -> Self {
236 Self {
237 attempted: 0,
238 source_observed: 0,
239 source_vision_promoted: 0,
240 source_unreported: 0,
241 hit: 0,
242 miss: 0,
243 ambiguous_refusal: 0,
244 stale_rejection: 0,
245 error: 0,
246 provider_escalations: 0,
247 provider_http: 0,
248 provider_acp: 0,
249 provider_direct_local: 0,
250 candidate_ranking_latency_ms: LatencyHistogramSnapshot {
251 buckets: Vec::new(),
252 overflow: 0,
253 },
254 confidence: ConfidenceMetricsSnapshot {
255 below_acceptance: 0,
256 accepted: 0,
257 high: 0,
258 unreported: 0,
259 },
260 verification_accepted: 0,
261 verification_rejected: 0,
262 }
263 }
264}
265
266#[derive(Debug, Clone, Serialize, Deserialize)]
268#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
269pub struct SessionState {
270 pub id: SessionId,
271 pub profile: String,
272 pub proxy: Option<String>,
273 pub page_ids: Vec<PageId>,
274 pub created_at: DateTime<Utc>,
275 pub last_used_at: DateTime<Utc>,
276 pub execution_policy: ExecutionPolicy,
277 #[serde(default, skip_serializing_if = "std::ops::Not::not")]
283 pub zigzagzig: bool,
284}
285
286#[derive(Debug, Clone, Serialize, Deserialize)]
288#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
289pub struct PageState {
290 pub id: PageId,
291 pub session_id: SessionId,
292 pub url: Option<String>,
293 pub mode: PageMode,
294 pub ready_state: String,
295 pub pending_requests: usize,
296}
297
298#[derive(Debug, Clone, Serialize, Deserialize)]
300#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
301pub struct NavigationResult {
302 pub page_id: PageId,
303 pub url: String,
304 pub ready_state: String,
305}
306
307#[derive(Debug, Clone, Serialize, Deserialize)]
309#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
310pub struct ExtractResult {
311 pub page_id: PageId,
312 pub data: serde_json::Value,
313}