Emet SDK for Python
Emet checks the claims an AI agent makes against the evidence behind them: the tool results and data the agent actually received. emet-sdk sends your agent's turns to Emet from the OpenTelemetry GenAI spans your framework already emits. There is no second instrumentation to maintain.
Early release. The API may change before 1.0; every change is in the changelog.
Quick start
You need an Emet account, a key for a development Application, and the External ID of the Team your users belong to.
uv add emet-sdk # or: pip install emet-sdk
import emet_sdk
from pydantic_ai import Agent
# Once, at startup, after your own tracing setup (such as logfire.configure())
emet_sdk.configure(
service_name="support-agent",
on_delivery=emet_sdk.write_delivery_file(".emet/last-delivery.json"),
)
Agent.instrument_all()
@app.post("/chat")
async def chat(body: ChatIn, user=Depends(current_user)):
# Take these from your server's own auth and storage, never from the request body
thread = await threads.get_for_user(user, body.thread_id)
with emet_sdk.turn(tenant_ref=user.team_external_id, conversation_id=thread.id, user=user.email):
result = await agent.run(body.message)
return result.output
Set EMET_APPLICATION_KEY in the app's environment. In production that is your host's secret settings; locally, load your .env the way your app already does, for example with uvicorn --env-file .env or python-dotenv. Without the key, the app logs "Emet is off" once and runs as before.
emet-cli can do this setup with your coding agent, and tells you whether a turn reached Emet whole.
How it works
- Adds, never replaces.
configure()adds one span processor to your app'sTracerProvider, and creates a provider only when the app has none. Logfire, Datadog or any other exporter keeps receiving the same spans, with the same sampling. - Opt in per turn. Only spans that start inside
turn()are sent. Other agents, HTTP calls and database queries are not. - Nothing written onto your spans. The tenant, conversation and user stay inside the SDK. They never reach your other exporters, and never travel to downstream services as baggage.
- Never in your way. Turns are built and sent on a background thread, and the SDK never raises into your app.
DESIGN.md has the details: how a turn is collected, built into Emet's envelope, split and sent.
What is sent, and what never leaves
Emet needs the content of a turn to check it: the user's question, each model call's messages, and each tool call's arguments and result. Before anything leaves the process:
- Secrets are redacted from every string: keys and tokens of common providers (OpenAI, Anthropic, AWS, GitHub, GitLab, Slack, Stripe, Google, npm), bearer tokens, JWTs, passwords in URLs, assignments such as
DB_PASSWORD=..., values under keys such aspasswordorapi_key, and your Emet key itself. Redaction is best effort; tell us about a pattern it misses. - The system prompt travels as a SHA-256 hash, never as text.
- Images and files sent inline are replaced by their type, size and digest.
- Errors carry their message, never the stack trace.
- Span attributes are limited to an allowlist of GenAI fields; framework dumps such as Pydantic AI's message history are dropped.
The user you pass to turn(), usually their email, is stored on the trace so Emet can show usage per person.
Reference
configure(*, service_name=None, on_delivery=None, endpoint="https://app.emet.so") |
Adds Emet to the global TracerProvider. Call once at startup; later calls do nothing. service_name names the app in Emet. on_delivery receives each turn's delivery result on the SDK's thread. endpoint takes only production or a localhost address, for testing against Emet on your machine. |
turn(tenant_ref, conversation_id, user=None) |
A context manager or decorator, sync or async, around one turn of your agent. tenant_ref is the External ID of the Team in Emet; conversation_id is your app's own thread id; user is who asked. |
flush(timeout=10.0) -> bool |
Waits until every finished turn is sent; returns False when the timeout ran out first. Call it before a short-lived process exits. The SDK also flushes for up to 5 seconds at exit. |
write_delivery_file(path) |
An on_delivery callback that saves the latest result to a file, for emet-cli check --last. Its folder must exist. |
EMET_APPLICATION_KEY |
The key, read from the environment when configure() runs. |
The delivery result reports each turn as sent, stopped or skipped, with the reason, the trace and conversation ids, span and batch counts, whether the turn matched a Team (unattributed) or arrived after Emet closed it (late), and any tools Emet found missing arguments or results. A turn is captured when it was sent whole, attributed, on time and with no gaps. The result never holds the key, the tenant, the user or any content.
Compatibility
| Supported | |
|---|---|
| Python | 3.10 to 3.14 |
| OpenTelemetry SDK | 1.27 or later |
| Pydantic AI | 2.63 tested in CI, on Python 3.11 or later |
Google ADK is planned. A TypeScript SDK will follow the same design.
Contributing and security
See CONTRIBUTING.md for development and releases, and SECURITY.md to report a vulnerability privately. Everyone taking part follows the Code of Conduct.
License
Metadata
Release files for emet-sdk 0.1.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 | Provenance | Actions |
|---|---|---|---|---|
| emet_sdk-0.1.1.tar.gz | 152.0 kB | With provenance | Details for emet_sdk-0.1.1.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| emet_sdk-0.1.1-py3-none-any.whl | Python 3 | none | any | Details for emet_sdk-0.1.1-py3-none-any.whl |
Total release size: 181.5 kB
Release files / emet_sdk-0.1.1.tar.gz
| Download URL | emet_sdk-0.1.1.tar.gz |
|---|---|
| Size | 152.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
a9ef043729489da97f841151099fdcd7a0d152f6d4cb32a2708a1f301956f5b8
|
|
BLAKE2b-256 checksum How to use checksums |
08e844b43a0367b3ca7b465c724a9b394394b016c097b129d598390ee4743003
|
| 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 Oct 30, 2026.
Transparency logRelease files / emet_sdk-0.1.1-py3-none-any.whl
| Download URL | emet_sdk-0.1.1-py3-none-any.whl |
|---|---|
| Size | 29.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
39acc2da0e48d48b19270767f9626f0a635412d056a414c50e24813edb68cdb6
|
|
BLAKE2b-256 checksum How to use checksums |
52b91930ec757bb9adbc460335e95ae9bd0d78b83b158616cf70347306680ece
|
| 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 Oct 30, 2026.
Transparency logSecurity for emet-sdk 0.1.1
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
All 2 files in this release have been uploaded with provenance metadata.
Attestation for emet_sdk-0.1.1.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 Oct 30, 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.