feat: add Go, Rust client examples and modular routing recipes

This commit is contained in:
2026-10-16 18:04:13 -07:00
parent a65f7e180d
commit 7d5fd84df4
7 changed files with 391 additions and 0 deletions
+18
View File
@@ -48,9 +48,27 @@ Runnable client examples in this repository:
npm start
```
- **Go**: [`examples/go/main.go`](examples/go/main.go)
```bash
export HANZO_API_KEY="your-api-key"
go run examples/go/main.go
```
- **Rust**: [`examples/rust/`](examples/rust/)
```bash
export HANZO_API_KEY="your-api-key"
cargo run --manifest-path examples/rust/Cargo.toml
```
- **cURL**: [`examples/curl/chat.sh`](examples/curl/chat.sh)
```bash
export HANZO_API_KEY="your-api-key"
./examples/curl/chat.sh
```
## Production Rust Recipes
[`recipes/`](recipes/): Modular, typed Rust patterns for model routing:
- **`ModelSuitabilityRecipe`**: Single-shot classification predicting optimal model capability tier given prompt tokens, domain, and latency/cost bounds.
- **`ConfidenceGate`**: Evaluates Kai decision margins ($\Delta = P_{(1)} - P_{(2)}$) against threshold $\tau$ for low-latency fast decisions vs. LLM escalation.
+94
View File
@@ -0,0 +1,94 @@
// Hanzo Enso Go Quickstart.
// Connects to Enso Router at /v1/chat/completions.
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"os"
"strings"
)
type ChatRequest struct {
Model string `json:"model"`
Messages []Message `json:"messages"`
MaxTokens int `json:"max_tokens"`
Temperature float64 `json:"temperature"`
}
type Message struct {
Role string `json:"role"`
Content string `json:"content"`
}
type ChatResponse struct {
Model string `json:"model"`
Choices []struct {
Message struct {
Role string `json:"role"`
Content string `json:"content"`
ReasoningContent string `json:"reasoning_content"`
} `json:"message"`
} `json:"choices"`
}
func main() {
apiHost := os.Getenv("HANZO_API_HOST")
if apiHost == "" {
apiHost = "https://api.hanzo.ai"
}
apiKey := os.Getenv("HANZO_API_KEY")
fmt.Printf("Connecting to Enso Router at %s...\n", apiHost)
reqBody := ChatRequest{
Model: "enso-free",
Messages: []Message{
{Role: "user", Content: "What is model routing?"},
},
MaxTokens: 128,
Temperature: 0.1,
}
jsonBytes, err := json.Marshal(reqBody)
if err != nil {
panic(err)
}
url := fmt.Sprintf("%s/v1/chat/completions", strings.TrimRight(apiHost, "/"))
req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonBytes))
if err != nil {
panic(err)
}
req.Header.Set("Content-Type", "application/json")
if apiKey != "" {
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", apiKey))
}
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
bodyBytes, _ := io.ReadAll(resp.Body)
if resp.StatusCode != http.StatusOK {
fmt.Printf("Error: HTTP %d: %s\n", resp.StatusCode, string(bodyBytes))
os.Exit(1)
}
var chatResp ChatResponse
if err := json.Unmarshal(bodyBytes, &chatResp); err != nil {
panic(err)
}
fmt.Printf("Model selected: %s\n", chatResp.Model)
if len(chatResp.Choices) > 0 {
fmt.Printf("Response: %s\n", chatResp.Choices[0].Message.Content)
}
}
+9
View File
@@ -0,0 +1,9 @@
[package]
name = "enso-quickstart"
version = "0.1.0"
edition = "2021"
[dependencies]
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
reqwest = { version = "0.11", features = ["json", "blocking"] }
+45
View File
@@ -0,0 +1,45 @@
//! Hanzo Enso Rust SDK Quickstart.
//!
//! Submits a query to the Enso router (POST /v1/chat/completions).
use serde_json::json;
use std::env;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let api_host = env::var("HANZO_API_HOST").unwrap_or_else(|_| "https://api.hanzo.ai".into());
let api_key = env::var("HANZO_API_KEY").unwrap_or_default();
println!("Connecting to Enso Router at {api_host}...");
let payload = json!({
"model": "enso-free",
"messages": [
{"role": "user", "content": "Explain single-shot routing in one sentence."}
],
"temperature": 0.1,
"max_tokens": 256
});
let client = reqwest::blocking::Client::new();
let mut req = client.post(format!("{}/v1/chat/completions", api_host.trim_end_matches('/')))
.header("Content-Type", "application/json")
.json(&payload);
if !api_key.is_empty() {
req = req.header("Authorization", format!("Bearer {api_key}"));
}
let resp = req.send()?;
let status = resp.status();
let body: serde_json::Value = resp.json()?;
println!("HTTP Status: {status}");
println!("Model Selected: {}", body["model"].as_str().unwrap_or("unknown"));
if let Some(content) = body["choices"][0]["message"]["content"].as_str() {
println!("Response:\n{}", content.trim());
} else {
println!("Raw Response:\n{}", serde_json::to_string_pretty(&body)?);
}
Ok(())
}
+107
View File
@@ -0,0 +1,107 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "enso-recipes"
version = "0.1.0"
dependencies = [
"serde",
"serde_json",
]
[[package]]
name = "itoa"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
[[package]]
name = "memchr"
version = "2.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98"
[[package]]
name = "proc-macro2"
version = "1.0.107"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.47"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001"
dependencies = [
"proc-macro2",
]
[[package]]
name = "serde"
version = "1.0.229"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba"
dependencies = [
"serde_core",
"serde_derive",
]
[[package]]
name = "serde_core"
version = "1.0.229"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.229"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "serde_json"
version = "1.0.154"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7e9cc8b1b85264074fbcc02a88680c4096b1e47df8f739dceb03bf482f04bd6"
dependencies = [
"itoa",
"memchr",
"serde",
"serde_core",
"zmij",
]
[[package]]
name = "syn"
version = "3.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d78c8dee4c7bf0e14673097256fed6142ce9d3b85a408189d07482442145823b"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "unicode-ident"
version = "1.0.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2c754d6c33795a1c324727428e5a7dedb5b06195f9890bdbcba760d3e246563"
[[package]]
name = "zmij"
version = "1.0.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b"
+9
View File
@@ -0,0 +1,9 @@
[package]
name = "enso-recipes"
version = "0.1.0"
edition = "2021"
description = "Hanzo Enso Model Suitability and Routing Recipes in Rust."
[dependencies]
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
+109
View File
@@ -0,0 +1,109 @@
//! Model Routing & Inference Optimization Recipes.
//!
//! Enso/Kai routing patterns: single-shot model suitability prediction,
//! adaptive thinking depth budgeting, and calibrated LLM escalation.
use serde::{Deserialize, Serialize};
use serde_json::{json, Value};
/// 1. Single-Shot Model Suitability Routing Recipe.
pub struct ModelSuitabilityRecipe;
#[derive(Debug, Serialize, Deserialize)]
pub struct TaskComplexityState {
pub prompt_token_count: usize,
pub task_domain: String,
pub requires_formal_reasoning: bool,
pub requires_code_execution: bool,
pub latency_deadline_ms: u32,
pub max_cost_nano_usd: u64,
}
impl ModelSuitabilityRecipe {
pub fn build_request(task: &TaskComplexityState) -> Value {
json!({
"model": "kai",
"state": task,
"questions": {
"selected_tier": {
"type": "choice",
"instructions": "Predict optimal model tier achieving >= 95% success probability at lowest cost",
"criteria": {
"edge_fast_3b": "trivial classification or pattern extraction; sub-50ms local inference",
"cloud_flash_8b": "standard conversational synthesis or lightweight summarization",
"frontier_pro_deep": "complex scientific reasoning, full codebase refactoring, or multi-step logic"
}
},
"thinking_depth": {
"type": "choice",
"instructions": "Calibrate reasoning budget depth for the chosen tier",
"criteria": {
"off": "no chain-of-thought needed; direct token output",
"low": "lightweight verification check (< 1,000 tokens)",
"medium": "standard step-by-step reasoning",
"high": "deep exhaustive exploration with self-reflection (> 8,000 tokens)"
}
}
}
})
}
}
/// 2. Confidence-Gated LLM Escalation.
pub struct ConfidenceGate;
impl ConfidenceGate {
/// Decides whether a fast Kai decision stands or requires escalation to an LLM.
///
/// If margin (P_top - P_second) >= tau, returns None (Kai decision stands).
/// If margin < tau, returns Some(target_llm) for fallback escalation.
pub fn evaluate_escalation(
probabilities: &[f64],
tau: f64,
target_llm: &str,
) -> Option<&'static str> {
if probabilities.len() < 2 {
return None;
}
let mut sorted = probabilities.to_vec();
sorted.sort_by(|a, b| b.partial_cmp(a).unwrap_or(std::cmp::Ordering::Equal));
let margin = sorted[0] - sorted[1];
if margin < tau {
Some(Box::leak(target_llm.to_string().into_boxed_str()))
} else {
None
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_model_suitability_request() {
let task = TaskComplexityState {
prompt_token_count: 520,
task_domain: "customer_support".into(),
requires_formal_reasoning: false,
requires_code_execution: false,
latency_deadline_ms: 500,
max_cost_nano_usd: 10000,
};
let req = ModelSuitabilityRecipe::build_request(&task);
assert_eq!(req["model"], "kai");
assert!(req["questions"]["selected_tier"].is_object());
assert!(req["questions"]["thinking_depth"].is_object());
}
#[test]
fn test_confidence_escalation() {
// High confidence: margin = 0.85 - 0.10 = 0.75 >= 0.40 -> Stands
let stand = ConfidenceGate::evaluate_escalation(&[0.85, 0.10, 0.05], 0.40, "zen-6");
assert!(stand.is_none());
// Low confidence: margin = 0.48 - 0.45 = 0.03 < 0.40 -> Escalates to Zen
let escalate = ConfidenceGate::evaluate_escalation(&[0.48, 0.45, 0.07], 0.40, "zen-6");
assert_eq!(escalate, Some("zen-6"));
}
}