A framework-free LLM engineering toolkit in one Python package: provider adapters, prompt and context engineering, structured outputs, a tool layer, a bounded agent loop, and a harness for long-running work.
Five layers, each built on the one below it, each with its own measurements.
| Layer | Package | What it adds |
|---|---|---|
| 1 | provider/ |
Token/cost/latency measurement, four adapters, retry and fallback |
| 2 | prompts/ |
Versioned prompts, context budget, structured output, a repair loop |
| 3 | tools/ |
Schemas from signatures, three provider exports, a five-gate executor |
| 4 | agent/ |
A bounded loop, four budgets, loop detection, checkpoints |
| 5 | harness/ |
Segments, spill, compaction, a memory store, sub-agents |
343 tests, no vendor SDK outside provider/adapters/, and every layer
runnable without an API key.
Install
pip install "aimai-kit[providers]"
From source:
uv sync --all-extras --group dev
cp .env.example .env # add your keys
uv run pytest # 343 tests, live ones excluded
Quick start
from aimai_kit.provider.adapters import make_adapter
from aimai_kit.provider.types import ChatRequest, Message, Role
client = make_adapter("anthropic:claude-opus-5")
result = client.complete(
ChatRequest(
messages=[Message(role=Role.USER, content="Say hello.")],
system="Be brief.",
max_output_tokens=64,
)
)
print(result.text, result.usage.input_tokens, result.usage.output_tokens)
Structured extraction with citation verification:
from aimai_kit.prompts import PromptRegistry, build_request, generate_structured
from aimai_kit.prompts.grounding import verify_citations
from aimai_kit.prompts.schemas import ContractSummary
built = build_request(
PromptRegistry("prompts"), "extract_contract@v2", document, schema=ContractSummary
)
repaired = generate_structured(client, built.req, ContractSummary)
summary, grounding = verify_citations(repaired.value, document)
print(summary.amount_minor, grounding.ratio) # ungrounded fields are dropped
An agent with tools:
from aimai_kit.agent import Agent, Budgets, Thread
from aimai_kit.tools import CallContext, ToolExecutor
from aimai_kit.tools.examples.orders import build_registry, seed_database
seed_database()
executor = ToolExecutor(build_registry())
agent = Agent(client, executor, budgets=Budgets(max_steps=8, max_seconds=60))
run = agent.run(Thread(), "What is the status of order 1002?",
ctx=CallContext(user_id="u-1", tenant_id="t-1"))
print(run.stop_reason, run.answer)
Command line
# Compare models: TTFT from streaming, real usage from one complete call
uv run model-probe --prompt evals/probe/sample-prompt.txt \
--models anthropic:claude-opus-5 anthropic:claude-haiku-4-5 -n 5
# Extraction quality against a golden set (no API key needed)
uv run prompt-lab eval --prompt extract_contract@v2 --schema v2 \
--pricing config/pricing.toml --pricing-model claude-opus-5
# With a real model
uv run prompt-lab --model anthropic:claude-opus-5 eval
Why each layer looks the way it does
Short version below; the full reasoning, including what was rejected and why,
is in docs/.
Provider — one internal message format and five
error classes. Retry decisions are driven by the error class, never by
matching message text. Cached tokens are normalized to a single rule across
providers, because Anthropic reports them outside input_tokens and every
cost calculation downstream depends on which convention you picked.
Prompts and context — prompts are versioned files
identified as name@vN+fingerprint. The document goes into a user message,
never the system block, so the cache prefix stays byte-identical across
requests. Trimming is an explicit decision that produces a report line, and
the non-trimmable sections raise rather than shrink.
Tools — the schema is derived from the function
signature, so the two cannot drift apart. Five gates run before anything
executes, each producing a message the model can act on. Server context
(tenant_id) is injected from the call and is absent from the schema, so a
model cannot claim to be another tenant.
Agent loop — four budgets, one stop reason, and a final tool-free turn so a stopped run still answers. Every tool call gets a result, including refused ones. Repetition is warned about before it is stopped, because a warned model usually recovers.
Harness — the atomic unit of context is a segment, not a message, so trimming can never separate a tool call from its result. Large output spills to disk with a reference the agent can follow. Compaction converts old turns instead of dropping them, with a versioned prompt that names what must survive.
Measurements
Every number below is reproducible from this repository with no credentials. Full tables and the honest caveats are in docs/measurements.md.
| Experiment | Finding |
|---|---|
| Schema v1 vs v2 | Grounding 0% → 100%, at 0.06 more attempts and 16% more cost per document |
| Grounding attribution | v1's start_date reads 0% with grounding on and 88.9% with it off — the drop is the missing citation field, not extraction |
| Tool descriptions | Cutting descriptions to one line leaves selection accuracy unchanged but raises forbidden-tool calls from 0% to 4.5% |
| Loop detection | p95 steps 7 → 3, at the cost of completion 100% → 75% on runs that would have recovered on their own |
| Harness configurations | Naive trim 101k tokens and no answer; compaction 69.5k and no answer; compaction plus a sub-agent 12k and the answer survives |
The models behind these numbers are deterministic stubs, not providers. That
is deliberate: the point is that the measurement harness works and the
comparisons are reproducible. Run the same commands with --model anthropic:claude-opus-5 for numbers about a model.
Testing
CI runs the suite on Python 3.12 and 3.13, and a second job re-runs every measurement script and fails if a committed result changed. A table in the docs that no longer matches the code is a broken build, not a reader's problem.
uv run pytest # 343 tests
uv run pytest -m live # calls real APIs, needs keys
uv run ruff check src/ tests/
A few tests are worth calling out because of what they protect:
| Test | Guards against |
|---|---|
test_no_vendor_leak.py |
An SDK import escaping adapters/ (AST-based, with an inverse check) |
test_prefix_stable.py |
A variable field leaking into the cache prefix and silently multiplying the bill |
test_schema_regression.py |
A schema changing without anyone noticing that old eval results are now incomparable |
test_segments.py |
A trim separating a tool call from its result |
test_compaction.py |
Compaction dropping a planted fact — three needles, both prompt versions |
test_eval_harness.py |
The eval harness silently returning "correct" for everything |
Known limits
- The golden sets are synthetic.
scripts/generate_golden_set.pywrites 36 contracts, 10 of them deliberate edge cases. Replace them with your own documents for a real evaluation; theEDGE_CASESmap is the guide for what to look for. - The stubs are not models. They perform real extraction and real selection, but they know the shape of the synthetic data, so their accuracy is optimistic.
- The pricing catalog only ships Anthropic rows. OpenAI and Gemini are
commented out in
config/pricing.toml; fill them from your own billing page. A missing model produces a visible "window not in catalog" warning rather than a silent zero. - Synchronous only. Async adds no teaching value at this size.
- Isolation is three layers and only two are in this repository. The in-process path jail and the cleaned subprocess environment are here; closing the network belongs to deployment, and it is the layer that matters most.
Brand assets
assets/ holds the mark in both forms. _generate.py builds all of them, so
a logo nobody can regenerate is not what ships here:
python assets/_generate.py # the SVGs
uvx --from pillow python assets/_generate.py --png # the rasters
| File | Use |
|---|---|
header.svg / header.png |
README and docs banner |
logo.svg |
Wordmark for slides and docs |
icon.svg / icon.png |
Avatar, favicon, social card |
License
MIT.
Release files for aimai-kit 1.0.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| aimai_kit-1.0.1.tar.gz | 247.8 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| aimai_kit-1.0.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 364.8 kB
Release files / aimai_kit-1.0.1.tar.gz
| Download URL | aimai_kit-1.0.1.tar.gz |
|---|---|
| Size | 247.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
5ac553c034418b30ad93285b6ff7566e38ee1822f0833e902473bad1e52bf399
|
|
BLAKE2b-256 checksum How to use checksums |
146a39a88f83b69449c99e23bfea378ccbe66fdf83e155d9e7ea7a2d47403d48
|
| 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 Sep 7, 2026.
Transparency logRelease files / aimai_kit-1.0.1-py3-none-any.whl
| Download URL | aimai_kit-1.0.1-py3-none-any.whl |
|---|---|
| Size | 117.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
19eb3fba040421766e3d5e770028e0d87ab35919f0789c832323bf5cdc5e4ad2
|
|
BLAKE2b-256 checksum How to use checksums |
3004f97a0df0d09f1e10a049ca2a8176d027a75778b450a17fd0342ceda7d22f
|
| 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 Sep 7, 2026.
Transparency log