Skip to main content

wave_dispatch/
lib.rs

1//! wave Dispatch — thin Rust client. Route each request to the cheapest capable model (local-first;
2//! escalate to your frontier only when needed). BYO keys + infra; the service returns a routing decision.
3use serde_json::{json, Value};
4use std::error::Error;
5
6pub struct Dispatch {
7    license: Option<String>,
8    endpoint: String,
9}
10
11impl Dispatch {
12    /// `license`: your `wv_...` key, or `None` to read `WAVE_LICENSE` (omit for x402 pay-per-use).
13    pub fn new(license: Option<String>) -> Self {
14        Dispatch {
15            license: license.or_else(|| std::env::var("WAVE_LICENSE").ok()),
16            endpoint: std::env::var("DISPATCH_ENDPOINT").unwrap_or_else(|_| "https://dispatch.wave.online".into()),
17        }
18    }
19
20    /// Classify a prompt (no execution): `{route, probability, margin, forward}`.
21    pub fn route(&self, prompt: &str) -> Result<Value, Box<dyn Error>> {
22        self.post(json!({ "prompt": prompt }))
23    }
24
25    /// Classify and run on the edge if your plan allows it.
26    pub fn execute(&self, prompt: &str) -> Result<Value, Box<dyn Error>> {
27        self.post(json!({ "prompt": prompt, "execute": true }))
28    }
29
30    fn post(&self, body: Value) -> Result<Value, Box<dyn Error>> {
31        let mut req = ureq::post(&self.endpoint).set("content-type", "application/json");
32        if let Some(l) = &self.license {
33            req = req.set("authorization", &format!("Bearer {}", l));
34        }
35        let resp = req.send_string(&body.to_string())?.into_string()?;
36        Ok(serde_json::from_str(&resp)?)
37    }
38}