Capture LLM call/response snapshots and validate them against the ModelGenius snapshot format — locally, with no network calls.
Project description
modelgenius-sdk
Capture your production LLM calls as local snapshot files, and validate them against the ModelGenius snapshot format — entirely on your machine, with zero network calls and zero telemetry.
ModelGenius answers "is it safe to switch
LLM models, and is it worth it?" by replaying your real production requests
against candidate models and comparing quality, cost, and latency. The input
to that analysis is a set of snapshots: your requests, the parameters they
ran with, and (ideally) the responses your current model actually produced.
This SDK is the capture side. It has one runtime dependency (jsonschema)
and never imports openai, anthropic, or litellm — it duck-types your
existing client objects.
Three ways to adopt it:
- Wrap your client — one line at startup; every call is spooled to local JSONL as it happens, with real outputs, token usage, and latency attached.
- Bring your own logs — if you already log LLM traffic, write it in the
snapshot format and use
modelgenius-sdk validateto check it before uploading. - Bring your OpenTelemetry traces — if your app is already instrumented
with the OTel GenAI semantic conventions
(OpenLLMetry, OpenLIT, vendor SDKs, …), convert an OTLP/JSON export with
modelgenius-sdk convert otlp— no app changes needed. Requires message content capture to be enabled in your instrumentation (it is off by default in OTel; metadata-only spans convert to an actionable error). Legacy flattened OpenLLMetry attributes (gen_ai.prompt.N.*) are also understood. Two gaps are inherent to the conventions and flagged rather than guessed:response_formatJSON schemas and (often) tool definitions are not carried in traces — converted records noteenrichment_neededin metadata and the validator reminds you to supply them at upload.
Install
pip install modelgenius-sdk
Quickstart
OpenAI (sync or async client; streaming supported):
import modelgenius_sdk
from openai import OpenAI
client = modelgenius_sdk.wrap_openai(OpenAI(), workflow_name="support_ticket_triage")
# use `client` exactly as before — every chat.completions call is snapshotted locally
Anthropic (sync or async client; messages.create streaming supported):
import modelgenius_sdk
from anthropic import Anthropic
client = modelgenius_sdk.wrap_anthropic(Anthropic(), workflow_name="math_tutoring")
LiteLLM (success callback):
import litellm
import modelgenius_sdk
litellm.success_callback.append(modelgenius_sdk.make_litellm_callback(workflow_name="ap_automation"))
Validate what you captured (or logs you wrote yourself):
modelgenius-sdk validate ./modelgenius_snapshots
The wrapper returns the same client object and never changes call behavior: your arguments pass through untouched, return values and exceptions propagate unchanged, and any internal capture error is swallowed (logged once), never raised into your app.
What gets captured
One JSON object per call, appended to a local JSONL spool file: the request
(messages, model, temperature, tools, response_format, and the rest of the
request parameters), plus a captured_output block with the real response —
output text, tool calls, token usage, latency, and, via the LiteLLM callback,
cost. See docs/snapshot_format.md for the full
format, and examples/ for validated sample files.
The more you capture, the more the analysis can do:
| You provide | You unlock |
|---|---|
captured_output |
Baseline scored from real production outputs — no baseline replay, real latency/cost preserved |
usage + cost_usd |
Honest savings baseline from actual spend |
workflow_name + call_name (or workflow_id) |
Precise workflow grouping instead of content-based inference |
tools / response_format |
Deterministic output-contract checks on every candidate |
model |
Automatic baseline identification |
Full table with grouping precedence: docs/snapshot_format.md.
Configuration
Everything can be set in code via CaptureConfig, or via environment
variables:
from modelgenius_sdk import CaptureConfig, wrap_openai
config = CaptureConfig(
spool_dir="/var/log/myapp/snapshots",
workflow_name="support_ticket_triage",
call_name="classify_ticket",
sample_rate=0.25, # capture 25% of calls
)
client = wrap_openai(OpenAI(), config)
| Environment variable | Effect | Default |
|---|---|---|
MODELGENIUS_CAPTURE |
Set to 0, false, or no (case-insensitive) to disable capture entirely — the kill switch |
enabled |
MODELGENIUS_CAPTURE_DIR |
Spool directory for snapshot files | ./modelgenius_snapshots |
MODELGENIUS_CAPTURE_SAMPLE_RATE |
Fraction of calls to capture, 0.0–1.0 |
1.0 |
MODELGENIUS_CAPTURE_DEBUG |
Set to 1 to make internal capture errors raise instead of being swallowed (for tests/debugging) |
off |
Explicit CaptureConfig values win over environment variables. The sampling
decision is made once per call, before dispatch, so a streamed response is
either fully captured or not at all.
Redaction and filtering
You stay in control of what lands on disk:
def redact(record: dict):
# Called with the full record just before it is written.
for message in record.get("messages", []):
if isinstance(message.get("content"), str):
message["content"] = scrub_pii(message["content"])
if record.get("metadata", {}).get("tenant_tier") == "enterprise":
return None # returning None drops the record entirely
return record
def only_prod_traffic(request_kwargs: dict) -> bool:
# Called with the request kwargs before capture is attempted.
return request_kwargs.get("user") != "internal-smoke-test"
config = CaptureConfig(
workflow_name="support_ticket_triage",
redact=redact,
request_filter=only_prod_traffic,
sample_rate=0.1,
)
Spool files and rotation
Records are appended to
<spool_dir>/snapshots-<YYYYMMDD>-<pid>-<seq>.jsonl — one JSON object per
line, flushed after every write, safe across processes (each PID writes its
own file). When the current file exceeds max_file_bytes (default 64 MiB)
the writer rotates to the next sequence number. The spool directory is
created lazily on first write. Point modelgenius-sdk validate (or the
ModelGenius upload step) at the directory when you are ready.
Trust posture
- Local-only. The library makes no network calls of its own — the only network traffic is your unmodified provider call. Uploading snapshots to ModelGenius is a separate, deliberate step you take elsewhere.
- No telemetry. Nothing is phoned home. Ever.
- Fail-open. Capture errors never raise into your application and never alter the provider call, its return value, or its exceptions.
- You control the data. Redaction hook, request filter, sampling, and a
one-variable kill switch (
MODELGENIUS_CAPTURE=0). - Small and auditable. A handful of modules, one dependency, no provider SDK imports — read the whole thing in one sitting.
CLI
modelgenius-sdk validate PATH [--json] [--strict] [--quiet]
modelgenius-sdk convert otlp PATH [-o OUTPUT.jsonl] [--json] [--quiet]
modelgenius-sdk version
validate accepts a .jsonl file, a .json file (object or array), or a
directory of both. Exit code 0 means every record is accepted by the same
semantics as the ModelGenius upload path; warnings point out optional fields
that would unlock capability. --strict turns warnings into failures (useful
in CI — this repo validates its own examples/ on every push).
convert otlp converts an OTLP/JSON trace export (a .json payload or a
collector file-exporter .jsonl) into snapshot JSONL and validates the
result in one step. Exit code 0 means every GenAI span converted and
validated; per-span problems (e.g. content capture disabled in your
instrumentation) are reported individually and exit 1.
Limitations (v0)
- Failed provider calls are not captured (only successful responses).
- OpenAI:
chat.completionsonly; theresponsesAPI surface is not wrapped yet. - Anthropic:
messages.create(includingstream=True) is captured; themessages.stream()helper is not yet. - LiteLLM: the callback is sync-only (litellm accepts sync callbacks for async paths).
Roadmap
- Direct upload client (
modelgenius-sdk push) as an explicit, opt-in step - Continuous monitoring mode (rolling capture windows + scheduled re-evaluation)
- TypeScript SDK
- OpenAI
responsessurface and Anthropicmessages.stream()capture
License
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 modelgenius_sdk-0.2.0.tar.gz.
File metadata
- Download URL: modelgenius_sdk-0.2.0.tar.gz
- Upload date:
- Size: 97.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c44e02007c2052e6312b6b4840e56359667ba689b546502c77b5bb92ad6fe18f
|
|
| MD5 |
77568f229bec269c683ccad4133141ea
|
|
| BLAKE2b-256 |
1c214f33a26f354de0f49fc53e12ace9078946bc2cdbab1b3219878c8d535e36
|
Provenance
The following attestation bundles were made for modelgenius_sdk-0.2.0.tar.gz:
Publisher:
release.yml on modelgenius/modelgenius-sdk
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
modelgenius_sdk-0.2.0.tar.gz -
Subject digest:
c44e02007c2052e6312b6b4840e56359667ba689b546502c77b5bb92ad6fe18f - Sigstore transparency entry: 2124520052
- Sigstore integration time:
-
Permalink:
modelgenius/modelgenius-sdk@04c7ba2687bae5ac06ac963969e8b1b63f7c5e37 -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/modelgenius
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@04c7ba2687bae5ac06ac963969e8b1b63f7c5e37 -
Trigger Event:
push
-
Statement type:
File details
Details for the file modelgenius_sdk-0.2.0-py3-none-any.whl.
File metadata
- Download URL: modelgenius_sdk-0.2.0-py3-none-any.whl
- Upload date:
- Size: 60.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d152858cb439f9f618a8fbea48fb2c861dadff6ef36d4eddfc0b63e22ddcdf30
|
|
| MD5 |
5854072c631df5cc598d50b2a153fa17
|
|
| BLAKE2b-256 |
c3645b88e7e032ca1e5cd536f2286544164585eac99adee507225b8f9439595a
|
Provenance
The following attestation bundles were made for modelgenius_sdk-0.2.0-py3-none-any.whl:
Publisher:
release.yml on modelgenius/modelgenius-sdk
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
modelgenius_sdk-0.2.0-py3-none-any.whl -
Subject digest:
d152858cb439f9f618a8fbea48fb2c861dadff6ef36d4eddfc0b63e22ddcdf30 - Sigstore transparency entry: 2124520097
- Sigstore integration time:
-
Permalink:
modelgenius/modelgenius-sdk@04c7ba2687bae5ac06ac963969e8b1b63f7c5e37 -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/modelgenius
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@04c7ba2687bae5ac06ac963969e8b1b63f7c5e37 -
Trigger Event:
push
-
Statement type: