Model or dataset
dreadnode/rigging avatar
dreadnode/rigging

dreadnode/rigging: a Pydantic-first LLM framework for production Python

Lightweight LLM Interaction Framework

417 stars32 forksPythonMIT

At a glance

What is it?
Rigging wraps LiteLLM in typed prompts, chat pipelines and connection strings so that language model calls look like ordinary async Python. It is a good fit when you want structured output and tool use without adopting a full agent runtime, and a poor fit when you need a hosted control plane or a large connector ecosystem.
Who is it for?
Adopt rigging if your team already writes async Python and wants Pydantic-validated model output, typed prompt functions and a single connection-string format for many providers, and if you are willing to pin litellm and verify structured parsing against the specific models you call.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 1 day ago.
What is it written in?
Mainly Python, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 15, 2026, and from our analysis. They are not legal advice.

DEEP OPEN-SOURCE ANALYSIS

The problem rigging addresses in a Python codebase

Most teams reach a point where a language model call sits inside application code and the surrounding plumbing becomes the work. You need a provider client, a retry policy, a way to turn the model's text back into a typed object, a place to attach tracing, and some scheme for switching between models during development and production. Rigging's answer is to keep all of that inside ordinary Python rather than in a separate orchestration layer. The README describes it as a lightweight LLM framework whose goal is making language models usable in production code, and the example it leads with is a decorated async function whose docstring becomes the prompt and whose return annotation becomes the expected shape.

The intended audience is Python engineers, not prompt authors working in a separate tool. The pyproject.toml lists pydantic, litellm, loguru and mcp as core dependencies, which tells you the framework assumes you are comfortable with type hints and async. The examples directory points the same way: chat.py, rag.py, jupyter.py, bandit.py, dvra.py, robopages.py. Several of those are security-oriented (an OverTheWire Bandit agent, a deliberately vulnerable restaurant agent, an nmap scan through robopages), which matches the fact that the project is built by dreadnode and used there daily according to the README. If your work looks like scripting against models rather than shipping a consumer chat product, the examples are closer to your shape of problem.

How rigging works: generators, pipelines and Pydantic parts

The architecture visible in the README has three layers. At the bottom is a generator, obtained through rg.get_generator with a connection string such as "claude-3-sonnet-20240229" or "gpt-4-turbo,api_key=...". The README compares these strings to database connection strings, and that is the right mental model: the generator carries the model identity and configuration, and the same string format is reused across providers because LiteLLM is the default backend. API keys can be embedded in the string or supplied through standard environment variables such as OPENAI_API_KEY, MISTRAL_API_KEY and ANTHROPIC_API_KEY.

Above the generator sits a pipeline. You build a chat from a list of role and content dictionaries, or a raw completion, then await pipeline.run(). The returned chat object holds the conversation, and the README's pirate example shows the printed transcript with system, user and assistant turns. Because the pipeline is a value you construct before running it, the README's feature list can offer forking, continuations and generation parameter overloads: you alter the pipeline and run it again rather than mutating global state.

The top layer is parsing. The README states that structured Pydantic models can be used interchangeably with unstructured text output, and that prompts can be defined as Python functions with type hints and docstrings. In the opening example, the return annotation list[str] and the docstring "Provide famous authors." are the entire prompt definition. Tool use is handled by the framework even for models that do not support tools at the API level, which is a meaningful abstraction: it means your tool-calling code does not have to branch on model capability. Tracing hooks into Logfire, and the README also lists callbacks, metadata, serialization and async batching as first-class features. The optional dependency groups in pyproject.toml (llm for vllm, transformers and accelerate; data for pandas and elasticsearch; examples for asyncssh, click, httpx, aiodocker and websockets) show that the heavy machinery is deliberately kept out of the default install.

Installing rigging and running a first typed prompt

Every release is published to PyPI, so installation is a single command. The README gives no virtual environment instructions, which is normal for a library, but the declared Python range in pyproject.toml is >=3.10,<3.14, so check your interpreter before installing.

bash
pip install rigging

If you prefer to build from source, the README's instructions are to change into the repository and use Poetry:

bash
cd rigging/
poetry install

Before the first call you need credentials for whichever provider you intend to use. The README shows both styles, an api_key inside the generator string or the provider's standard environment variable. For OpenAI models the variable is OPENAI_API_KEY.

bash
export OPENAI_API_KEY=...

The smallest useful program follows the README's three-step pattern: get a generator, build a chat pipeline, run it. The code below uses the same structure as the README example, with the model identifier replaced by one of the strings shown there.

python
import rigging as rg
import asyncio

async def main():
    generator = rg.get_generator("claude-3-sonnet-20240229")
    pipeline = generator.chat(
        [
            {"role": "system", "content": "Talk like a pirate."},
            {"role": "user", "content": "Say hello!"},
        ]
    )
    chat = await pipeline.run()
    print(chat.conversation)

asyncio.run(main())

What you should see is a transcript with the system and user messages echoed back alongside an assistant reply. If the provider rejects the request, the error surfaces from LiteLLM rather than from rigging, because LiteLLM performs the HTTP call. The decorated-function style is the other entry point worth trying first, since it collapses prompt and schema into one definition:

python
import rigging as rg

@rg.prompt(generator_id="gpt-4")
async def get_authors(count: int = 3) -> list[str]:
    """Provide famous authors."""

print(await get_authors())

The README shows the output as a Python list of author names, which is the point of the library: the model's text arrives already parsed into the annotated type. When that parsing fails, the failure is a validation error from Pydantic, not a silent string.

Where rigging is the wrong tool

The README describes rigging as lightweight and as a library, and the repository layout backs that up: there is a rigging package, docs, examples and tests, with no server component. If you need a hosted control plane that schedules runs, stores traces for a team, or exposes a UI for non-engineers, rigging does not provide one. Tracing is integrated with Logfire, which means you are relying on an external service for that visibility rather than getting it from the framework itself.

Structured output is the feature most likely to disappoint in practice. The README says Pydantic models can be used interchangeably with text output, but it does not claim that every model produces parseable structured output reliably. Models and providers differ in how well they follow schemas, and a validation failure is a runtime error in your application. Test your specific model before designing around parsed output.

The dependency situation is the second constraint. pyproject.toml pins litellm to exactly 1.79.3, not a range. That is a deliberate stability choice given how quickly LiteLLM moves, but it means upgrading LiteLLM is a rigging upgrade, and it can conflict with another package in the same environment that wants a different LiteLLM version. The Python ceiling of <3.14 has the same character: it is a compatibility boundary, and you should read it as one rather than as a suggestion.

Finally, the README does not document rollback behaviour, retry semantics or what happens when a pipeline run fails partway through a batch. Those are the questions to answer from the docs or from reading the source before you put rigging in a critical path.

rigging compared with LangChain and the OpenAI SDK

The most direct comparison is LangChain. LangChain is a broad framework with a large catalogue of integrations, document loaders, retrievers and agent abstractions, and it tends to define its own object model that your code adopts. Rigging takes the opposite position: it keeps the surface small, leans on LiteLLM for provider breadth instead of maintaining its own connectors, and leans on Pydantic for the data model rather than inventing one. The practical difference shows up in how much of the framework appears in your code. A rigging call is an async function returning a typed value; a LangChain chain is assembled from framework-specific components. If you have ever wanted to remove a framework from a codebase and found it threaded through everything, that is the failure mode rigging is designed to avoid.

The second comparison is the official OpenAI SDK. It is excellent and well documented if you only call OpenAI models, and it now supports structured outputs. The difference is scope: switching providers means switching clients and adapting your code, whereas rigging's connection string is meant to make that a string change. The trade-off is that you inherit LiteLLM's translation layer and its bugs, plus the pinned version. For a single-provider project, the SDK is the simpler dependency. For a project that needs to move between Anthropic, OpenAI, Mistral and a local vLLM server, the connection-string approach earns its keep, and the optional llm extra exists precisely for the local case.

Maintenance, upgrades and the MIT licence

The repository is not archived, and the last push was on 2026-09-14, so the project is being worked on. The most recent tagged release listed is v3.3.2 from 2025-08-01, while pyproject.toml declares version 3.3.5, which suggests releases are tagged less frequently than the main branch moves. For a library, that gap matters: if you install from PyPI you get tagged versions, and if you install from source you get whatever is on main.

Upgrade cost is dominated by the pinned litellm dependency. Because litellm is fixed at 1.79.3, a rigging upgrade is also a LiteLLM upgrade, and provider API changes arrive through that door. The Python range narrows the other direction: you cannot run rigging on 3.14 or later until the constraint is widened. The optional extras mean you can keep the install small, and pulling in the llm extra for local vLLM or transformers work brings a much larger dependency tree than the base package.

The licence is MIT, declared in pyproject.toml and in the LICENSE file at the repository root. MIT is permissive: it allows commercial use, modification and redistribution provided the copyright notice and permission notice are retained. That is a summary of what the licence text does, not legal advice; if your organisation has licence review requirements, the file to read is LICENSE. One dependency note worth raising with whoever reviews licences: the package depends on dreadnode, a separate library from the same organisation, so the effective dependency set is larger than the rigging name alone suggests.

Editorial conclusion

Adopt rigging if your team already writes async Python and wants Pydantic-validated model output, typed prompt functions and a single connection-string format for many providers, and if you are willing to pin litellm and verify structured parsing against the specific models you call. Do not adopt it if you need a hosted orchestration service, a large first-party connector catalogue, or a framework that manages deployment and scaling for you; rigging is a library, and the README points at the docs rather than at any runtime product. Before committing, check three things: that your Python version falls inside the >=3.10,<3.14 range declared in pyproject.toml, that the pinned litellm version resolves cleanly with your other dependencies, and that your chosen model actually honours the structured output you expect, since the README does not claim it works everywhere.

Frequently asked questions

What is rigging in the context of this Python library?

Rigging is a lightweight LLM interaction framework from dreadnode, described in its README as a way to use language models in production code. It provides generators from connection strings, chat and completion pipelines, Pydantic structured parsing, tool use and tracing support.

How do you set up rigging for a first run?

Install it with pip install rigging, then either export a provider key such as OPENAI_API_KEY or pass api_key inside the generator string. After that, the README's pattern is to get a generator, build a chat pipeline and await pipeline.run().

How do you use rigging with a specific model?

Models are selected through the generator connection string, for example rg.get_generator("claude-3-sonnet-20240229"). The README states that LiteLLM is the default generator, so any model LiteLLM supports is reachable, and vLLM and transformers models are also listed as supported.

Official sources

  1. dreadnode/rigging on GitHub
  2. License: MIT
  3. Project website
  4. README
  5. Releases
Community notes

Community notes