1use std::time::Instant;
15
16use anyhow::{Context, Result};
17use serde_json::json;
18
19use jev_bridge::server::{Bridge, BridgeConfig};
20use jev_bridge::wire::{Row, RowOption};
21
22fn option(id: &str, description: &str) -> RowOption {
23 RowOption {
24 id: id.to_string(),
25 description: description.to_string(),
26 }
27}
28
29#[tokio::main]
30async fn main() -> Result<()> {
31 let openai_base = std::env::var("JEV_BRIDGE_UPSTREAM_URL")
32 .unwrap_or_else(|_| "http://127.0.0.1:8080/v1".to_string());
33 let native_base = std::env::var("JEV_BRIDGE_NATIVE_URL")
34 .unwrap_or_else(|_| openai_base.trim_end_matches("/v1").to_string());
35 let upstream_key = std::env::var("JEV_BRIDGE_UPSTREAM_KEY").ok();
36 let upstream_model =
37 std::env::var("JEV_BRIDGE_UPSTREAM_MODEL").unwrap_or_else(|_| "local-model".to_string());
38
39 let started = Instant::now();
40 let bridge = Bridge::connect(
41 reqwest::Client::new(),
42 BridgeConfig {
43 openai_base,
44 native_base,
45 upstream_model,
46 upstream_key,
47 served_model: "example-bridge".to_string(),
48 description: "example bridge".to_string(),
49 release_date: "2026-09-22".to_string(),
50 chat_template_kwargs: json!({"enable_thinking": false}),
51 max_input_tokens: None,
52 local: Default::default(),
53 accept_any_model: false,
54 readout: Default::default(),
55 },
56 )
57 .await
58 .context("connecting to the upstream service failed")?;
59
60 println!(
61 "connected in {:.2?}: transport={} endpoint={} renders_locally={} tokenizes_locally={}",
62 started.elapsed(),
63 bridge.probe().name(),
64 bridge.probe().endpoint(),
65 bridge.renders_locally(),
66 bridge.tokenizes_locally(),
67 );
68
69 let rows = vec![
70 Row {
71 id: "refund".to_string(),
72 state: json!("I was charged twice and need a refund today."),
73 question: "Which queue should handle this request?".to_string(),
74 options: vec![
75 option("access", "Account access and authentication."),
76 option("billing", "Billing, payments, and refunds."),
77 option("sales", "Pricing and new contracts."),
78 ],
79 },
80 Row {
81 id: "outage".to_string(),
82 state: json!("The API returns 502 for every request since this morning."),
83 question: "Which queue should handle this request?".to_string(),
84 options: vec![
85 option("access", "Account access and authentication."),
86 option("billing", "Billing, payments, and refunds."),
87 option("sales", "Pricing and new contracts."),
88 ],
89 },
90 ];
91
92 let started = Instant::now();
93 let mut scored = Vec::with_capacity(rows.len());
95 for row in &rows {
96 scored.push(bridge.score_row(row).await?);
97 }
98 println!("scored {} rows in {:.2?}\n", scored.len(), started.elapsed());
99
100 for row in &scored {
101 let winner = row
102 .answer
103 .probabilities
104 .iter()
105 .enumerate()
106 .max_by(|a, b| a.1.partial_cmp(b.1).unwrap())
107 .map(|(index, _)| row.answer.option_ids[index].as_str())
108 .unwrap();
109 println!("{} -> {winner}", row.answer.id);
110 for (id, probability) in row.answer.option_ids.iter().zip(&row.answer.probabilities) {
111 println!(" {id:<8} {probability:.4}");
112 }
113 println!(
114 " input_tokens={} prompt_sha256={}",
115 row.answer.input_tokens,
116 &row.prompt_sha256[..16]
117 );
118 }
119
120 println!("\nThese scores are conditional option probabilities, not calibrated confidence.");
122 Ok(())
123}