Skip to content

Documentation / Reference / Configuration and policy

Configuration, deadlines, and policies

Most applications select a complete closed-schema TOML preset through Assistant; they do not construct the values below. Use Define an Assistant for the normal path. This page documents the advanced compatibility interface.

One preset declares the provider-policy identities, exact models and routes, language/voice choices, operation and lifecycle deadlines, measurement requirements, standard tools, Judge slot, price book, extras, and logical credential sources. Every field is explicit. Unknown fields, partial deadline tables, unsafe durations, and route/policy mismatches fail locally.

Environment bootstrap

Backend and worker startup must set VAANI_ENVIRONMENT to exactly development or production. Missing, blank, differently-cased, and unknown values fail locally; there is no default.

from vaani import Assistant, bootstrap_environment

configuration = bootstrap_environment()
assistant = Assistant(
    name="Reception",
    prompt="Help the caller briefly.",
    environment=configuration,
)

bootstrap_environment() reads only the selected versioned TOML document and then resolves its exact preset profile. Development and production documents are complete alternatives: VAANI never merges them and neither document has precedence over the other. The resolved environment document and preset are immutable, and their identities and SHA-256 digests are retained in CallResult.provenance.preset_versions.

Tests can avoid process-environment state by passing an explicit environment mapping and can inject the returned configuration directly:

configuration = bootstrap_environment(
    environ={"VAANI_ENVIRONMENT": "development"}
)

Pass EnvironmentDocuments(development=..., production=...) to select deployment-owned manifests. Both use the closed vaani.environment.v1 schema:

schema = "vaani.environment.v1"
environment = "development"
revision = "1"
profile = "basic-monolingual@1"
# Explicit immutable deployment capability: none, cloud, or self-host.
noise_cancellation_mode = "none"
digest = "<sha256 of the other canonical fields>"

noise_cancellation_mode is optional for legacy documents and defaults to none; new documents should declare it. A preset noise profile is Cloud-only, so a worker in self-host or none mode rejects that profile before LiveKit job admission. VAANI never infers this capability from a URL or hostname. Use cloud only when the deployment has explicitly approved the Cloud noise plugin; use none when self-hosted audio should remain unfiltered.

Unknown keys, an environment/file mismatch, a non-exact profile, or digest drift fail before provider access. Frontends should receive only derived read-only status; they must never edit these files or reconfigure the running Assistant.

Development Evaluation Catalog

The Starter loads a backend-owned development Route Catalog:

catalog = DevelopmentEvaluationCatalog.from_environment(
    configuration,
    route_catalog=load_production_deployment(
        "development-options@1.toml"
    ),
)

Despite the loader name, this document is an allow-list for local testing; it does not make development production-locked. public_summary() exposes only safe target IDs, provider/model labels, supported languages and voices, fixed settings for display, and readiness reasons. Credentials and secret values are never included.

The browser submits IDs from that exact response:

{
  "language": "en-IN",
  "selection": {
    "speech_recognition": "stt-deepgram-nova-3",
    "reasoning": "llm-vertex-gemini-3-flash-preview",
    "speech_generation": "tts-smallest-lightning-v3-1-pro",
    "voice": "meher"
  }
}

catalog.bind_session(...) rejects unknown, unavailable, or language/voice-incompatible targets and returns an immutable EvaluationSession. The token server serializes the validated request as private job metadata; one worker binds it to that call. Installed plugins never add targets automatically.

The older complete-profile allow-list remains supported when no route_catalog is supplied: the browser then sends one exact evaluation_profile_identity. New Starter applications use route selection. Production creates neither catalog and rejects both forms.

Production Deployment Profile

A production document instead names one production_deployment and its production_lock. The profile fixes one primary plus ordered fallbacks for speech recognition, reasoning, and speech generation. Generate the secret-free review bytes with the production-only API, write them as JSON, review them, and commit them:

import json
from pathlib import Path

from vaani import (
    create_production_deployment_lock,
    load_production_deployment,
)

profile = load_production_deployment("production-deployment@1.toml")
lock = create_production_deployment_lock(
    profile,
    "production@1#sha256:<production-environment-digest>",
)
Path("production-deployment@1.lock.json").write_text(
    json.dumps(dict(lock), indent=2, sort_keys=True) + "\n",
    encoding="utf-8",
)

The lock binds the environment, preset, complete Route Target fingerprints, fallback order, Provider Failure Domains, and Resilience Policy values. Startup verifies it before worker readiness. Configuration changes require new documents, a regenerated lock, and a restart.

Use this page to find the right public value. In the matching source release, contracts/public/v1/vectors/construction.json and src/vaani/_model.py define exact fields and defaults.

CallSetup map

Field Type Owner
prompt str consumer
mode CascadeMode consumer route/fallback selection
greeting GreetingPolicy consumer wording/failure choice
language LanguagePolicy consumer enabled languages
conversation ConversationPolicy consumer behavior/profile
ending EndingPolicy consumer terminal media + lifecycle budgets
measurement MeasurementPolicy consumer capture/usage requirements
recording RecordingPolicy consumer legal/business choice
transfer TransferPolicy consumer allowed routes
tools tuple of RuntimeTool consumer business capabilities
judge optional JudgeSetup consumer evaluation definition

All are immutable once supplied. Build a new setup with dataclasses.replace or use CallUpdate for a running call.

Lifecycle deadlines

LifecycleDeadlinePolicy covers connection, readiness, greeting preparation and playout, terminal media, work drain, teardown, base finalization, Judge, and total startup/closure.

These are absolute phase budgets, not per-retry waits. Assistant compiles the values declared by the selected preset; it does not substitute dataclass defaults.

Operation deadlines

OperationDeadlinePolicy covers provider operations, tools, update preparation, retired resources, blind transfer, recording start/stop, and result publication.

Keep the total consistent with lifecycle budgets. For example, a terminal-media provider retry must still finish within the terminal-media phase cutoff.

Conversation policy

Choose either:

ConversationPolicy.from_profile("vertex-v42@1")

or explicit ConversationPolicyValues. Explicit values include both agent_response_progress_seconds and agent_response_total_seconds; VAANI does not insert hidden watchdog limits. Compatibility profiles are revisioned behavior bundles, not global defaults and never include business wording.

Assistant presets use the same exclusive choice. Keep a revisioned profile:

[conversation]
profile = "vertex-v42@2"
vad = "silero"

Or declare every value explicitly:

[conversation]
maximum_endpoint_seconds = "1.5"
minimum_consecutive_speech_seconds = "0.1"
silence_seconds = "13"
mechanical_repetition_enabled = true
agent_response_progress_seconds = "10"
agent_response_total_seconds = "60"
vad = "silero"

Do not mix profile with explicit values. Write durations as quoted canonical Decimal strings so TOML parsing cannot lose precision. All durations must be positive except minimum_consecutive_speech_seconds, which may be "0" for profiles equivalent to collection@1.

When an Agent-response watch expires, VAANI preserves that timeout as CallResult failure evidence before it attempts any recovery media. The first configured recovery receives one independent absolute budget equal to provider_operation_seconds for synthesis and confirmed playout; it does not extend the original response total or restart expired LLM/Tool work. Missing, failed, cancelled, or timed-out playout is a terminal failure with immutable evidence. A participant turn observed during recovery waits until recovery playout finishes. A second unresolved watch expiry performs the configured typed terminal action instead of opening another recovery loop.

Measurement and privacy

TranscriptCapturePolicy is FULL, REDACTED, or OMITTED. MeasurementPolicy can require usage dimensions and known estimated cost. Unmet required evidence affects validated behavior/failure rather than being silently guessed.

Assistant definition lock

Judge evidence policy

Preset schema v2 makes Judge evidence choices explicit:

[judge]
enabled = true
transcript = "REDACTED" # FULL, REDACTED, or OMITTED
include_tool_activity = true
include_consumer_evidence = false
on_empty = "EVIDENCE_EMPTY"
on_oversize = "EVIDENCE_TOO_LARGE"

Judge transcript modes do not change the terminal CallResult capture policy:

  • FULL sends the captured text and audible-prefix evidence unchanged.
  • REDACTED keeps each item's speaker, order, language, disposition, revision, and provider-segment references. It sends text with email addresses, phone-like values containing 8 to 15 actual digits, labelled OTP/PIN, credential, token, account/customer/member/loyalty identifiers, and obvious long credential-like tokens replaced by [REDACTED]. Audible-prefix evidence is omitted. Dates, semantic versions, and separator-heavy non-phone facts remain unchanged. Each text value is at most 2,048 Unicode characters, including a final …[TRUNCATED] marker when needed.
  • OMITTED sends no transcript items.

The runtime applies this projection and the existing 256 KiB total Judge evidence limit before invoking a Judge provider. REDACTED is intended to retain conversational meaning, not to make arbitrary sensitive prose safe; consumer business evidence must still use its separate allow-list.

Rubric instructions and output schemas remain business-owned Python, never preset content. If include_consumer_evidence is true, pass a per-call provider as consumer_evidence= to Assistant.configure_judge(...). It is invoked only when the preset permits it; the result must be JSON-safe and at most 16 KiB. Return only allow-listed business facts, never credentials, tokens, arbitrary objects, or unfiltered provider payloads.

A minimal consumer definition is:

from vaani import Call

JUDGE_SCHEMA = {
    "type": "object",
    "additionalProperties": False,
    "required": ["resolved", "reason"],
    "properties": {
        "resolved": {"type": "boolean"},
        "reason": {"type": ["string", "null"]},
    },
}


async def judge_evidence(call: Call) -> object:
    # Project reviewed facts; do not return all of call.state.
    return {
        "requested_action": call.state.get("requested_action"),
        "action_completed": call.state.get("action_completed"),
    }


assistant.configure_judge(
    rubric_id="support-resolution",
    revision="1",
    instructions="Judge resolution only from the supplied evidence.",
    output_schema_id="support-resolution-result",
    output_schema_revision="1",
    output_schema=JUDGE_SCHEMA,
    consumer_evidence=judge_evidence,
)

This requires include_consumer_evidence = true in the selected v2 preset. When it is false, omit the provider; VAANI does not silently widen the preset's evidence boundary.

Legacy v1 identities retain the previous redacted-transcript, included-tool, no-consumer-evidence behavior. Policy changes use a new v2 preset revision and therefore contribute to the production lock and call provenance identity.

vaani preset lock PRESET --output production-lock.json records the exact deployment identities without secret values. Pass that file as Assistant(..., production_lock="production-lock.json"). Assistant.check() then compares every identity—including the installed VAANI version and enabled Judge rubric/schema identities—before lifecycle ownership. The lock also binds each constructed official Adapter manifest to its exact installed distribution version, including LiveKit transport and any enabled transfer or recording Adapter. Enabled recording adds its preset requirement and secret-free storage binding digest; raw bucket, prefix, credential, and audio values remain absent. The CLI form targets an Assistant without named prompts, transfer routes, or a recording binding. After configuring those capabilities, tool disables, or language/voice overrides, use Assistant.create_production_lock() so the lock binds the exact tool revisions, allowed choices, language profiles, transfer-route digests, and recording identity. This lock is separate from the environment's Production Deployment Profile lock; production verifies both applicable contracts.

Updates

CallUpdate always requires:

  • expected_revision;
  • reason;
  • boundary;
  • interrupted-reply handling when required.

The six optional change fields default to UNCHANGED: prompt, language, tools, STT, reasoning, and TTS.

Next: public API map or Assistant update boundaries.