Toadoum commited on
Commit
a87bd84
·
verified ·
1 Parent(s): d8a53e2

Update dialogue.py

Browse files
Files changed (1) hide show
  1. dialogue.py +79 -2
dialogue.py CHANGED
@@ -25,6 +25,8 @@ class DialogueState:
25
  turn_count: int = 0
26
  escalate_to_human: bool = False
27
  history: list = field(default_factory=list)
 
 
28
 
29
  def to_dict(self):
30
  return asdict(self)
@@ -195,12 +197,36 @@ SCENARIOS = {
195
  }
196
 
197
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
198
  def get_prompt(vertical: str, state_name: str) -> dict:
199
  if state_name == "escalate_virtual":
200
  return {"ha": "Zan juya ka ga wakili na mutum yanzu. Ka jira ɗan lokaci.",
201
  "en": "I'll transfer you to a human agent now. Please hold."}
202
  if state_name == "exit":
203
  return {"ha": "Nagode. Sai watan.", "en": "Thank you. Goodbye."}
 
 
204
  s = SCENARIOS[vertical]["states"].get(state_name)
205
  if not s:
206
  return {"ha": "Ban fahimci abin da ka ce ba.", "en": "I didn't understand."}
@@ -208,6 +234,9 @@ def get_prompt(vertical: str, state_name: str) -> dict:
208
 
209
 
210
  def get_expected_slot(vertical: str, state_name: str) -> Optional[str]:
 
 
 
211
  s = SCENARIOS[vertical]["states"].get(state_name)
212
  return s.get("expects") if s else None
213
 
@@ -217,21 +246,69 @@ def transition(state: DialogueState, intent: str, entities: dict) -> DialogueSta
217
  for k, v in entities.items():
218
  state.slots[k] = v
219
 
 
220
  if intent == "human_agent" or state.turn_count > 12:
221
  state.current_state = "escalate_virtual"
222
  state.escalate_to_human = True
223
  return state
224
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
225
  current = SCENARIOS[state.vertical]["states"].get(state.current_state)
226
  if not current:
227
  state.current_state = "greeting"
228
- return state
229
 
 
230
  next_state = current.get("transitions", {}).get(intent)
 
 
 
 
 
 
 
 
 
 
 
 
231
  if next_state:
232
  state.current_state = next_state
233
  target = SCENARIOS[state.vertical]["states"].get(next_state, {})
234
  if target.get("escalate"):
235
  state.escalate_to_human = True
 
 
 
 
 
 
 
 
 
 
 
236
 
237
- return state
 
25
  turn_count: int = 0
26
  escalate_to_human: bool = False
27
  history: list = field(default_factory=list)
28
+ consecutive_unknowns: int = 0
29
+ state_before_fallback: Optional[str] = None
30
 
31
  def to_dict(self):
32
  return asdict(self)
 
197
  }
198
 
199
 
200
+ # Vertical-specific fallback prompts — spoken when the user asks something
201
+ # out of scope. Each prompt: (1) acknowledges confusion, (2) lists the
202
+ # services this bot CAN perform in that vertical, (3) offers human agent.
203
+ FALLBACK_PROMPTS = {
204
+ "bank": {
205
+ "ha": "Ban fahimci tambayarka ba. A nan, zan iya taimake ka da 'duba ma'auni', 'toshe kati', ko 'canjin kuɗi'. Don wasu tambayoyi, ka ce 'wakili' don yin magana da mutum.",
206
+ "en": "I didn't understand your question. Here I can help with 'check balance', 'block card', or 'transfer money'. For other questions, say 'agent' to speak with a person.",
207
+ },
208
+ "telecom": {
209
+ "ha": "Ban fahimci tambayarka ba. Zan iya taimake ka da 'saya airtime', 'saya bundle', ko 'yin korafi'. Don wasu tambayoyi, ka ce 'wakili' don yin magana da mutum.",
210
+ "en": "I didn't understand your question. I can help with 'buy airtime', 'buy bundle', or 'file a complaint'. For other questions, say 'agent' to speak with a person.",
211
+ },
212
+ "ecommerce": {
213
+ "ha": "Ban fahimci tambayarka ba. Zan iya taimake ka da 'bincika oda', 'sake tsara lokaci', ko 'mayar da kaya'. Don wasu tambayoyi, ka ce 'wakili' don yin magana da mutum.",
214
+ "en": "I didn't understand your question. I can help with 'check order', 'reschedule delivery', or 'return an item'. For other questions, say 'agent' to speak with a person.",
215
+ },
216
+ }
217
+
218
+ # After this many consecutive 'unknown' intents, auto-escalate to human.
219
+ MAX_CONSECUTIVE_UNKNOWNS = 2
220
+
221
+
222
  def get_prompt(vertical: str, state_name: str) -> dict:
223
  if state_name == "escalate_virtual":
224
  return {"ha": "Zan juya ka ga wakili na mutum yanzu. Ka jira ɗan lokaci.",
225
  "en": "I'll transfer you to a human agent now. Please hold."}
226
  if state_name == "exit":
227
  return {"ha": "Nagode. Sai watan.", "en": "Thank you. Goodbye."}
228
+ if state_name == "fallback":
229
+ return FALLBACK_PROMPTS.get(vertical, FALLBACK_PROMPTS["bank"])
230
  s = SCENARIOS[vertical]["states"].get(state_name)
231
  if not s:
232
  return {"ha": "Ban fahimci abin da ka ce ba.", "en": "I didn't understand."}
 
234
 
235
 
236
  def get_expected_slot(vertical: str, state_name: str) -> Optional[str]:
237
+ if state_name == "fallback":
238
+ # Fallback accepts any intent — user might repeat, rephrase, or escalate
239
+ return "intent"
240
  s = SCENARIOS[vertical]["states"].get(state_name)
241
  return s.get("expects") if s else None
242
 
 
246
  for k, v in entities.items():
247
  state.slots[k] = v
248
 
249
+ # Explicit human-agent request or too many turns → escalate
250
  if intent == "human_agent" or state.turn_count > 12:
251
  state.current_state = "escalate_virtual"
252
  state.escalate_to_human = True
253
  return state
254
 
255
+ # Unknown intent handling: route to fallback, track consecutive count,
256
+ # auto-escalate if user keeps asking out-of-scope things.
257
+ if intent == "unknown":
258
+ state.consecutive_unknowns += 1
259
+ if state.consecutive_unknowns >= MAX_CONSECUTIVE_UNKNOWNS:
260
+ state.current_state = "escalate_virtual"
261
+ state.escalate_to_human = True
262
+ return state
263
+ if state.current_state != "fallback":
264
+ state.state_before_fallback = state.current_state
265
+ state.current_state = "fallback"
266
+ return state
267
+
268
+ # Recognized intent → reset the unknown counter
269
+ state.consecutive_unknowns = 0
270
+
271
+ # If we're in fallback and the user now says something recognized,
272
+ # resume from the state we were in before falling back
273
+ if state.current_state == "fallback" and state.state_before_fallback:
274
+ resume_state = state.state_before_fallback
275
+ state.state_before_fallback = None
276
+ state.current_state = resume_state
277
+
278
  current = SCENARIOS[state.vertical]["states"].get(state.current_state)
279
  if not current:
280
  state.current_state = "greeting"
281
+ current = SCENARIOS[state.vertical]["states"]["greeting"]
282
 
283
+ # Try transition from the current state first
284
  next_state = current.get("transitions", {}).get(intent)
285
+
286
+ # If current state has no transition for this intent, but GREETING does,
287
+ # treat this as the user pivoting to a new top-level intent (e.g. midway
288
+ # through balance check they say "transfer money" instead). Restart flow.
289
+ if not next_state:
290
+ greeting = SCENARIOS[state.vertical]["states"]["greeting"]
291
+ pivot_state = greeting.get("transitions", {}).get(intent)
292
+ if pivot_state:
293
+ state.slots = {} # Reset slots when starting a new flow
294
+ state.state_before_fallback = None
295
+ next_state = pivot_state
296
+
297
  if next_state:
298
  state.current_state = next_state
299
  target = SCENARIOS[state.vertical]["states"].get(next_state, {})
300
  if target.get("escalate"):
301
  state.escalate_to_human = True
302
+ else:
303
+ # Intent recognized but neither current state nor greeting has a
304
+ # transition for it. Route to fallback.
305
+ state.consecutive_unknowns += 1
306
+ if state.consecutive_unknowns >= MAX_CONSECUTIVE_UNKNOWNS:
307
+ state.current_state = "escalate_virtual"
308
+ state.escalate_to_human = True
309
+ return state
310
+ if state.current_state != "fallback":
311
+ state.state_before_fallback = state.current_state
312
+ state.current_state = "fallback"
313
 
314
+ return state