Spaces:
Sleeping
Sleeping
| """ | |
| 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)) | |
| 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")} | |