thunc
think + function. Call an LLM like a typed Python function.
Status: beta (v0.1). Expect bugs; the API may change. Feedback and issues are welcome.
import thunc
thunc.configure(backend="claude-code")
@thunc.function
def urgency(ticket: str) -> int:
"""Rate how urgent this ticket is, from 1 (can wait) to 5 (customer is blocked)."""
...
urgency("I was charged twice!") # -> 4, a checked int
The answer is parsed into the declared type. If it doesn't fit, the model is asked again, and
after that thunc.ThuncError is raised. The library uses the standard library only and needs
Python 3.10+.
Install
pip install thunc # standard library only
pip install "thunc[anthropic]" # adds the Claude API backend
Try it
Clone the repo and run the examples from its root. No install is needed; the examples run through your local Claude Code login:
git clone https://github.com/Eltarras/thunc && cd thunc
python3 -m examples.hello
python3 -m examples.support_inbox
THUNC_BACKEND=codex python3 -m examples.log_triage
Two ways to write a prompt
| When | ||
|---|---|---|
@thunc.function |
The prompt is fixed and should read like code | The docstring is the prompt, the parameters are the inputs, the return annotation is the type |
thunc.call(...) |
The prompt is built in code (from config, in a loop, loaded from a file) | thunc.call(f"Translate into {lang}.", {"text": note}) |
@thunc.function(instructions=some_string) combines the two: a typed, reusable function whose
prompt is generated.
Keep user data out of the instructions. Your own text can go in the instructions string. Anything from users, files or the web goes in the inputs:
@thunc.functiondoes this automatically.- With
thunc.callit's up to you. In a live test, a hostile email pasted in with an f-string tricked the model 3 out of 3 times. Passed as an input, it failed 3 out of 3 times.
API
@thunc.function |
Turns a signature + docstring into an AI-backed function. Options: instructions=, ensure=, retries=, backend=, model=. The body must be empty (...); real code raises TypeError. async def works |
thunc.call(instructions, inputs=None, *, returns=str, ensure=None, retries=2, backend=None, model=None) |
One prompt. Inputs are sent separately from the instructions |
thunc.map(func, items, workers=8) |
Runs calls in parallel, keeping the input order. Each call takes 4–8s, so this is the main speed lever |
thunc.configure(backend=, api_key=, model=, timeout=, trace=) |
Process-wide settings. trace="calls.jsonl" logs every call |
thunc.ThuncError |
Raised when no valid answer arrives after the retries |
Return types: str, bool, int, float, Literal[...], list[T], dict[str, T],
T | None, and dataclasses (built into real instances).
ensure= adds your own check, for example ensure=lambda n: 1 <= n <= 5. A failed check is
sent back to the model and retried.
Backends:
anthropicis the Claude API:configure(api_key=...)orANTHROPIC_API_KEY, pluspip install anthropic.claude-codeandcodexcall your local CLI login, and are meant for cheap testing.
The backend can also be set with THUNC_BACKEND.
Type checking: signatures and return types are visible to mypy and Pyright. mypy reports
empty bodies; turn that off with disable_error_code = ["empty-body"].
Examples
| hello.py | The smallest call |
| support_inbox.py | Docstring functions returning a Literal, an int with ensure=, a dataclass, and a reply; tickets processed in parallel |
| dynamic_prompts.py | Prompts built from a style guide with thunc.call, and a grading function generated from a rubric |
| log_triage.py | Plain Python and AI functions mixed, with tracing |
Code
thunc/
__init__.py public API
decorator.py @thunc.function
core.py thunc.call, thunc.map, tracing
schema.py return types: describe, parse, validate
config.py settings and backend selection
backends.py anthropic, claude-code, codex
errors.py ThuncError
tests/ offline: a fake backend, never a real model
live_tests/ against a real model: hello, a yes/no decision, messy text to a dict
examples/
Limitations
- There's no caching and no record/replay yet, so repeated calls cost again.
- The API-key backend hasn't been run live yet. It's only checked against the SDK's types.
Literalresults fromthunc.callare typed asAny.@thunc.functionhas no such gap.- Docstrings disappear under
python -OO. Useinstructions=there.
Development
python3 -m venv .venv && .venv/bin/pip install -e ".[anthropic,dev]"
.venv/bin/pytest # offline tests (these run in CI)
.venv/bin/pytest live_tests # real model calls through your Claude Code login; costs quota
.venv/bin/ruff check . && .venv/bin/mypy --strict thunc
License
Metadata
Release files for thunc 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| thunc-0.1.0.tar.gz | 18.6 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| thunc-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 32.7 kB
Release files / thunc-0.1.0.tar.gz
| Download URL | thunc-0.1.0.tar.gz |
|---|---|
| Size | 18.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
ff87c16f8a89db65753ccb024a99c21bc26ccb686821f43d6e90d27ac19571b1
|
|
BLAKE2b-256 checksum How to use checksums |
a9c4a48a94d3d3d62b9bdbc68bf573eb293801332ccabdac6a1df6b61fe4fabd
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 3, 2026.
Transparency logRelease files / thunc-0.1.0-py3-none-any.whl
| Download URL | thunc-0.1.0-py3-none-any.whl |
|---|---|
| Size | 14.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
d7cc95a7487abe621e3ab25915cd2dfc3921c9e7a89c832c2f584b53493713d3
|
|
BLAKE2b-256 checksum How to use checksums |
e3b78e28ec0849973b24b38a0daed4cd3728d23ec6ff8c4ff687a0cd41b7c210
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 3, 2026.
Transparency log