Vaanieval
Reference

Configuration

Every SDK option, environment variable and dashboard setting in one place, with defaults and the tradeoff each one carries.

Python SDK

VaaniObserver(
    endpoint=None,
    api_key=None,
    spool_directory=None,
    capture=None,
    instrumentations=None,
    endpoints=None,
    upload=None,
    strict=False,
)
OptionTypeDefaultNotes
endpointstrNoneDashboard base URL; only used by upload
api_keystrNoneBearer token; not validated by the local dashboard
spool_directorystr | Pathcwd/.vaani-spoolPackage output directory
capturedictsee belowWhat to record
instrumentationsdict{http: True, websocket: True}Which clients to patch
endpointslist[dict][]Provider URL rules
uploaddictsee belowUpload behaviour
strictboolFalseRaise instead of degrading

Node.js SDK

new VaaniObserver({
  endpoint: undefined,
  apiKey: undefined,
  spoolDirectory: undefined,
  capture: undefined,
  instrumentations: undefined,
  endpoints: undefined,
  upload: undefined,
  strict: false,
});

Identical shape in camelCase. instrumentations uses { fetch, websocket } rather than { http, websocket }.

capture

Option (Python / Node)DefaultEffect
audio / audioTrueRecord caller and agent PCM
http_bodies / httpBodiesFalseCapture request and response bodies
websocket_text_frames / websocketTextFramesFalseConfiguration flag; the documented socket observer records counts/lifecycle, not frame contents
stt_content / sttContentFalseCapture transcript text
payload_max_bytes / payloadMaxBytes16384Size bound on every payload

Audio is on by default when frames are supplied. http_bodies and stt_content can put prompts and caller speech in plain text into events.jsonl and the dashboard's database. Review recording permissions, retention and access for your use case; these settings provide no legal or compliance guarantee. See Capture and privacy.

endpoints

{"id": "llm", "type": "llm", "url": "https://api.openai.com/v1", "match": "path"}
FieldRequiredValues
idyesUnique; duplicates raise at construction
typeyesstt | llm | tts
urlyeshttp, https, ws or wss
matchnopath (default) | origin | exact

An unknown match raises TypeError. A URL matching more than one rule at the same scheme precedence raises Ambiguous Vaani endpoint rules for …. See Endpoints and instrumentation.

upload

The environment column is Python only. The Node SDK accepts the same three transport options as camelCase constructor keys — { retries, timeoutMs, minThroughputBps } — with the same defaults (3, 30000, 131072). It does not compress.

OptionEnvironmentDefaultEffect
retriesVAANI_UPLOAD_RETRIES3Retries per request for eligible transient failures; checkout-specific status rules are below
timeout_sVAANI_UPLOAD_TIMEOUT_S30.0Socket timeout for the handshake
min_throughput_bpsVAANI_UPLOAD_MIN_THROUGHPUT_BPS131072Assumed worst-case speed; extends the timeout by body size
compressVAANI_UPLOAD_COMPRESSTruegzip objects of 64 KiB or more, when the ingest advertises gzip

The environment column matters because from_env() — the constructor every integration guide shows — builds the observer itself. Until these variables existed, a value set from this table was silently discarded. An explicit upload={...} argument still wins over the environment.

timeout_s is a base request budget, with a transfer-size allowance controlled by min_throughput_bps. Larger allowances help slow uplinks but can occupy a shutdown hook longer. Set min_throughput_bps to 0 to disable the allowance. Test your longest recordings against the actual SDK revision and network.

upload_package(finalized, timeout=...) provides an overall upload budget across objects and retries. It is not a delivery or durability guarantee: finalization, local file work and the host's shutdown deadline also matter. Digests describe the uncompressed object, never the transfer encoding. A failed upload normally leaves its package on the spool; retry from persistent storage with python -m vaani_observer.drain.

Development-checkout uploader hardening

The current checkout adds streamed object uploads, an inventory at session creation, per-object upload headers, bounded retries that respect Retry-After, and a 300-second default overall upload budget. These changes are available in the pinned hosted source previews in the Python and Node.js quickstarts. Both revisions were installed in clean environments. Registry publication and public service admission remain separate; check your installed revision before relying on this behavior.

Development-checkout settingDefaultOverride
Python upload.total_timeout_s300 secondsupload_package(finalized, timeout=90) or the sync equivalent overrides the overall budget
Node.js upload.totalTimeoutMs300000 millisecondsuploadPackage(finalized, 90000) overrides the overall budget

In Python, configure the constructor with upload={"total_timeout_s": 300}; in Node.js use upload: { totalTimeoutMs: 300000 }. Omitting the per-call override (or passing Python timeout=None) uses the configured total budget. Node's second argument is a number of milliseconds, not an options object. Total budgets must be finite and positive. This is distinct from Python timeout_s or Node.js timeoutMs, which are base per-request budgets. Hashing, compression where applicable, requests and retries share the overall budget.

Python total_timeout_s is a constructor setting only; no new environment variable is provided. VAANI_UPLOAD_TIMEOUT_S continues to configure the per-request base, not the overall budget. Eligible retry statuses are 408, 425, 429, 500, 502, 503 and 504. Named quota, validation and limit errors remain permanent even on 429; ordinary 401/403 responses are not retried. An explicit expired-object-capability response can refresh the idempotent create once, without resetting the total budget.

Refresh requires an explicit expiry code or message. The hosted development backend uses 401 with detail.code: "upload_expired". An elapsed expires_at alone does not permit refreshing a generic 401 or 403: invalid, tampered or wrong-object credentials remain permanent failures. Hosted 429 responses with detail.code: "monthly_quota_exhausted" or "storage_quota_exhausted" are also permanent, not transient retry signals.

For a create response with upload URLs, the SDK repeats PUTs and completion, while the hosted development backend verifies identical content without replacing accepted objects.

If the initial create or refresh explicitly returns status: "accepted" with upload_urls: {}, the checkout SDKs skip object uploads and repeat completion with the unchanged inventory. Missing URLs without the explicit accepted status are not a success shortcut. The current hosted development backend uses this accepted-replay response, also returning empty upload_headers and the same session/upload identity. Different manifests or inventories fail with 409 before the shortcut. The compatible SDK handling is included in those pinned source previews; older or unqualified Git revisions may not support this backend contract. See HTTP API.

Streaming limits object buffering, but hashing and optional Python gzip scratch files still consume I/O, CPU and disk. A retry reopens the stream rather than making failed delivery durable. HTTPS or loopback HTTP is required, redirects are rejected, and an HTTPS ingest cannot downgrade to an HTTP object upload. Only allowed object headers are applied without forwarding the ingest bearer token. See HTTP API for the additive protocol fields; local backwards compatibility does not imply hosted acceptance.

Python custom transport overrides have additional limits: legacy four-argument callables cannot be forcibly interrupted. Unmarked overrides receive byte bodies only up to 1 MiB; larger uploads require supports_streaming=True. Adapters must enforce timeouts and reject redirects themselves. The native Python transport ignores proxy environment variables, while Node sends raw uncompressed objects. System DNS resolution and stalled filesystem work also prevent a universal hard wall-clock guarantee. See the SDK's upload documentation before replacing its transport.

strict

ValueBehaviourUse it
False (default)Instrumentation errors are swallowed; degradation surfaces in capture_statusProduction
TrueErrors raiseStaging and CI

The default trades visibility for safety: a bug in observability cannot take down a call, but capture can degrade silently. Your signals that it happened are capture_status in the manifest and the unverifiable class in the dashboard — not an exception.

LiveKit integration environment

Read by VaaniLiveKitRecorder.from_env().

VariableDefaultPurpose
VAANI_ENABLEDfalseMaster switch — off by default
VAANI_ENDPOINThttp://localhost:8000Dashboard base URL
VAANI_API_KEYlocal-devBearer token
VAANI_SPOOL_DIR.vaani-spoolPackage output directory
VAANI_CAPTURE_AUDIOtrueRecord PCM
VAANI_CAPTURE_HTTP_BODIEStrueCapture bodies
VAANI_CAPTURE_STT_CONTENTtrueCapture transcripts
VAANI_PAYLOAD_MAX_BYTES16384Payload bound
VAANI_AGENT_IDlivekit-agentDefault agent id
VAANI_UPLOADtrueUpload after finish()
VAANI_UPLOAD_TIMEOUT_S30.0Socket timeout for the handshake
VAANI_UPLOAD_RETRIES3Retries per request
VAANI_UPLOAD_MIN_THROUGHPUT_BPS131072Assumed worst-case upload speed
VAANI_UPLOAD_COMPRESStruegzip large objects in transit
VAANI_ENDPOINTS[]JSON array of endpoints rules; enables connection capture

These defaults are more permissive than the core SDK's — VAANI_CAPTURE_HTTP_BODIES and VAANI_CAPTURE_STT_CONTENT default to true. Set them to false for production traffic unless you have reviewed the privacy implications.

from_env() never raises: on failure it returns an inert recorder whose methods are no-ops and whose enabled is False. Check recorder.enabled at startup if you want a misconfiguration to be visible; recorder.last_error carries the reason. When VAANI_ENABLED is set but recording ends up off, the reason is also logged at ERROR — silence there was a bug, not a design.

When VAANI_ENDPOINTS is empty, HTTP and WebSocket instrumentation is not installed at all: patching clients with no rules to match is pure cost.

Dashboard

VariableDefaultPurpose
VAANI_DATA_DIR./dataSQLite file and uploaded objects
VAANI_REQUIRE_API_KEY0Optional local ingest gate; does not authenticate reads or isolate tenants
VAANI_EVALUATIONS_ENABLEDoffOnly 1 opts into manual external evaluation
ELEVENLABS_API_KEY—Challenger transcription; required to evaluate
OPENAI_API_KEY—Semantic risk judge
STT_EVAL_JUDGE_MODELgpt-4o-miniOpenAI judge model; compatible OpenAI model fallback may be used
VAANI_ENV_FILE./.envos.pathsep-separated dotenv paths

Fixed limits, not configurable:

ConstantValue
MAX_UPLOAD_BYTES128 MiB
ALLOWED_OBJECTSevents.jsonl, call.audio, caller.audio, agent.audio
Challenger worker pool2 threads
COHORT_SAMPLE_LIMIT25 sessions
CHALLENGER_MODELSelevenlabs_scribe_v2

These limits describe the localhost app.main:app path, not the separate hosted beta under construction. The local entrypoint refuses hosted-environment startup except for explicitly configured curated demo operation. Do not expose it behind a key as a tenant-safe service.

External evaluation requires the current consent_version from GET /v1/evaluation-policy on every confirmed run. It sends full recorded caller audio to ElevenLabs and transcript comparison content to OpenAI when judging is configured. The local policy supplies no cost estimate or allowance (null); provider billing and a global spend cap are not managed by this path.

SettingSensitive callsSynthetic debugging calls
audioOff unless recording is approved; no audio review when offOn
http_bodiesOffOn
stt_contentOff unless evaluating STTOn
websocket_text_framesOffDo not rely on it for frame-content capture
payload_max_bytes1638465536
strictFalseTrue
SDK sourcePinned tag or commitBranch is fine
Spool cleanupRequiredOptional

Upload methods do not remove spool directories. The Python drainer has a separate delivered-package cleanup policy; configure it deliberately. The local dashboard has no automated retention policy. More capture improves review coverage but increases data exposure, disk use and maintenance across spools, database rows, objects and backups.

Next

On this page