Skip to main content

thunc

CI

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

It runs on the Claude API, the OpenAI API, a local model (LM Studio, or any server that speaks the OpenAI API), or your Claude Code or Codex login.

Install

pip install thunc               # standard library only
pip install "thunc[anthropic]"  # adds the Claude API backend
pip install "thunc[openai]"     # adds the OpenAI 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

With an API key instead, install the SDK and pick the backend: THUNC_BACKEND=openai with OPENAI_API_KEY, or THUNC_BACKEND=anthropic with ANTHROPIC_API_KEY.

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.function does this automatically.
  • With thunc.call it'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:

  • anthropic is the Claude API: configure(api_key=...) or ANTHROPIC_API_KEY, plus pip install "thunc[anthropic]".
  • openai is the OpenAI API: configure(backend="openai", api_key=...) or OPENAI_API_KEY, plus pip install "thunc[openai]". The default model is gpt-5.5. OPENAI_BASE_URL points it at any server that speaks the OpenAI Responses API.
  • claude-code and codex call your local CLI login, and are meant for cheap testing.

Local models: the openai backend works with a local server through OPENAI_BASE_URL. This has been tested with LM Studio running openai/gpt-oss-20b:

# OPENAI_BASE_URL=http://localhost:1234/v1  OPENAI_API_KEY=lm-studio  (any non-empty key works)
thunc.configure(backend="openai", model="openai/gpt-oss-20b")

Small models sometimes wrap an answer, like {"rating": 5} for an int, and need the retry more often.

The backend can also be set with THUNC_BACKEND. With none set, ANTHROPIC_API_KEY (or a configure(api_key=...) alone) selects anthropic, and otherwise OPENAI_API_KEY selects openai.

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, openai, 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.
  • Literal results from thunc.call are typed as Any. @thunc.function has no such gap.
  • Docstrings disappear under python -OO. Use instructions= there.

Development

python3 -m venv .venv && .venv/bin/pip install -e ".[anthropic,openai,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
THUNC_BACKEND=anthropic .venv/bin/pytest live_tests   # the same, through the Claude API (needs ANTHROPIC_API_KEY)
THUNC_BACKEND=openai .venv/bin/pytest live_tests      # the same, through the OpenAI API (needs OPENAI_API_KEY)
.venv/bin/ruff check . && .venv/bin/mypy --strict thunc

License

MIT

Metadata

Release files for thunc 0.1.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for thunc 0.1.1
File Size Uploaded
thunc-0.1.1.tar.gz 21.9 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for thunc 0.1.1
File Interpreter ABI Platform
thunc-0.1.1-py3-none-any.whl Python 3 none any Details

Total release size: 36.9 kB

Release files / thunc-0.1.1.tar.gz

Download URL thunc-0.1.1.tar.gz
Size 21.9 kB
Tags Source
SHA-256 checksum
How to use checksums
5b6f844fcf65f064f7299bdec3456a52e2b2c535eee68ff63ec9b1680af2b445
BLAKE2b-256 checksum
How to use checksums
83227cb1b52e7e0ddc77ba00755aa7f0671b8cf41ba67c9cdb94e295ac648264
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

Release files / thunc-0.1.1-py3-none-any.whl

Download URL thunc-0.1.1-py3-none-any.whl
Size 15.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
a997858632c10951266eff577144e1296eaf0b28b2050ff2527d767dc2a66782
BLAKE2b-256 checksum
How to use checksums
5d10e56b7796b4fb9e8dd4a7a6084f4319c8ec3f8771177132a58a34d0c7eab9
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

Release history Release notifications | RSS feed

This release

0.1.1 This release

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page