Skip to main content

context_snapshot/
context_snapshot.rs

1//! Generic context snapshot — builds a ContextSnapshot from arbitrary streams.
2//!
3//! Usage:
4//!   cargo run -p cel-context --example context_snapshot
5//!   cargo run -p cel-context --example context_snapshot -- --json
6
7use std::collections::HashMap;
8
9use cel_context::{
10    ContentRole, ContextContribution, ContextElement, ContextMerger, ContextSource, ElementState,
11    StreamStatus,
12};
13
14fn main() {
15    let json_output = std::env::args().any(|arg| arg == "--json");
16
17    let mut merger = ContextMerger::new().with_defaults("Incident Review", "Checkout Flow");
18    merger.push(
19        ContextContribution::new(
20            "metrics_stream",
21            vec![
22                element(
23                    "metric:checkout:error_rate",
24                    "metric",
25                    "Checkout error rate is above threshold",
26                    Some("7.2%"),
27                    ContextSource::External,
28                    ContentRole::Content,
29                ),
30                element(
31                    "metric:checkout:p95_latency",
32                    "metric",
33                    "Checkout p95 latency is elevated",
34                    Some("1840ms"),
35                    ContextSource::External,
36                    ContentRole::Content,
37                ),
38            ],
39        )
40        .with_app("Incident Review")
41        .with_window("Checkout Flow")
42        .with_stream_status(StreamStatus {
43            accessibility: false,
44            display: false,
45            network: true,
46            signals: true,
47            vision: false,
48            audio_capture: false,
49        }),
50    );
51    merger.push(ContextContribution::new(
52        "support_tickets",
53        vec![element(
54            "ticket:1842",
55            "ticket",
56            "Users report payment confirmation page timing out",
57            Some("priority=high"),
58            ContextSource::External,
59            ContentRole::Content,
60        )],
61    ));
62    merger.push(ContextContribution::new(
63        "browser_dom",
64        vec![element(
65            "dom:button:retry-payment",
66            "button",
67            "Retry payment",
68            None,
69            ContextSource::Cdp,
70            ContentRole::Interactive,
71        )],
72    ));
73    merger.push(ContextContribution::new(
74        "service_logs",
75        vec![element(
76            "log:payment-service:timeout",
77            "log_event",
78            "payment-service timed out calling card processor",
79            Some("Timeout after 3000ms"),
80            ContextSource::External,
81            ContentRole::Content,
82        )],
83    ));
84
85    let ctx = merger.build();
86
87    if json_output {
88        println!("{}", serde_json::to_string_pretty(&ctx).unwrap());
89        return;
90    }
91
92    println!("=== Agent Context Snapshot ===");
93    println!("App:       {}", ctx.app);
94    println!("Window:    {}", ctx.window);
95    println!("Timestamp: {} ms", ctx.timestamp_ms);
96    println!("Elements:  {}", ctx.elements.len());
97    println!();
98
99    for (i, elem) in ctx.elements.iter().enumerate() {
100        let label = elem.label.as_deref().unwrap_or("(no label)");
101        let value = elem.value.as_deref().unwrap_or("");
102        println!(
103            "  {:>2}. [{:.2}] {:12} {:48} {:18} {:?}",
104            i + 1,
105            elem.confidence,
106            elem.element_type,
107            label,
108            value,
109            elem.source
110        );
111    }
112}
113
114fn element(
115    id: &str,
116    element_type: &str,
117    label: &str,
118    value: Option<&str>,
119    source: ContextSource,
120    content_role: ContentRole,
121) -> ContextElement {
122    ContextElement {
123        id: id.into(),
124        label: Some(label.into()),
125        description: None,
126        element_type: element_type.into(),
127        value: value.map(String::from),
128        bounds: None,
129        state: ElementState::default(),
130        parent_id: None,
131        actions: Vec::new(),
132        confidence: 0.0,
133        source,
134        content_role,
135        properties: HashMap::new(),
136    }
137}