# Python functions that answer questions about text

Pass a string or a list. Read the answer from `Call.value`.

## Install

| Channel |
| --- |
| `pip install thinkthen` |
| `uv add thinkthen` |

Set `THINKTHEN_API_KEY` in the environment.

## A first call

*form.json, a file the sample reads*

```
{
  "version": 1,
  "questions": {
    "steps": {
      "decide": "Does the report give steps to reproduce?"
    },
    "area": {
      "choose": "Which part of the app is this?",
      "options": [
        "export",
        "login",
        "billing"
      ]
    },
    "impact": {
      "score": "How much does this block the user?",
      "levels": [
        "None.",
        "Slows them.",
        "Blocks work."
      ]
    }
  }
}
```

```
import thinkthen as tt

question = "Does the customer ask for a refund?"

broken = "Please refund my order. It arrived broken."
is_refund = tt.decide(question, broken).value
assert is_refund is True

refund = tt.question(decide=question, threshold=(0.2, 0.8))
send_back = "I want to send this back."
is_refund = tt.decide(refund, send_back).value
assert is_refund is None

reports = [
    (
        "CSV export fails every time. "
        "Steps: open a report,\n"
        "click Export, pick CSV. "
        "My month-end numbers are stuck.\n"
    ),
    (
        "Steps: open the login page, enter a password, "
        "press Enter. The page spins and "
        "nobody can sign in."
    ),
    (
        "The Pay button on the billing page is a slightly "
        "different blue. No steps, I just noticed it."
    ),
]
triage = tt.annotate("form.json", reports).value
assert triage == [
    {"steps": True, "area": "export", "impact": 1.99},
    {"steps": True, "area": "login", "impact": 2.0},
    {"steps": False, "area": "billing", "impact": 0.01},
]
```

Not sure comes back as `None`. Branch on it and send those cases to a person. Each [function page](/functions/) shows its Python call.

To apply one question to a list or a stream, read [Functional patterns](/learn/functional/).

## Run facts

`Call.facts` counts this call.

## Errors

Every failure raises a `ThinkThenError`. Its subclass names the kind: `UsageError`, `BackendError`, `LocalError`, `DeadlineError`, `DefectError` or `Cancelled`.

## Settings and recording

`tt.Engine` takes each setting as a keyword and reads the environment for the rest. `record=` writes a recording to a folder. `replay=` answers from that recording with no connection. The [Settings](/install/settings/) page lists every setting.

## Jev, Liquid d1 and Ollama from Python

Python reads its backend from the environment. Set `THINKTHEN_BACKEND` to the name below. Set the backend's key variable in the shell, and the same sample asks that backend.

| 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. |

*Ask TypeSafe Jev.*

```
export THINKTHEN_BACKEND=typesafe
export TYPESAFE_API_KEY=...
```

*Ask Liquid d1.*

```
export THINKTHEN_BACKEND=liquid
export LIQUIDAI_API_KEY=...
```

*Ask Ollama. This block names a second port, 11535, as the site's runs do. On the default port, leave THINKTHEN_BASE_URL out.*

```
export THINKTHEN_BACKEND=ollama
export THINKTHEN_BASE_URL=http://localhost:11535/v1
```

```
import thinkthen as tt

question = "Does the customer ask for a refund?"
broken = "Please refund my order. It arrived broken."
thanks = "Thanks for the quick help yesterday!"
broken_is_refund = tt.decide(question, broken).value
thanks_is_refund = tt.decide(question, thanks).value
assert broken_is_refund is True
assert thanks_is_refund is False
```

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 Python

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 Python

- A list goes in and `Call.value` holds the answered list. The list crosses into the engine once.
- `tt.question()` builds a question that carries its own threshold. Reuse it wherever you ask.

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