# 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

*Cargo.toml, a project file beside the sample*

```
[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(())
}
```

*Build and run it beside Cargo.toml*

```
mkdir -p src/bin
cp first-call.rs src/bin/
cargo run --quiet --bin first-call
```

Not 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](/functions/) 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](/install/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](/install/backends/typesafe/) | `typesafe` | `TYPESAFE_API_KEY` |
| [Liquid d1](/install/backends/liquid/) | `liquid` | `LIQUIDAI_API_KEY`, then `LIQUID_API_KEY` |
| [Ollama](/install/backends/ollama/) | `ollama` | `OLLAMA_API_KEY`. A local address needs no key. |

*The Ollama engine names the second port, 11535. On the default port, leave base_url out.*

```
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(())
}
```

*Build and run it beside Cargo.toml*

```
mkdir -p src/bin
cp backends.rs src/bin/
cargo run --quiet --bin backends
```

`EngineBuilder::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](/install/configuration/#configuration-file) page shows the file.

### OpenAI Decisions API from Rust

OpenAI announced its Decisions API on 2026-09-29 in its [DevDay 2026 recap](https://openai.com/index/devday-2026-recap/). OpenAI has published no address, schema or price, so ThinkThen cannot call it. The [OpenAI Decisions API](/install/backends/openai/) page says what ThinkThen knows.

## What is different in Rust

- Calls block. No async runtime comes with it.

On GitHub: [github.com/botassembly/thinkthen](https://github.com/botassembly/thinkthen)
