Advanced example: porting Loyalty v4.2¶
The isolated birla-agent-vertex-with-vaani project tests the main portability
claim: a large bot can keep its business behavior while VAANI replaces its
voice plumbing. The original production repository remains unchanged.
What stays in the bot¶
| Loyalty concern | Consumer implementation |
|---|---|
| caller lookup and parallel account reads | @assistant.prepare |
| personalized Hindi or English greeting | call-aware greeting function |
| intent flows | base prompt plus registered named prompts |
| account, KYC, QR, points, complaint, and scheme actions | typed @assistant.tool functions |
| identity and write authorization | business state and tools |
| specialist destinations | named transfer routes |
| evaluation rubric | bot-owned Judge configuration |
| frontend, UAT endpoints, SIP, and deployment | consuming application |
VAANI owns worker registration, LiveKit media, streaming STT/LLM/TTS, transcripts, VAD, interruption, silence timing, standard tools, cleanup, and the terminal result.
Prepare before greeting¶
Use incoming identity to fetch customer context once:
from vaani import Call
@assistant.prepare
async def prepare_call(call: Call) -> object:
customer = await loyalty.lookup(call.caller)
return {
"customer_id": customer.id,
"customer_name": customer.first_name,
"language": customer.language,
"participant_silence_nudges": (
"Hello? Are you still there?",
"I still cannot hear you.",
),
"participant_inactivity_ending": "I will end this call for now.",
}
The real port performs several independent read-only prefetches and records which ones failed. It does not replace failed live data with fixtures. The greeting can use the successful customer lookup immediately; prompts and tools can handle missing secondary data honestly.
Register many flows without voice code¶
for intent in FLOW_INTENTS:
assistant.add_prompt(intent.lower(), flow_prompt(intent))
for business_tool in ACTIVE_BUSINESS_TOOLS:
assistant.tool(business_tool)
The bot does not create provider clients, audio buffers, VAD handlers, or transcript publishers.
Protect write tools inside the consumer:
@assistant.tool
async def create_case(call: Call, summary: str) -> str:
"""Create one case after caller verification."""
if not call.state["identity_verified"]:
return "Case not created: caller identity is not verified."
result = await loyalty.create_case(
call.state["customer_id"],
summary,
idempotency_key=f"{call.call_id}:create-case",
)
return result.safe_message
If an external timeout occurs after submission, return “completion unknown.” Do not retry a side effect merely because the model repeats the request.
What the current acceptance proves¶
The isolated consumer passed:
- 205 backend tests covering the business surface;
- frontend formatting, lint, type checking, and production build;
- development and production configuration checks;
- a browser-room call with a personalized greeting, participant and assistant transcripts, streaming Deepgram STT, streaming Gemini reasoning, single-utterance SmallestAI streaming playout, Silero VAD, and browser audio;
- microphone mute/unmute, WebRTC noise suppression, echo cancellation, and automatic gain control; and
- graceful browser ending with a
COMPLETEDVAANI outcome.
An earlier registered-customer probe completed the customer lookup and six read-only prefetch APIs. The acceptance path performed no business writes.
This does not prove production SIP routing, carrier audio, real transfer, write APIs, capacity, monitoring, or rollback. Run those checks in the target deployment before traffic.
Next: port an existing bot or use the production checklist.