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
{
"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 shows its Python call.
To apply one question to a list or a stream, read Functional patterns.
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 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 | 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. |
export THINKTHEN_BACKEND=typesafe
export TYPESAFE_API_KEY=...export THINKTHEN_BACKEND=liquid
export LIQUIDAI_API_KEY=...export THINKTHEN_BACKEND=ollama
export THINKTHEN_BASE_URL=http://localhost:11535/v1import 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 FalseThe configuration file's backend names a default backend for every run. The Configuration page shows the file.
OpenAI Decisions API from Python
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 Python
- A list goes in and
Call.valueholds 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