The Python SDK for the Witness Application.
Project description
Witness Python SDK
The Python SDK for the Witness Application.
Repository: github.com/witness-sdk/python-sdk
Witness captures inference telemetry on a side channel: events are enqueued in-process and delivered by a background thread to witness.sh. Your inference hot path is never blocked, and delivery failures never raise into your application — if ingestion is down, your models still answer.
Install
pip install witness-sdk
The import name is witness. Requires Python 3.10+. The only runtime
dependency is httpx.
Quickstart
import witness
from openai import OpenAI
witness.init(api_key="w_live_...", project="prod-chat")
witness.watch(OpenAI)
client = OpenAI(
api_key="gsk_...",
base_url="https://api.groq.com/openai/v1",
)
response = client.chat.completions.create(
model="llama-3.3-70b-versatile",
messages=[{"role": "user", "content": "Hello"}],
)
# inference → your provider, direct
# telemetry → witness.sh, async
witness.watch() patches an OpenAI-compatible client class once at startup.
Every chat.completions.create, embeddings.create, messages.create
(Anthropic-style), and streaming call on instances of that class is traced
automatically. Provider is inferred from the client base_url (Groq, OpenAI,
Together, etc.). Async clients are not supported yet — watch(AsyncOpenAI)
raises rather than recording wrong data.
Prefer explicit control? Use @witness.log() instead:
@witness.log(provider="groq")
def chat(messages):
return client.chat.completions.create(
model="llama-3.3-70b-versatile",
messages=messages,
)
witness.log() wraps any callable that performs an inference call. It times
the call, extracts model and token usage from the response by duck-typing
(any OpenAI-compatible response shape works), and enqueues an event. The
wrapped function's return value and exceptions pass through untouched —
failed calls are recorded with status: "error" and re-raised.
Provider and model are read from the response when possible; decorator
keyword arguments (provider=, model=) fill in whatever the response
cannot answer.
Opting out of telemetry
Skip reporting for specific calls:
# Block scope — works with watch() and @witness.log()
with witness.ignore():
client.chat.completions.create(model="...", messages=[...])
# Single call — works with both; stripped before your code / the provider sees it
client.chat.completions.create(model="...", messages=[...], witness_ignore=True)
To keep only a fraction of events on high-volume paths, set a sample rate:
witness.init(api_key="...", project="...", sample_rate=0.1) # keep ~10%
Configuration
witness.init() accepts:
| Parameter | Default | Notes |
|---|---|---|
api_key |
WITNESS_API_KEY env var |
Required (argument or env) |
project |
WITNESS_PROJECT env var |
Required (argument or env) |
base_url |
https://witness.sh/api/v1 |
Override for staging / self-hosted |
queue_size |
10000 |
Bounded in-memory event queue |
sample_rate |
1.0 |
Fraction of calls to record (per-call decision) |
If no API key or project is configured, init() logs a warning and the SDK
stays disabled: instrumented code runs normally and nothing is sent.
Your observability layer never crashes your app over a missing env var.
witness.flush(timeout=5.0) blocks until queued events are delivered and
keeps the worker running — useful in notebooks and short scripts.
witness.shutdown() flushes and stops; an atexit handler is registered
automatically by init().
Privacy
Witness sends metadata only: provider, model, latency, token counts, and status. Prompt and completion text are never transmitted.
Prefork servers (gunicorn, uwsgi)
Background threads do not survive fork(). If you run a prefork server,
call witness.init() in a post-fork hook so each worker gets its own
transport:
# gunicorn.conf.py
def post_fork(server, worker):
import witness
witness.init() # reads WITNESS_API_KEY / WITNESS_PROJECT
Wire contract
The SDK posts {"events": [...]} batch envelopes to
POST {base_url}/events with a Bearer API key. The full contract lives in
src/witness/schema/v1/event.json and is
versioned via the schema_version field on every event.
Roadmap
- Async
@witness.logfor coroutines - Worker-side batching (the wire format already supports it)
- Time-to-last-token on streamed responses
Development
python -m venv .venv && .venv/bin/pip install -e ".[dev]"
.venv/bin/python -m pytest tests/
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file witness_sdk-0.1.1.tar.gz.
File metadata
- Download URL: witness_sdk-0.1.1.tar.gz
- Upload date:
- Size: 56.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.11.21 {"installer":{"name":"uv","version":"0.11.21","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a9c30401397896bcf05dcbdf7891f13dd3a302f23e7d63c494df9024fd42db11
|
|
| MD5 |
c8cc39a230df2645079fef39fe920ad4
|
|
| BLAKE2b-256 |
9a7104ac1a8809f6c455897bf6a3935c5896244771ee7e4bb17bee1fdc04b118
|
File details
Details for the file witness_sdk-0.1.1-py3-none-any.whl.
File metadata
- Download URL: witness_sdk-0.1.1-py3-none-any.whl
- Upload date:
- Size: 19.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.11.21 {"installer":{"name":"uv","version":"0.11.21","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0b9dae39df45c0d50a717962316bafe4d51cb7f3c084fdb3ada8a697018d963c
|
|
| MD5 |
7fb3a90a66b491a279800efd2bf76149
|
|
| BLAKE2b-256 |
1249d52c385a66df89a144c2fd91c5921e02e6de266ada3445ea8d531bf6cb2f
|