anvil-sdk (Python)
Trace instrumentation for LLM apps. Spec: plan/sdk.md.
pip install anvil-sdk # + [langchain] for the callback handler
import anvil_sdk as anvil
anvil.init(api_key="ak_...", base_url="http://localhost:8000")
with anvil.trace("answer_question", input=question) as t:
with anvil.span("retrieve", type="retrieval") as s:
s.output = search(question)
with anvil.span("generate", type="llm") as s:
s.end(output=answer, tokens_in=120, tokens_out=30)
t.output = answer
LangChain apps need one line instead:
from anvil_sdk.langchain import AnvilCallbackHandler
chain.invoke(question, config={"callbacks": [AnvilCallbackHandler()]})
The guarantee
This SDK never blocks or crashes your app (ADR-0013).
Traces go to a bounded in-memory queue and a background thread ships them. If
Anvil is slow, down, or misconfigured: the queue sheds its oldest entries,
counts them in anvil.stats(), and your request path never notices. Nothing in
this package raises into your code except exceptions your own functions raised.
API
init(api_key, base_url, flush_interval=2.0, max_queue=1000, redact=None) |
Configure the global client |
trace(name, input=…, metadata=…, session_id=…, user_ref=…) |
Context manager; root of a unit of work |
span(name, type=…, input=…) |
Context manager; nests via contextvars |
@observe(type="llm") |
Decorator sugar — args in, return value out, exceptions marked |
start_trace(...) / start_span(...) |
Manual API for middleware and callbacks; call .end(output=…) |
feedback(trace_id, rating, comment=None, user_ref=None) |
End-user thumbs up/down (-1 / +1) |
flush() / shutdown() / stats() |
Lifecycle and counters |
trace.id is a client-generated UUID exposed on the object — hand it to your
frontend so a thumbs-down can be attached to that exact trace later.
Span types: llm, retrieval, tool, embedding, chain, custom.
feedback — closing the loop
Round-trip trace.id to your frontend, then send the vote back with it:
@app.post("/ask")
def ask(body: AskRequest):
handler = AnvilCallbackHandler(trace_name="ask")
answer = chain.invoke(body.question, config={"callbacks": [handler]})
return {"answer": answer, "trace_id": handler.trace.id}
@app.post("/feedback")
def feedback(body: FeedbackRequest):
anvil.feedback(body.trace_id, -1, comment=body.comment, user_ref=body.user_id)
return {"ok": True}
Two things worth knowing:
- Call it as soon as the user clicks. The vote routinely beats its own trace to Anvil's database; the server holds it and waits for the trace rather than rejecting the race. You never need to sleep first.
- Pass
user_refif you have one. With it, repeat votes upsert — one user toggling 👎 → 👍 replaces their row instead of leaving two contradictory ones. Without it, every call is an independent anonymous vote.
Fire-and-forget like everything else here: feedback() never blocks or raises,
and its own counters (feedback_queued/feedback_sent/feedback_dropped) are
separate from the trace ones so a busy trace queue can't hide a lost vote.
Downvoted traces show up in the dashboard's failure inbox — thumbs-down traces not yet in any dataset — where one click turns a real failure into a test case.
redact — scrub PII before it leaves your process
anvil.init(api_key=..., redact=scrub)
def scrub(obj): # called for every trace *and* every span
obj.input = mask(obj.input)
return obj # or None to drop the object entirely
Payloads are usually dicts and lists, not flat strings — walk them recursively.
examples/demo-rag-app/demo/app.py has a worked example. A hook that raises
causes the object to be dropped rather than shipped un-scrubbed.
Fields over 100 KB are truncated to a preview before sending, mirroring the server-side policy so large payloads degrade instead of 413ing.
Tests
pip install -e .[dev] && python -m pytest -q
Keyless: LLM calls use LangChain's fake models and the transport is mocked with
respx. Emitted payloads are validated against packages/shared/trace-schema.json,
the same schema the ingest endpoint enforces.
Metadata
Release files for anvil-sdk 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 | Provenance | Actions |
|---|---|---|---|---|
| anvil_sdk-0.1.0.tar.gz | 34.4 kB | With provenance | Details for anvil_sdk-0.1.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| anvil_sdk-0.1.0-py3-none-any.whl | Python 3 | none | any | Details for anvil_sdk-0.1.0-py3-none-any.whl |
Total release size: 58.3 kB
Release files / anvil_sdk-0.1.0.tar.gz
| Download URL | anvil_sdk-0.1.0.tar.gz |
|---|---|
| Size | 34.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
1b0090548941a5cb8196e9646d3767a6ec7cdaa683eece3cea8ecc0353c19af9
|
|
BLAKE2b-256 checksum How to use checksums |
f2cce142ab037f74e53dedeb6bb5de3f9905d1d2ab26dfae1e9f6a07bf9f33e5
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
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 Nov 8, 2026.
Transparency logRelease files / anvil_sdk-0.1.0-py3-none-any.whl
| Download URL | anvil_sdk-0.1.0-py3-none-any.whl |
|---|---|
| Size | 23.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
a7b91ef1ac55981feabe780dab3ced03412efb4ce28fb3eae7fd427e45414482
|
|
BLAKE2b-256 checksum How to use checksums |
afa9974ce1e936267fe2c9ae60ee91551d88bb46d5dfb5cdc50631c4c6e8f538
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
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 Nov 8, 2026.
Transparency logSecurity for anvil-sdk 0.1.0
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.
Full provenance
Both files in this release have been uploaded with provenance metadata.
Attestation for anvil_sdk-0.1.0.tar.gz
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 Nov 8, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.