Rust functions that answer questions about text
Call the engine directly. The compiler makes you handle not sure.
Install
| Channel |
|---|
cargo add thinkthen |
Set THINKTHEN_API_KEY in the environment.
A first call
[package]
name = "first-call"
edition = "2024"
[dependencies]
thinkthen = "0.0.1"use thinkthen::{Description, Engine, Question};
thinkthen::choices! {
enum Team {
Billing => "billing",
Shipping => "shipping",
Account => "account",
}
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
let tt = Engine::from_env()?;
let question = "Which team owns this?";
let billing =
Description::text("Invoices, fees, and refunds.")?;
let shipping =
Description::text("Parcels and delivery.")?;
let account =
Description::text("Logins and passwords.")?;
let owner = Question::choose::<Team>(question)?
.option(Team::Billing, Some(billing))?
.option(Team::Shipping, Some(shipping))?
.option(Team::Account, Some(account))?
.build()?;
let texts = [
"Please refund the extra fee on my invoice.",
"My parcel went to the wrong address.",
"I cannot reset my password.",
];
let teams = texts
.iter()
.map(|text| {
tt.choose(&owner, text)
.map(|call| call.into_value())
})
.collect::<Result<Vec<_>, _>>()?;
let expected = [
Some(Team::Billing),
Some(Team::Shipping),
Some(Team::Account),
];
assert_eq!(teams, expected);
Ok(())
}mkdir -p src/bin
cp first-call.rs src/bin/
cargo run --quiet --bin first-callNot sure comes back as Answer::Unsure from decide, and None from choose. Branch on it and send those cases to a person. Each function page shows its Rust call.
Run facts
Call::facts() counts this call.
Errors
Every call returns a Result. Its Error names one of six kinds. A started call that fails keeps its facts in Error::facts().
Settings and recording
EngineBuilder sets each setting. record writes a recording to a folder. replay answers from that recording and sends nothing, even on a miss. The Settings page lists every setting.
Jev, Liquid d1 and Ollama from Rust
EngineBuilder::backend names the backend in code, and it outranks the environment. The sample builds one engine for each backend and asks each the same question.
| Backend | Name | Key variable |
|---|---|---|
| TypeSafe Jev | typesafe | TYPESAFE_API_KEY |
| Liquid d1 | liquid | LIQUIDAI_API_KEY, then LIQUID_API_KEY |
| Ollama | ollama | OLLAMA_API_KEY. A local address needs no key. |
use thinkthen::{Answer, EngineBuilder, Question};
fn main() -> Result<(), Box<dyn std::error::Error>> {
let typesafe = EngineBuilder::from_env()?
.backend("typesafe")?
.build()?;
let liquid = EngineBuilder::from_env()?
.backend("liquid")?
.build()?;
let ollama = EngineBuilder::from_env()?
.backend("ollama")?
.base_url("http://localhost:11535/v1")?
.build()?;
let question = "Does the customer ask for a refund?";
let refund = Question::decide(question)?.cut();
let broken =
"Please refund my order. It arrived broken.";
let thanks = "Thanks for the quick help yesterday!";
for tt in [typesafe, liquid, ollama] {
let yes = tt.decide(&refund, broken)?.into_value();
let no = tt.decide(&refund, thanks)?.into_value();
assert_eq!(yes, Answer::Yes);
assert_eq!(no, Answer::No);
}
Ok(())
}mkdir -p src/bin
cp backends.rs src/bin/
cargo run --quiet --bin backendsEngineBuilder::from_env also reads THINKTHEN_BACKEND and THINKTHEN_BASE_URL, so the shell settings the other language pages show work here too.
The configuration file's backend names a default backend for every run. The Configuration page shows the file.
OpenAI Decisions API from Rust
OpenAI announced its Decisions API on 2026-09-29 in its DevDay 2026 recap. OpenAI has published no address, schema or price, so ThinkThen cannot call it. The OpenAI Decisions API page says what ThinkThen knows.
What is different in Rust
- Calls block. No async runtime comes with it.
On GitHub: github.com/botassembly/thinkthen