Spaces:
Sleeping
Sleeping
File size: 3,288 Bytes
3c8a5a9 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 | """
PlotWeaver Voice Agent — Backend interface
==========================================
Separates *what the bot says* (dialogue.py) from *what actually happens*
(account lookups, card blocks, transfers, order status, ...).
How it wires in
---------------
A state spec in dialogue.py may carry an optional ``"action"`` key naming a
Backend method. When the FSM transitions *into* that state, it calls the
method with the current slots dict; the method returns a dict of values that
are merged back into slots, which the prompt templates then interpolate.
Contract
--------
Backends return ONLY language-neutral data — numbers, ids, names, dates the
caller supplied. They never return Hausa/English sentences: all phrasing lives
in dialogue.py prompts. This keeps a real integration (bank sandbox, telecom
API, logistics API) free of any localization concern.
MockBackend ships canned-but-input-aware data for the demo. To go live, drop in
an implementation satisfying the ``Backend`` protocol; dialogue.py is unchanged.
"""
from __future__ import annotations
import random
import string
from typing import Protocol, runtime_checkable
def _ref(prefix: str, n: int = 6) -> str:
return prefix + "".join(random.choices(string.ascii_uppercase + string.digits, k=n))
@runtime_checkable
class Backend(Protocol):
# --- bank ---
def get_balance(self, slots: dict) -> dict: ...
def block_card(self, slots: dict) -> dict: ...
def transfer(self, slots: dict) -> dict: ...
# --- telecom ---
def buy_airtime(self, slots: dict) -> dict: ...
def buy_bundle(self, slots: dict) -> dict: ...
def file_complaint(self, slots: dict) -> dict: ...
# --- ecommerce ---
def check_order(self, slots: dict) -> dict: ...
def reschedule(self, slots: dict) -> dict: ...
def return_item(self, slots: dict) -> dict: ...
class MockBackend:
"""Deterministic-enough mock. Echoes user-provided slots so confirmations
and receipts reflect what the caller actually said, and fabricates only the
values a real backend would return (balances, refs, etc.)."""
# --- bank ---
def get_balance(self, slots: dict) -> dict:
return {"balance": "245,000", "account_last4": slots.get("digits", "")}
def block_card(self, slots: dict) -> dict:
return {"card_eta_days": "3-5", "block_ref": _ref("BLK")}
def transfer(self, slots: dict) -> dict:
return {
"txn_ref": _ref("TXN"),
"amount": slots.get("amount", ""),
"name": slots.get("name", ""),
}
# --- telecom ---
def buy_airtime(self, slots: dict) -> dict:
return {"amount": slots.get("amount", ""), "airtime_balance": "1,500"}
def buy_bundle(self, slots: dict) -> dict:
return {"bundle": slots.get("bundle", "")}
def file_complaint(self, slots: dict) -> dict:
return {"ticket_id": _ref("TKT")}
# --- ecommerce ---
def check_order(self, slots: dict) -> dict:
return {"order_id": slots.get("digits", "")}
def reschedule(self, slots: dict) -> dict:
return {"order_id": slots.get("digits", ""), "date": slots.get("date", "")}
def return_item(self, slots: dict) -> dict:
return {"order_id": slots.get("digits", ""), "return_ref": _ref("RET")}
|