MVK SDK for Python - Ultralight OpenTelemetry-compatible SDK for AI Observability
Project description
MVK SDK
Pure Python • Serverless Ready • Zero Breaking Guarantee • OTEL-style Smart Auto-instrumentation • W3C TraceContext
The MVK SDK is a production-grade telemetry SDK with serverless auto-detection, simplified MVK-only configuration, and a non-breaking guarantee. Deploy with confidence using DIRECT mode (straight to MVK) or COLLECTOR mode (via OTEL Collector). The SDK uses OTEL-style smart auto-instrumentation with wrapt for immediate and lazy loading of AI providers and vector databases with zero risk to your application.
[!IMPORTANT] Zero Breaking Guarantee. Instrumentation never raises into your code. Any SDK-internal error is caught, logged, and swallowed — your application's execution flow is never interrupted.
Table of Contents
- Table of Contents
- 1. Installation
- 2. Configuration & Instrumentation
- 3.
@mvk.context()— enriching spans (decorator vs. context manager) - 4. Cost signals —
@mvk.signal(),create_signal()& metered usage - 5. Reference
- 6. Support
1. Installation
pip install mvk-sdk-py
Install providers and protocols as optional extras:
[!TIP] Quote the package name when installing extras. Shells like zsh treat unquoted square brackets as glob patterns and the install will fail.
# AI providers
pip install "mvk-sdk-py[genai]" # All AI providers
pip install "mvk-sdk-py[openai]" # OpenAI only
pip install "mvk-sdk-py[anthropic]" # Anthropic only
# Vector databases
pip install "mvk-sdk-py[vectordb]" # All vector DBs
pip install "mvk-sdk-py[pinecone]" # Pinecone only
# Export protocols
pip install "mvk-sdk-py[grpc]" # gRPC exporter
pip install "mvk-sdk-py[compression]" # Compression support
# Everything
pip install "mvk-sdk-py[all]"
2. Configuration & Instrumentation
mvk.instrument() is the single entry point — call it once per process,
before your provider calls. All settings are passed as top-level keyword
arguments (no config={...} wrapper); grouped settings (exporter,
batching, failed_batch_disk, logging, serverless, wrappers) are
nested dicts. Every parameter also has a MVK_* environment-variable
equivalent.
Minimal setup:
import mvk_sdk as mvk
mvk.instrument(
agent_id="agent-123", # Required (or MVK_AGENT_ID)
api_key="mvk_...", # Required for DIRECT mode (or MVK_API_KEY)
tenant_id="tenant-123", # Required for DIRECT mode (or MVK_TENANT_ID)
)
# That's it — all provider calls are now traced
import openai
response = openai.ChatCompletion.create(...) # Automatically traced
DIRECT mode (default) sends traces straight to Mavvrik; the endpoint is auto-set if omitted. COLLECTOR mode sends to your own OTEL Collector:
# DIRECT — to the Mavvrik backend
mvk.instrument(
agent_id="agent-123", api_key="mvk_...", tenant_id="tenant-123",
exporter={"mode": "DIRECT", "type": "otlp_http"}, # endpoint auto-set
)
# COLLECTOR — to your OTEL Collector
mvk.instrument(
agent_id="agent-123",
exporter={"mode": "COLLECTOR", "type": "otlp_grpc", "endpoint": "localhost:4317"},
)
2.1 Configuration Reference — all values
Every setting below can be supplied as an instrument() parameter or the
matching environment variable. Nested instrument() keys are shown as
group.key (e.g. exporter.timeout → exporter={"timeout": 30}). 🔒 marks
sensitive values — see §2.3.
Core / identity
instrument() |
Environment variable | Default | Notes |
|---|---|---|---|
agent_id |
MVK_AGENT_ID |
— | Required. Agent identifier |
api_key 🔒 |
MVK_API_KEY |
— | Required in DIRECT mode |
tenant_id |
MVK_TENANT_ID |
— | Required in DIRECT mode; sent as X-Tenant-ID |
enabled |
MVK_ENABLED |
true |
Master on/off switch |
Exporter / transport (exporter={...})
instrument() |
Environment variable | Default | Notes |
|---|---|---|---|
exporter.mode |
MVK_MODE |
DIRECT |
DIRECT or COLLECTOR |
exporter.type |
MVK_EXPORTER_TYPE |
otlp_http |
otlp_http, otlp_grpc, console, file |
exporter.endpoint |
MVK_ENDPOINT |
auto | URL or host:port (auto-set in DIRECT) |
exporter.headers |
MVK_HEADERS |
— | JSON string or k1=v1,k2=v2 |
exporter.insecure |
MVK_EXPORTER_INSECURE |
false |
HTTP instead of HTTPS (local only) |
exporter.compression |
MVK_EXPORTER_COMPRESSION |
gzip |
gzip or none |
exporter.timeout |
MVK_EXPORTER_TIMEOUT |
10 |
Seconds (1–300) |
exporter.max_retries |
MVK_EXPORTER_MAX_RETRIES |
6 |
0–20 |
exporter.retry_timeout |
MVK_EXPORTER_RETRY_TIMEOUT |
60 |
Total retry seconds (1–600) |
exporter.file_path |
MVK_EXPORTER_FILE_PATH |
— | Directory for file exporter |
exporter.format |
MVK_EXPORTER_FORMAT |
simple |
simple or json (console/file) |
Batching (batching={...})
instrument() |
Environment variable | Default | Notes |
|---|---|---|---|
batching.max_items |
MVK_BATCH_MAX_ITEMS |
2000 |
Spans/batch (1–10000) |
batching.max_bytes |
MVK_BATCH_MAX_BYTES |
2097152 |
2 MiB (1 KB–10 MB) |
batching.max_interval_ms |
MVK_BATCH_MAX_INTERVAL_MS |
3000 |
ms (100–60000) |
Failed-batch disk recovery (failed_batch_disk={...})
instrument() |
Environment variable | Default | Notes |
|---|---|---|---|
failed_batch_disk.enabled |
MVK_FAILED_BATCH_DISK_ENABLED |
false |
|
failed_batch_disk.path |
MVK_FAILED_BATCH_DISK_PATH |
— | Required when enabled |
failed_batch_disk.max_size_mb |
MVK_FAILED_BATCH_DISK_MAX_SIZE_MB |
1000 |
10–100000 |
failed_batch_disk.retry_interval |
MVK_FAILED_BATCH_DISK_RETRY_INTERVAL |
60 |
Seconds (1–3600) |
Logging (logging={...}) — see §2.4
instrument() |
Environment variable | Default | Notes |
|---|---|---|---|
logging.level |
MVK_LOG_LEVEL |
OFF |
OFF, INFO, DEBUG |
logging.wrapper_level |
MVK_WRAPPER_LOG_LEVEL |
— | Provider wrappers only |
logging.prompts_responses 🔒 |
MVK_LOG_PROMPTS_RESPONSES |
false |
Logs raw prompts/responses |
logging.prompts_storage_mode |
MVK_PROMPTS_STORAGE_MODE |
truncate |
truncate, compress, envelope |
logging.prompts_max_length |
MVK_PROMPTS_MAX_LENGTH |
1000 |
Truncation length (100–100000) |
logging.prompts_masking |
MVK_PROMPTS_MASKING |
true |
Mask PII/PHI/PCI (on by default) |
Validation, tags & wrappers
instrument() |
Environment variable | Default | Notes |
|---|---|---|---|
strict_validation |
MVK_STRICT_VALIDATION |
false |
Raise on schema violations |
tag_limit |
MVK_TAG_LIMIT |
10 |
Max tags/span (1–10) |
tags={"env": "prod"} |
MVK_TAG_ENV=prod |
— | One env var per tag: MVK_TAG_<KEY> |
wrappers={"include": [...]} |
MVK_WRAPPERS |
genai,vectordb |
Comma-separated in env |
wrappers.http.exclusions |
MVK_HTTP_EXCLUSIONS |
— | JSON array or comma-separated |
Serverless & diagnostics
instrument() |
Environment variable | Default | Notes |
|---|---|---|---|
serverless.force |
MVK_SERVERLESS |
false |
Force serverless optimizations |
system_alerts_enabled |
MVK_SYSTEM_ALERTS_ENABLED |
true |
SDK self-diagnostics telemetry |
collect_cloud_metadata |
MVK_COLLECT_CLOUD_METADATA |
true |
Auto-skipped in local/CI |
auto_middleware |
MVK_AUTO_MIDDLEWARE |
true |
Auto W3C context for Flask/FastAPI/Django |
Full instrument() call with every group
mvk.instrument(
agent_id="agent-123",
api_key="mvk_...", # Required in DIRECT mode
tenant_id="tenant-123", # Required in DIRECT mode
exporter={
"mode": "DIRECT", # DIRECT or COLLECTOR
"type": "otlp_http", # otlp_http | otlp_grpc | console | file
"endpoint": "https://ingest.mavvrik.ai/v1/traces", # auto-set if omitted
"timeout": 10,
"max_retries": 6,
"compression": "gzip", # gzip | none
},
batching={"max_items": 2000, "max_bytes": 2097152, "max_interval_ms": 3000},
failed_batch_disk={
"enabled": False,
"path": "/tmp/mvk/failed_batches", # required when enabled
"max_size_mb": 1000,
"retry_interval": 60,
},
logging={"level": "INFO", "prompts_responses": False}, # ⚠️ see §2.4
serverless={"force": False},
strict_validation=False,
wrappers={"include": ["genai", "vectordb"]},
tags={"env": "prod"},
)
2.2 Precedence — defaults < parameters < environment
When the same setting is supplied in more than one place, the SDK resolves it in this order (lowest → highest priority):
Schema default < instrument() parameter < MVK_* environment variable
Environment variables always win. This is deliberate: it lets ops/platform teams override application code at deploy time without a code change. Example:
mvk.instrument(agent_id="agent-123", logging={"level": "INFO"})
export MVK_LOG_LEVEL=DEBUG # effective level is DEBUG, not INFO
Tags merge the same way — MVK_TAG_<KEY> overrides the same key passed in
tags={...}, while other keys are preserved.
2.3 Secrets & environment (recommended)
The only secret value is api_key (MVK_API_KEY); tenant_id is
sensitive-but-identifying. Do not hard-code these in source. Because
environment variables take precedence (§2.2),
the recommended production pattern is:
- Keep
agent_idand non-secret config in code or env as convenient. - Inject
MVK_API_KEY(andMVK_TENANT_ID) from a secrets manager at runtime — AWS Secrets Manager / SSM, GCP Secret Manager, Azure Key Vault, or Vault — never from a committed.envor the image.
import mvk_sdk as mvk
# api_key / tenant_id resolved from MVK_API_KEY / MVK_TENANT_ID,
# which your platform injects from the secret manager.
mvk.instrument(agent_id="agent-123")
# Example: hydrate env from a secret manager before the process starts
export MVK_API_KEY="$(aws secretsmanager get-secret-value \
--secret-id mvk/api-key --query SecretString --output text)"
export MVK_TENANT_ID=tenant-123
[!WARNING] Avoid passing
api_key=as a literal in code or logging it. Keeplogging.prompts_responsesdisabled in production — it logs raw LLM prompts/responses, which may contain regulated data.
2.4 Logging
Logging is off by default (level: "OFF") for security and performance.
When enabled, the SDK auto-configures Python logging — no manual setup needed.
mvk.instrument(agent_id="agent-123", api_key="mvk_...",
logging={"level": "DEBUG"}) # controls all components
wrapper_level overrides the level for the AI provider wrappers only:
mvk.instrument(agent_id="agent-123", api_key="mvk_...",
logging={"level": "INFO", "wrapper_level": "DEBUG"})
export MVK_LOG_LEVEL=INFO
export MVK_WRAPPER_LOG_LEVEL=DEBUG
Levels: OFF (default) · INFO (lifecycle — config, batch flushes,
shutdown) · DEBUG (per-span detail — instrumentation, token extraction,
queueing; enabling DEBUG also enables INFO).
Log lines are formatted as TIMESTAMP - LOGGER - LEVEL - MESSAGE, where the
timestamp is YYYY-MM-DD HH:MM:SS,mmm and loggers are namespaced mvk.* (e.g.
mvk.instrumentation.openai, mvk.processors.writer). Example DEBUG output
for a single auto-traced OpenAI chat completion:
2026-06-29 18:37:28,807 - mvk.instrumentation.wrapper_base - DEBUG - Wrapper invoked for span_name=openai.chat.completion, method=create, instance=Completions
2026-06-29 18:37:29,044 - mvk.instrumentation.wrapper_base - DEBUG - Sync wrapper: Created span openai.chat.completion with trace_id=6eeb9c79..., operation_subtype=sync
2026-06-29 18:37:29,061 - mvk.instrumentation.openai - DEBUG - [OpenAI Token Extraction] Extracted tokens - prompt: 12, completion: 8, total: 20
2026-06-29 18:37:29,061 - mvk.processors.writer - DEBUG - Span queued for export: openai.chat.completion (queue size: 1)
2026-06-29 18:37:29,062 - mvk.processors.writer - INFO - Flushing batch of 1 span(s) to exporter
3. @mvk.context() — enriching spans (decorator vs. context manager)
mvk.instrument() only knows process-wide defaults (agent, environment,
global tags). It cannot know who the current user is, which session a
request belongs to, or what business workflow is executing. mvk.context()
layers that runtime identity and business context onto every span produced
inside its scope — auto-traced LLM calls, vector DB queries, and manual
@mvk.signal / create_signal() operations all inherit it automatically.
Dual purpose — when to use which
mvk.context() has the same signature in both forms; pick by scope:
| Form | Use when |
|---|---|
Context manager — with mvk.context(...): |
Scoping a block at runtime: an HTTP handler, message consumer, or job; per-request identity that varies per call; or wrapping an async generator (the decorator does not support async generators). |
Decorator — @mvk.context(...) |
A whole function should share one (usually static) context. Works on sync functions and async def coroutines (context is preserved across await within the same task). |
Why it matters
- Cost attribution —
customer_id,application_id, andtagslet the Mavvrik backend split AI spend by tenant, product, team, or feature for chargeback / showback reporting. - Trace correlation —
user_id,session_id, andrequest_idstitch multi-step agent workflows back to a single end-user request. - Business grouping —
use_casegroups related operations under a named business process (e.g."fraud_detection_v2","customer_onboarding"). - Distributed tracing —
traceparent/tracestateaccept incoming W3C Trace Context headers so spans link across service boundaries. - Nearest-wins inheritance — context layered closer to the call overrides outer layers (see Context inheritance).
Signature
mvk.context(
name: str | None = None, # Logical context name
user_id: str | None = None, # End-user identity
session_id: str | None = None, # Session / conversation ID
application_id: str | None = None, # Calling application
customer_id: str | None = None, # Your customer (multi-tenant)
request_id: str | None = None, # External request correlation ID
region: str | None = None, # Geographic region
cloud_provider_code: str | None = None, # e.g. "aws", "gcp", "azure"
use_case: str | None = None, # Business workflow (snake_case)
tags: dict[str, str] | None = None, # Up to 10 custom key/value tags
traceparent: str | None = None, # W3C traceparent header
tracestate: str | None = None, # W3C tracestate header
)
All parameters are optional — pass only what is meaningful at the call site. Unknown keyword arguments are logged and ignored; the SDK never raises into client code.
Examples
Context manager — per-request scope (HTTP handler, consumer, job):
import mvk_sdk as mvk
import openai
mvk.instrument(agent_id="agent-123", api_key="mvk_...", tenant_id="tenant-123")
def handle_chat(request):
with mvk.context(
user_id=request.user.id,
session_id=request.session.id,
customer_id=request.tenant.id,
use_case="customer_support_chat",
tags={"feature": "chat", "tier": "premium"},
):
# Every span below inherits the identity, use_case, and tags above
return openai.ChatCompletion.create(...)
Decorator — function-scoped enrichment:
@mvk.context(region="us-east-1", use_case="document_search",
tags={"service": "search-api"})
def search_handler(query: str):
return run_pipeline(query) # all LLM / vector DB calls inherit the context
Continuing a distributed trace from an upstream service:
def grpc_handler(request, metadata):
with mvk.context(
traceparent=metadata.get("traceparent"),
tracestate=metadata.get("tracestate"),
request_id=metadata.get("x-request-id"),
):
return process(request)
Nesting — innermost wins for scalars, tags merge:
with mvk.context(customer_id="acme", tags={"env": "prod"}):
with mvk.context(user_id="u-42", tags={"feature": "summarize"}):
# Span sees: customer_id="acme", user_id="u-42",
# tags={"env": "prod", "feature": "summarize"}
summarize(doc)
Clearing an inherited attribute — pass an empty string:
with mvk.context(user_id="u-42"):
with mvk.context(user_id=""):
run_background_job() # user_id removed from spans in this inner block
Notes & limits
- Tag cap: maximum 10 tags per span after all levels merge. Excess tags
are dropped (or raise if
strict_validation=True). See Tag validation for key/value rules. - Per-request headers override everything: incoming
x-mvk-*headers on a single call take precedence over decorator and context-manager values for that call only. - Unknown kwargs are non-fatal: a misspelled parameter (e.g.
userid=...) is logged and ignored. The SDK never raises into client code. - All attributes are prefixed
mvk.*on the wire: e.g.user_idbecomesmvk.user_id, andtags={"team": "ml"}becomesmvk.tags.team.
4. Cost signals — @mvk.signal(), create_signal() & metered usage
Auto-instrumentation covers LLM providers, vector DBs, and supported frameworks out of the box. For everything else — wrapping a business workflow under a single named span, tracing an in-house LLM gateway, attributing custom tool / storage / API costs — the SDK exposes three manual APIs:
| API | Purpose | Use as |
|---|---|---|
@mvk.signal() |
Wrap a function so its entire execution becomes one parent span; child auto-traced calls nest under it | Decorator |
mvk.create_signal() |
Create a span for a code block with explicit step_type / operation |
Context manager |
mvk.add_metered_usage() |
Attach billable quantity metrics (pages, API calls, bytes, custom tokens) to the current span | Plain function call |
Identity, session, customer, and tags propagate to these spans automatically
from mvk.context().
@mvk.signal() — decorate a function as a named span
The function name (or name=...) becomes the span name; every auto-traced call
inside (LLM, vector DB, HTTP) and every create_signal() block becomes a child
span. A run_id is auto-generated at the root signal and propagated to all
descendants (mvk.run_id on the wire) for trace correlation.
[!NOTE]
step_type,operation,operation_subtype, andmodelare auto-set by wrappers for child spans.@mvk.signal()also acceptsstep_typeandtool_namewhen the decorated function itself represents a tool wrapper, buttool_nameis only applied whenstep_type="TOOL". For explicit manual span classification (operation,operation_subtype), usemvk.create_signal().
@mvk.signal(
name: str | None = None, # Span name (default: function name)
user_id: str | None = None,
session_id: str | None = None,
application_id: str | None = None,
customer_id: str | None = None,
request_id: str | None = None,
region: str | None = None,
cloud_provider_code: str | None = None,
use_case: str | None = None,
step_type: str | MVKStepType | None = None, # Optional; use with TOOL spans
tool_name: str | None = None, # TOOL-only label for the wrapped function
tags: dict[str, str] | None = None, # Custom tags (max 10 per span)
)
Works on sync functions, async def coroutines, and async generators — the SDK
auto-detects the function type.
# Group a multi-step workflow under one named span
@mvk.signal(name="answer_question", use_case="customer_support_chat",
tags={"tier": "premium"})
def answer_question(query: str):
embedding = openai.Embedding.create(...) # child span: EMBEDDING
hits = pinecone.query(...) # child span: RETRIEVER
return openai.ChatCompletion.create(...) # child span: LLM
# Custom tool wrapper — attach mvk.tool_name to the decorator span
@mvk.signal(name="search_documents", step_type="TOOL",
tool_name="knowledge_base_search")
def search_documents(query: str):
return internal_search(query)
mvk.create_signal() — manual spans with explicit step_type
Use this when auto-instrumentation does not cover the call — a custom
in-house LLM service, a paid third-party API, a parsing step, a file upload.
Unlike @mvk.signal(), this is a context manager and lets you set
step_type / operation explicitly so the backend classifies the cost
correctly.
from mvk_sdk.schema import MVKStepType # optional; strings also accepted
mvk.create_signal(
name: str, # Span name (required)
step_type: MVKStepType | str | None = None, # LLM | TOOL | RETRIEVER |
# EMBEDDING | BATCH | AGENT_CALL
operation: str | None = None, # e.g. "parse", "api_call"
operation_subtype: str | None = None, # Free-form refinement
tool_name: str | None = None, # TOOL-only per-tool attribution
tags: dict[str, str] | None = None,
)
Inherits user_id, session_id, customer_id, region, use_case, and tags
from the surrounding mvk.context() / @mvk.signal(). Pass tags={} to opt
out of context-tag inheritance.
with mvk.create_signal(name="parse-document", step_type="TOOL",
operation="parse", tool_name="document_parser"):
content = parse_pdf(file_path)
mvk.add_metered_usage() — attach billable quantities
Auto-instrumented LLM calls already populate mvk.metered_usage with token
counts. For everything else with a cost dimension — pages processed,
external API calls, storage bytes, image generations, characters translated —
call add_metered_usage() inside the current signal.
mvk.add_metered_usage([
{
"metric_kind": str, # e.g. "file.pages_processed", "api.calls", "storage.bytes"
"quantity": float, # Runtime-measured quantity (never hardcoded)
"uom": str, # Unit of measure: "page", "request", "byte", "image", ...
"metadata": {
"rate_per_unit": float, # Cost per UOM unit (USD by default)
"provider": str, # External vendor name
# ...any other free-form metadata
},
},
])
[!IMPORTANT]
rate_per_unitis what turns a quantity into a cost. The backend computes a line item asquantity × rate_per_unitand rolls it up bycustomer_id/tags/use_case.
- Omit it → the backend applies the configured rate template for that
metric_kind. Use this when a template exists and you want central control.- Set it inline → overrides the template, and is required for
metric_kinds that have no template. Always pair a numericrate_per_unitwith the correctuomsoquantityand rate are in the same units.
Also accepts Metric instances (from mvk_sdk.metrics import Metric) for the
three core fields only (metric_kind, quantity, uom); use the dict form
when you need metadata / rate_per_unit. If no active span exists, the call
is logged and skipped — it never raises.
End-to-end example — tools, custom costs, and auto-traced LLM together
import mvk_sdk as mvk
from openai import OpenAI
mvk.instrument(agent_id="doc-agent", api_key="mvk_...", tenant_id="tenant-123",
wrappers={"include": ["genai"]})
client = OpenAI()
def process_document(file_path: str, user_id: str,
session_id: str, customer_id: str) -> str:
with mvk.context(user_id=user_id, session_id=session_id, customer_id=customer_id):
# 1. Manual TOOL span — custom parsing cost (per-page)
with mvk.create_signal(name="parse-document", step_type="TOOL",
operation="parse", tool_name="document_parser"):
content = parse_pdf(file_path)
page_count = get_page_count(file_path)
mvk.add_metered_usage([{
"metric_kind": "file.pages_processed",
"quantity": page_count, # runtime value, never hardcoded
"uom": "page",
"metadata": {"rate_per_unit": 0.0015}, # $/page
}])
# 2. Manual TOOL span — paid third-party API
with mvk.create_signal(name="extract-metadata", step_type="TOOL",
operation="api_call", tool_name="metadata_service"):
metadata = call_metadata_api(content)
mvk.add_metered_usage([{
"metric_kind": "api.calls",
"quantity": 1,
"uom": "request",
"metadata": {"rate_per_unit": 0.05, "provider": "metadata-service"},
}])
# 3. Auto-traced LLM call — wrapper populates token metered_usage
summary = client.chat.completions.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": f"Summarize: {content[:2000]}"}],
)
return summary.choices[0].message.content
This single request produces one trace with three child spans
(parse-document, extract-metadata, the auto-traced chat), all stamped with
user_id / session_id / customer_id, each carrying its own metered_usage
line items so cost is attributed end-to-end.
Notes & limits
- Always capture
quantityfrom runtime values (e.g.len(file_bytes),page_count,1per API call) — never hardcode. - Multiple metrics per span — pass a list in one
add_metered_usage()call, or call multiple times within the same span (entries are appended). - No active span = no-op — calling outside a
@mvk.signal()/create_signal()/ auto-traced call logs a warning and skips silently. - Common
metric_kindexamples:token.prompt,token.completion,file.pages_processed,api.calls,storage.bytes,images.generated,characters.translated,email.sent,db.rows_scanned.
5. Reference
Context inheritance (Nearest Wins)
The SDK layers context from four sources, applied outermost → innermost:
Global (mvk.instrument(tags=...)) → Decorator (@mvk.signal /
@mvk.context) → Context manager (with mvk.context(...)) →
Per-request headers (x-mvk-*).
- Scalars (
user_id,session_id,customer_id,request_id, …): nearest wins completely. - Tags: merge all levels; nearest wins for duplicate keys.
- Empty values clear attributes:
mvk.user_id=""removes user attribution from this scope down.
Tag validation
- Maximum 10 tags per span after merging
- Keys:
^[a-z0-9._-]{1,64}$(dots allowed) - Values: UTF-8 strings ≤256 chars
- Validation errors are logged (or raised if
strict_validation=True)
valid, issues = mvk.validate_tags({
"user.id": "123", # dots allowed
"tier": "premium",
"INVALID!": "will-be-dropped", # invalid characters
})
Architecture
Memory-First Architecture — best for development, QA, and production (all environments):
Producers → Memory Queue (10MB) → Writer Thread → Exporter
↓
(exponential backoff retry)
↓
(on failure after 10 attempts)
↓
FailedBatchDisk
- Memory-first performance with a 10MB bounded queue, non-blocking producers
- Exponential backoff retry (1s to 5min); persistently failed batches saved to disk
- Fork-safe (Gunicorn, uWSGI, prefork servers); shuts down gracefully on SIGTERM/SIGINT, flushing pending spans
Step types (MVKStepType): LLM (token metrics), EMBEDDING (embedding
metrics), RETRIEVER (vector/search), TOOL (tool/HTTP + manual tool spans),
AGENT_CALL (orchestration), BATCH (batch ops). MEMORY is reserved for
future use.
Smart auto-instrumentation uses OpenTelemetry's proven strategy: if a
library is already imported → immediate instrumentation; if not → lazy hook via
wrapt.when_imported().
Supported integrations
AI Providers (wrappers={"include": ["genai"]}):
| Provider | Library Versions | Step Type | Token Tracking |
|---|---|---|---|
| OpenAI | 0.x, 1.x | LLM, EMBEDDING | ✓ metered_usage |
| Anthropic | 0.20-0.35 | LLM | ✓ metered_usage |
| Gemini | google.generativeai | LLM, EMBEDDING | ✓ metered_usage |
| AWS Bedrock | boto3 bedrock-runtime | LLM, EMBEDDING | ✓ metered_usage |
| Azure OpenAI | azure.ai.openai | LLM, EMBEDDING | ✓ metered_usage |
| Vertex AI | vertexai/google.cloud.aiplatform | LLM, EMBEDDING | ✓ metered_usage |
Vector Databases (wrappers={"include": ["vectordb"]}):
| Provider | Library Versions | Step Type | Metrics |
|---|---|---|---|
| Pinecone | 2.x-4.x | RETRIEVER | vector_count, dimension |
| Weaviate | 3.x-4.x | RETRIEVER | vector_count, dimension |
| ChromaDB | 0.4.x-0.5.x | RETRIEVER | vector_count, dimension |
| Qdrant | 1.x | RETRIEVER | vector_count, dimension |
Frameworks: Semantic Kernel, LangChain, LangGraph, Agno, CrewAI, OpenAI Agents.
Routers & Proxies: OpenRouter, LiteLLM.
Batch APIs: OpenAI, Anthropic, Azure OpenAI, AWS Bedrock, Gemini, Vertex AI,
LangChain, Agno, CrewAI, Semantic Kernel, and ThreadPool concurrent execution.
HTTP Clients (wrappers={"include": ["http"]}, disabled by default): HTTPX 0.25-0.27 (TOOL).
Performance characteristics
Memory-First Mode (all environments) — throughput 500–2000 spans/sec; ~10MB bounded queue; producer latency <100 µs (non-blocking); export latency p99 <100ms (network dependent); failed batches saved to disk for retry.
Batching defaults (all modes) — 2000 spans max, 2 MiB max, 3000 ms max, gzip compression (default) or none.
Serverless deployment
# AWS Lambda
from mvk_sdk.serverless import lambda_handler
@lambda_handler(flush_timeout_ms=1000) # auto flush on completion
def handler(event, context):
# Auto-detects Lambda; optimizes batch_size=1, flush=100ms, memory-first
return process_request(event)
# Google Cloud Functions — auto-detected via K_SERVICE / FUNCTION_NAME
import mvk_sdk as mvk
mvk.instrument(agent_id="gcf-function")
def main(request):
result = process_request(request)
mvk.force_flush() # manual flush for Cloud Functions
return result
export MVK_SERVERLESS=true # force serverless optimizations
6. Support
- Agent Onboarding](https://help.mavvrik.ai/content/agent-onboarding-1)
- Quick Start Guide
- Email: support@mavvrik.ai
Project details
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 mvk_sdk_py-1.3.13.tar.gz.
File metadata
- Download URL: mvk_sdk_py-1.3.13.tar.gz
- Upload date:
- Size: 838.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c74db48a16ef10eb66016cd8b184a74ce119513a2583c788b6444933d93e031c
|
|
| MD5 |
bcfaf37e50f227c4964f5d0000b9339b
|
|
| BLAKE2b-256 |
c3a8bfb47315a2522b510d17b25e7bb36566d847363e210c1d095df50417ce32
|
Provenance
The following attestation bundles were made for mvk_sdk_py-1.3.13.tar.gz:
Publisher:
release.yml on cloudwizio/agentic-python-sdk
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
mvk_sdk_py-1.3.13.tar.gz -
Subject digest:
c74db48a16ef10eb66016cd8b184a74ce119513a2583c788b6444933d93e031c - Sigstore transparency entry: 2017603095
- Sigstore integration time:
-
Permalink:
cloudwizio/agentic-python-sdk@b679cd318c2d55719ddd122221434d69f62a3767 -
Branch / Tag:
refs/heads/master - Owner: https://github.com/cloudwizio
-
Access:
internal
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@b679cd318c2d55719ddd122221434d69f62a3767 -
Trigger Event:
workflow_dispatch
-
Statement type:
File details
Details for the file mvk_sdk_py-1.3.13-py3-none-any.whl.
File metadata
- Download URL: mvk_sdk_py-1.3.13-py3-none-any.whl
- Upload date:
- Size: 900.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
191bb66056e15ec75df9e265944050f787aaa719fef68b76eeedbace02c763a7
|
|
| MD5 |
c7559100c7f2092db9eb456bbcb4e988
|
|
| BLAKE2b-256 |
634f8539f0cee556e1e56788fb125e939acfa5db3adff4b1970b20e6d5cd3afd
|
Provenance
The following attestation bundles were made for mvk_sdk_py-1.3.13-py3-none-any.whl:
Publisher:
release.yml on cloudwizio/agentic-python-sdk
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
mvk_sdk_py-1.3.13-py3-none-any.whl -
Subject digest:
191bb66056e15ec75df9e265944050f787aaa719fef68b76eeedbace02c763a7 - Sigstore transparency entry: 2017603574
- Sigstore integration time:
-
Permalink:
cloudwizio/agentic-python-sdk@b679cd318c2d55719ddd122221434d69f62a3767 -
Branch / Tag:
refs/heads/master - Owner: https://github.com/cloudwizio
-
Access:
internal
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@b679cd318c2d55719ddd122221434d69f62a3767 -
Trigger Event:
workflow_dispatch
-
Statement type: