TypedLM

Showcase

Each example shows real output produced by TypedLM itself, from files in the project repository. Input on the left, output on the right.

Classification

crates/typedlm/examples/classification.rsDetail: Classification →
//! Classify support tickets into enums.
//!
//! ```sh
//! TYPEDLM_MODEL=qwen3:32b cargo run --example classification
//! ```

mod common;

use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use typedlm::prelude::*;

#[derive(Debug, Serialize, Deserialize, JsonSchema)]
enum Urgency {
    Low,
    Medium,
    High,
}

#[derive(Debug, Serialize, Deserialize, JsonSchema)]
enum Category {
    Billing,
    Technical,
    Shipping,
    /// Anything that fits none of the other categories.
    Other,
}

#[derive(Debug, Serialize, Deserialize, JsonSchema)]
struct TicketClassification {
    urgency: Urgency,
    category: Category,
}

/// Classify a customer support ticket by urgency and category.
#[derive(TypedLm, Serialize)]
#[lm(output = TicketClassification)]
struct ClassifyTicket {
    /// The ticket as written by the customer.
    text: String,
}

#[tokio::main(flavor = "current_thread")]
async fn main() -> Result<(), Error> {
    let classify = Program::<ClassifyTicket, _>::new(common::provider()).temperature(0.0);

    // `run` returns the typed output; a single `String` field lets it take text.
    let ticket = classify
        .run("Production database is down since 9:00")
        .await?;
    match ticket.urgency {
        Urgency::High => println!("escalate: {ticket:?}"),
        Urgency::Medium | Urgency::Low => println!("queue: {ticket:?}"),
    }

    // `execute` also reports what the call cost.
    let execution = classify.execute("I was charged twice this month").await?;
    println!(
        "{:?} — {} attempt(s), {} tokens, model {}",
        execution.output,
        execution.attempts,
        execution.usage.input_tokens + execution.usage.output_tokens,
        execution.model
    );
    Ok(())
}
escalate: TicketClassification { urgency: High, category: Technical }
TicketClassification { urgency: High, category: Billing } — 1 attempt(s), 318 tokens, model qwen3:32b
  • enums
  • run
  • execute

Invoice extraction

crates/typedlm/examples/extraction.rsDetail: Invoice extraction →
//! Extract a nested invoice structure and check it against domain rules.
//!
//! ```sh
//! TYPEDLM_MODEL=qwen3:32b cargo run --example extraction
//! ```

mod common;

use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use typedlm::prelude::*;

#[derive(Debug, Serialize, Deserialize, JsonSchema)]
struct LineItem {
    description: String,
    quantity: u32,
    /// Net unit price in cents.
    unit_price_cents: u64,
}

#[derive(Debug, Serialize, Deserialize, JsonSchema)]
struct Invoice {
    number: String,
    /// Issue date as YYYY-MM-DD.
    date: String,
    items: Vec<LineItem>,
    /// Net total in cents, before tax.
    net_cents: u64,
    /// Grand total in cents, including tax.
    total_cents: u64,
    /// Anything unusual about the invoice, if mentioned.
    note: Option<String>,
}

/// Extract the invoice data from the text. Amounts are in cents.
#[derive(TypedLm, Serialize)]
#[lm(output = Invoice, validate = items_add_up)]
struct ExtractInvoice {
    text: String,
}

/// Rules the schema cannot express. Failing ones are sent back to the model,
/// so each message says what to fix.
fn items_add_up(invoice: &Invoice) -> Result<(), Vec<String>> {
    let items: u64 = invoice
        .items
        .iter()
        .map(|item| u64::from(item.quantity) * item.unit_price_cents)
        .sum();
    let mut errors = Vec::new();
    if items != invoice.net_cents {
        errors.push(format!(
            "the line items add up to {items} cents but net_cents is {}; check quantities and unit prices",
            invoice.net_cents
        ));
    }
    if invoice.total_cents < invoice.net_cents {
        errors.push("total_cents must not be smaller than net_cents".into());
    }
    if errors.is_empty() {
        Ok(())
    } else {
        Err(errors)
    }
}

const TEXT: &str = "Invoice no. RE-2026-0042, issued 14 September 2026.
2 x Server maintenance at 150.00 EUR each
1 x Emergency call-out at 89.50 EUR
Subtotal 389.50 EUR, VAT 19 % 74.01 EUR, total 463.51 EUR.
Payable within 14 days.";

#[tokio::main(flavor = "current_thread")]
async fn main() -> Result<(), Error> {
    let extract = Program::<ExtractInvoice, _>::new(common::provider()).temperature(0.0);
    let invoice = extract.run(TEXT).await?;

    println!("{} from {}", invoice.number, invoice.date);
    for item in &invoice.items {
        println!(
            "  {} x {} à {:.2}",
            item.quantity,
            item.description,
            item.unit_price_cents as f64 / 100.0
        );
    }
    println!(
        "net {:.2}, total {:.2}",
        invoice.net_cents as f64 / 100.0,
        invoice.total_cents as f64 / 100.0
    );
    if let Some(note) = invoice.note {
        println!("note: {note}");
    }
    Ok(())
}
RE-2026-0042 from 2026-09-14
  2 x Server maintenance à 150.00
  1 x Emergency call-out à 89.50
net 389.50, total 463.51
note: Payable within 14 days.
  • nested types
  • Option
  • validate

Evaluation report

crates/typedlm/examples/evaluation.rsDetail: Evaluation report →
//! Measure a classifier on a labelled dataset.
//!
//! ```sh
//! TYPEDLM_MODEL=qwen3:32b cargo run --example evaluation
//! ```
//!
//! `examples/data/tickets.jsonl` labels only some fields per ticket; each line is
//! checked against the output type when loading. Keep datasets much larger than
//! this one: the report's confidence interval shows how little 8 examples say.

mod common;

use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use typedlm::eval::{Dataset, ExactMatch, evaluate};
use typedlm::prelude::*;

#[derive(Debug, Serialize, Deserialize, JsonSchema)]
enum Urgency {
    Low,
    Medium,
    High,
}

#[derive(Debug, Serialize, Deserialize, JsonSchema)]
enum Category {
    Billing,
    Technical,
    Shipping,
    /// Anything that fits none of the other categories.
    Other,
}

#[derive(Debug, Serialize, Deserialize, JsonSchema)]
struct TicketClassification {
    urgency: Urgency,
    category: Category,
}

/// Classify a customer support ticket by urgency and category.
#[derive(TypedLm, Serialize, Deserialize, Clone, Debug)]
#[lm(output = TicketClassification)]
struct ClassifyTicket {
    /// The ticket as written by the customer.
    text: String,
}

#[tokio::main(flavor = "current_thread")]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let path = concat!(env!("CARGO_MANIFEST_DIR"), "/examples/data/tickets.jsonl");
    let dataset = Dataset::<ClassifyTicket>::from_jsonl(path)?;

    let classify = Program::<ClassifyTicket, _>::new(common::provider()).temperature(0.0);
    let report = evaluate(&classify, &dataset, &ExactMatch, 4).await;

    println!("{report}");
    for (index, error) in &report.failures {
        println!("example {index} failed: {error}");
    }
    // The report is plain data, e.g. for storing next to the dataset.
    println!("{}", serde_json::to_string(&report)?);
    Ok(())
}
ClassifyTicket
model           qwen3:32b
examples        8
exact match     87.5 %  (95 % CI 52.9 % – 97.8 %)
  category      85.7 %  (6/7)
  urgency       100.0 %  (5/5)
valid           100.0 %
repaired        0.0 %
failed          0.0 %
latency p50     96248 ms
latency p95     121426 ms
tokens          1099 in / 1925 out
  • dataset
  • ExactMatch
  • confidence interval