Mr-Help commited on
Commit
3ecfbbf
·
verified ·
1 Parent(s): 6510c33

Update knowledge/message_understanding.py

Browse files
Files changed (1) hide show
  1. knowledge/message_understanding.py +258 -219
knowledge/message_understanding.py CHANGED
@@ -1,5 +1,7 @@
1
  from typing import Optional
2
 
 
 
3
 
4
  def normalize_text(text: str) -> str:
5
  return (text or "").strip().lower()
@@ -197,140 +199,229 @@ def detect_payment_method(text: str) -> Optional[str]:
197
  return None
198
 
199
 
200
- def classify_message(state: str, text: str, flow_data: dict | None = None):
201
- """
202
- Returns a structured classification:
203
- {
204
- "kind": "direct_answer" | "state_switch" | "topic_switch" | "unclear",
205
- "value": str | None,
206
- "confidence": float,
207
- "entities": dict
208
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
209
  """
210
  flow_data = flow_data or {}
211
  t = normalize_text(text)
212
 
213
  # ===== Global topic switches =====
214
  if wants_restart(t):
215
- return {
216
- "kind": "topic_switch",
217
- "value": "restart",
218
- "confidence": 0.99,
219
- "entities": {}
220
- }
221
 
222
  if wants_new_topic(t):
223
- return {
224
- "kind": "topic_switch",
225
- "value": "new_topic",
226
- "confidence": 0.95,
227
- "entities": {}
228
- }
229
 
230
  if is_complaint(t):
231
- return {
232
- "kind": "topic_switch",
233
- "value": "complaint",
234
- "confidence": 0.98,
235
- "entities": {}
236
- }
237
 
238
  if wants_direct_support(t):
239
- return {
240
- "kind": "topic_switch",
241
- "value": "direct_support",
242
- "confidence": 0.95,
243
- "entities": {}
244
- }
245
 
246
  if wants_courses_info(t):
247
- return {
248
- "kind": "topic_switch",
249
- "value": "courses_info",
250
- "confidence": 0.90,
251
- "entities": {}
252
- }
253
 
254
  if is_children(t):
255
- return {
256
- "kind": "topic_switch",
257
- "value": "children_courses",
258
- "confidence": 0.88,
259
- "entities": {"audience": "children"}
260
- }
261
 
262
  if is_adults(t):
263
- return {
264
- "kind": "topic_switch",
265
- "value": "adults_courses",
266
- "confidence": 0.88,
267
- "entities": {"audience": "adults"}
268
- }
269
 
270
  if is_new_student(t):
271
- return {
272
- "kind": "topic_switch",
273
- "value": "new_student",
274
- "confidence": 0.90,
275
- "entities": {"customer_type": "new"}
276
- }
277
 
278
  if is_current_student(t):
279
- return {
280
- "kind": "topic_switch",
281
- "value": "current_student",
282
- "confidence": 0.90,
283
- "entities": {"customer_type": "current"}
284
- }
285
 
286
  # ===== State-specific understanding =====
287
  if state == "WAITING_USER_TYPE":
288
  if is_new_student(t):
289
- return {
290
- "kind": "direct_answer",
291
- "value": "new_student",
292
- "confidence": 0.95,
293
- "entities": {"customer_type": "new"}
294
- }
295
  if is_current_student(t):
296
- return {
297
- "kind": "direct_answer",
298
- "value": "current_student",
299
- "confidence": 0.95,
300
- "entities": {"customer_type": "current"}
301
- }
302
 
303
  if state == "WAITING_AUDIENCE":
304
  if is_adults(t):
305
- return {
306
- "kind": "direct_answer",
307
- "value": "adults",
308
- "confidence": 0.95,
309
- "entities": {"audience": "adults"}
310
- }
311
  if is_children(t):
312
- return {
313
- "kind": "direct_answer",
314
- "value": "children",
315
- "confidence": 0.95,
316
- "entities": {"audience": "children"}
317
- }
318
 
319
  if state == "WAITING_PRIOR_STUDY":
320
  if is_yes(t):
321
- return {
322
- "kind": "direct_answer",
323
- "value": "prior_study_yes",
324
- "confidence": 0.96,
325
- "entities": {"prior_study": True}
326
- }
327
  if is_no(t):
328
- return {
329
- "kind": "direct_answer",
330
- "value": "prior_study_no",
331
- "confidence": 0.96,
332
- "entities": {"prior_study": False}
333
- }
334
 
335
  if state in [
336
  "WAITING_BEGINNER_SCHEDULE_CHOICE",
@@ -338,171 +429,119 @@ def classify_message(state: str, text: str, flow_data: dict | None = None):
338
  "WAITING_PLACEMENT_TEST_CONFIRMATION",
339
  ]:
340
  if asks_about_prior_study_case(t):
341
- return {
342
- "kind": "state_switch",
343
- "value": "switch_to_prior_study_true",
344
- "confidence": 0.92,
345
- "entities": {"prior_study": True}
346
- }
347
 
348
  if asks_about_beginner_case(t):
349
- return {
350
- "kind": "state_switch",
351
- "value": "switch_to_prior_study_false",
352
- "confidence": 0.92,
353
- "entities": {"prior_study": False}
354
- }
355
 
356
  if state == "WAITING_BEGINNER_SCHEDULE_CHOICE":
357
  if contains_any(t, ["تم", "اخترت", "اختارت", "جاهز", "جاهزة"]):
358
- return {
359
- "kind": "direct_answer",
360
- "value": "confirm_schedule_reviewed",
361
- "confidence": 0.92,
362
- "entities": {}
363
- }
364
 
365
  if contains_any(t, ["عايز احجز", "عايزة احجز", "احجز", "حجز", "اشترك", "اشتراك"]):
366
- return {
367
- "kind": "direct_answer",
368
- "value": "proceed_booking",
369
- "confidence": 0.90,
370
- "entities": {}
371
- }
372
 
373
  if is_support_request(t):
374
- return {
375
- "kind": "state_switch",
376
- "value": "support_needed",
377
- "confidence": 0.88,
378
- "entities": {}
379
- }
380
 
381
  if state == "WAITING_PDF_102_CONFIRMATION":
382
  if contains_any(t, ["تم", "خلصت", "قريت", "اطلعت", "جاهز", "جاهزة"]):
383
- return {
384
- "kind": "direct_answer",
385
- "value": "confirm_pdf_reviewed",
386
- "confidence": 0.92,
387
- "entities": {}
388
- }
389
 
390
  if is_support_request(t):
391
- return {
392
- "kind": "state_switch",
393
- "value": "support_needed",
394
- "confidence": 0.88,
395
- "entities": {}
396
- }
397
 
398
  if state == "WAITING_PLACEMENT_TEST_CONFIRMATION":
399
  if contains_any(t, ["تم", "اخترت", "اختارت", "جاهز", "جاهزة"]):
400
- return {
401
- "kind": "direct_answer",
402
- "value": "confirm_placement_test_reviewed",
403
- "confidence": 0.92,
404
- "entities": {}
405
- }
406
 
407
  if is_support_request(t):
408
- return {
409
- "kind": "state_switch",
410
- "value": "support_needed",
411
- "confidence": 0.88,
412
- "entities": {}
413
- }
414
 
415
  if state == "WAITING_CURRENT_STUDENT_ACTION":
416
  if is_support_request(t):
417
- return {
418
- "kind": "direct_answer",
419
- "value": "current_student_support",
420
- "confidence": 0.92,
421
- "entities": {}
422
- }
423
 
424
  if is_next_level_booking(t):
425
- return {
426
- "kind": "direct_answer",
427
- "value": "current_student_next_level",
428
- "confidence": 0.92,
429
- "entities": {}
430
- }
431
 
432
  if state == "WAITING_SUPPORT_QUESTION":
433
  if t:
434
- return {
435
- "kind": "direct_answer",
436
- "value": "support_question_text",
437
- "confidence": 0.85,
438
- "entities": {"support_question": text}
439
- }
440
 
441
  if state == "WAITING_LEVEL_SELECTION":
442
  level = detect_level(t)
443
  if level:
444
- return {
445
- "kind": "direct_answer",
446
- "value": "level_selected",
447
- "confidence": 0.95,
448
- "entities": {"selected_level": level}
449
- }
450
 
451
  if is_support_request(t) or contains_any(t, ["مش عارف", "مش متأكد", "مش متاكدة"]):
452
- return {
453
- "kind": "state_switch",
454
- "value": "support_needed",
455
- "confidence": 0.85,
456
- "entities": {}
457
- }
458
 
459
  if state == "WAITING_PAYMENT_METHOD":
460
  payment_method = detect_payment_method(t)
461
  if payment_method:
462
- return {
463
- "kind": "direct_answer",
464
- "value": "payment_method_selected",
465
- "confidence": 0.95,
466
- "entities": {"payment_method": payment_method}
467
- }
468
 
469
  if is_support_request(t):
470
- return {
471
- "kind": "state_switch",
472
- "value": "support_needed",
473
- "confidence": 0.85,
474
- "entities": {}
475
- }
476
 
477
  if state == "WAITING_COMPLAINT_FORM":
478
  if contains_any(t, ["تم", "خلصت", "سجلت", "قدمت", "بعت"]):
479
- return {
480
- "kind": "direct_answer",
481
- "value": "complaint_form_submitted",
482
- "confidence": 0.90,
483
- "entities": {}
484
- }
485
 
486
  if state == "HANDOFF_DONE":
487
  if contains_any(t, ["شكرا", "متشكر", "تسلم", "ميرسي"]):
488
- return {
489
- "kind": "direct_answer",
490
- "value": "thanks",
491
- "confidence": 0.95,
492
- "entities": {}
493
- }
494
 
495
  if is_support_request(t):
496
- return {
497
- "kind": "topic_switch",
498
- "value": "direct_support",
499
- "confidence": 0.90,
500
- "entities": {}
501
- }
502
-
503
- return {
504
- "kind": "unclear",
505
- "value": None,
506
- "confidence": 0.30,
507
- "entities": {}
508
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  from typing import Optional
2
 
3
+ from services.intent_classifier_client import classify_message_with_model
4
+
5
 
6
  def normalize_text(text: str) -> str:
7
  return (text or "").strip().lower()
 
199
  return None
200
 
201
 
202
+ def _make_result(kind: str, value: Optional[str], confidence: float, entities: Optional[dict] = None, source: str = "rules", raw_model_output: Optional[str] = None):
203
+ result = {
204
+ "kind": kind,
205
+ "value": value,
206
+ "confidence": confidence,
207
+ "entities": entities or {},
208
+ "source": source,
 
209
  }
210
+ if raw_model_output is not None:
211
+ result["raw_model_output"] = raw_model_output
212
+ return result
213
+
214
+
215
+ def _map_model_intent_to_result(state: str, text: str, model_intent: str, raw_output: str = ""):
216
+ # ===== Topic switch labels =====
217
+ if model_intent == "restart":
218
+ return _make_result("topic_switch", "restart", 0.90, {}, "model", raw_output)
219
+
220
+ if model_intent == "new_topic":
221
+ return _make_result("topic_switch", "new_topic", 0.88, {}, "model", raw_output)
222
+
223
+ if model_intent == "complaint":
224
+ return _make_result("topic_switch", "complaint", 0.92, {}, "model", raw_output)
225
+
226
+ if model_intent == "direct_support":
227
+ return _make_result("topic_switch", "direct_support", 0.90, {}, "model", raw_output)
228
+
229
+ if model_intent == "courses_info":
230
+ return _make_result("topic_switch", "courses_info", 0.88, {}, "model", raw_output)
231
+
232
+ if model_intent == "children_courses":
233
+ return _make_result(
234
+ "topic_switch",
235
+ "children_courses",
236
+ 0.88,
237
+ {"audience": "children"},
238
+ "model",
239
+ raw_output
240
+ )
241
+
242
+ if model_intent == "adults_courses":
243
+ return _make_result(
244
+ "topic_switch",
245
+ "adults_courses",
246
+ 0.88,
247
+ {"audience": "adults"},
248
+ "model",
249
+ raw_output
250
+ )
251
+
252
+ if model_intent == "new_student":
253
+ target_kind = "direct_answer" if state == "WAITING_USER_TYPE" else "topic_switch"
254
+ return _make_result(
255
+ target_kind,
256
+ "new_student",
257
+ 0.90,
258
+ {"customer_type": "new"},
259
+ "model",
260
+ raw_output
261
+ )
262
+
263
+ if model_intent == "current_student":
264
+ target_kind = "direct_answer" if state == "WAITING_USER_TYPE" else "topic_switch"
265
+ return _make_result(
266
+ target_kind,
267
+ "current_student",
268
+ 0.90,
269
+ {"customer_type": "current"},
270
+ "model",
271
+ raw_output
272
+ )
273
+
274
+ # ===== Direct answers =====
275
+ if model_intent == "adults":
276
+ return _make_result("direct_answer", "adults", 0.93, {"audience": "adults"}, "model", raw_output)
277
+
278
+ if model_intent == "children":
279
+ return _make_result("direct_answer", "children", 0.93, {"audience": "children"}, "model", raw_output)
280
+
281
+ if model_intent == "prior_study_yes":
282
+ return _make_result("direct_answer", "prior_study_yes", 0.94, {"prior_study": True}, "model", raw_output)
283
+
284
+ if model_intent == "prior_study_no":
285
+ return _make_result("direct_answer", "prior_study_no", 0.94, {"prior_study": False}, "model", raw_output)
286
+
287
+ if model_intent == "confirm_schedule_reviewed":
288
+ return _make_result("direct_answer", "confirm_schedule_reviewed", 0.90, {}, "model", raw_output)
289
+
290
+ if model_intent == "proceed_booking":
291
+ return _make_result("direct_answer", "proceed_booking", 0.90, {}, "model", raw_output)
292
+
293
+ if model_intent == "confirm_pdf_reviewed":
294
+ return _make_result("direct_answer", "confirm_pdf_reviewed", 0.90, {}, "model", raw_output)
295
+
296
+ if model_intent == "confirm_placement_test_reviewed":
297
+ return _make_result("direct_answer", "confirm_placement_test_reviewed", 0.90, {}, "model", raw_output)
298
+
299
+ if model_intent == "current_student_support":
300
+ return _make_result("direct_answer", "current_student_support", 0.90, {}, "model", raw_output)
301
+
302
+ if model_intent == "current_student_next_level":
303
+ return _make_result("direct_answer", "current_student_next_level", 0.90, {}, "model", raw_output)
304
+
305
+ if model_intent == "support_question_text":
306
+ return _make_result(
307
+ "direct_answer",
308
+ "support_question_text",
309
+ 0.85,
310
+ {"support_question": text},
311
+ "model",
312
+ raw_output
313
+ )
314
+
315
+ if model_intent == "level_selected":
316
+ level = detect_level(text)
317
+ if level:
318
+ return _make_result(
319
+ "direct_answer",
320
+ "level_selected",
321
+ 0.92,
322
+ {"selected_level": level},
323
+ "model",
324
+ raw_output
325
+ )
326
+
327
+ if model_intent == "payment_method_selected":
328
+ payment_method = detect_payment_method(text)
329
+ if payment_method:
330
+ return _make_result(
331
+ "direct_answer",
332
+ "payment_method_selected",
333
+ 0.92,
334
+ {"payment_method": payment_method},
335
+ "model",
336
+ raw_output
337
+ )
338
+
339
+ if model_intent == "complaint_form_submitted":
340
+ return _make_result("direct_answer", "complaint_form_submitted", 0.90, {}, "model", raw_output)
341
+
342
+ if model_intent == "thanks":
343
+ return _make_result("direct_answer", "thanks", 0.95, {}, "model", raw_output)
344
+
345
+ # ===== State switches =====
346
+ if model_intent == "switch_to_prior_study_true":
347
+ return _make_result(
348
+ "state_switch",
349
+ "switch_to_prior_study_true",
350
+ 0.90,
351
+ {"prior_study": True},
352
+ "model",
353
+ raw_output
354
+ )
355
+
356
+ if model_intent == "switch_to_prior_study_false":
357
+ return _make_result(
358
+ "state_switch",
359
+ "switch_to_prior_study_false",
360
+ 0.90,
361
+ {"prior_study": False},
362
+ "model",
363
+ raw_output
364
+ )
365
+
366
+ if model_intent == "support_needed":
367
+ return _make_result("state_switch", "support_needed", 0.86, {}, "model", raw_output)
368
+
369
+ return None
370
+
371
+
372
+ def _fallback_rule_based_classification(state: str, text: str, flow_data: dict | None = None):
373
+ """
374
+ ده اللوجيك القديم كما هو تقريبًا، عشان ما نكسرش أي حاجة.
375
  """
376
  flow_data = flow_data or {}
377
  t = normalize_text(text)
378
 
379
  # ===== Global topic switches =====
380
  if wants_restart(t):
381
+ return _make_result("topic_switch", "restart", 0.99, {})
 
 
 
 
 
382
 
383
  if wants_new_topic(t):
384
+ return _make_result("topic_switch", "new_topic", 0.95, {})
 
 
 
 
 
385
 
386
  if is_complaint(t):
387
+ return _make_result("topic_switch", "complaint", 0.98, {})
 
 
 
 
 
388
 
389
  if wants_direct_support(t):
390
+ return _make_result("topic_switch", "direct_support", 0.95, {})
 
 
 
 
 
391
 
392
  if wants_courses_info(t):
393
+ return _make_result("topic_switch", "courses_info", 0.90, {})
 
 
 
 
 
394
 
395
  if is_children(t):
396
+ return _make_result("topic_switch", "children_courses", 0.88, {"audience": "children"})
 
 
 
 
 
397
 
398
  if is_adults(t):
399
+ return _make_result("topic_switch", "adults_courses", 0.88, {"audience": "adults"})
 
 
 
 
 
400
 
401
  if is_new_student(t):
402
+ return _make_result("topic_switch", "new_student", 0.90, {"customer_type": "new"})
 
 
 
 
 
403
 
404
  if is_current_student(t):
405
+ return _make_result("topic_switch", "current_student", 0.90, {"customer_type": "current"})
 
 
 
 
 
406
 
407
  # ===== State-specific understanding =====
408
  if state == "WAITING_USER_TYPE":
409
  if is_new_student(t):
410
+ return _make_result("direct_answer", "new_student", 0.95, {"customer_type": "new"})
 
 
 
 
 
411
  if is_current_student(t):
412
+ return _make_result("direct_answer", "current_student", 0.95, {"customer_type": "current"})
 
 
 
 
 
413
 
414
  if state == "WAITING_AUDIENCE":
415
  if is_adults(t):
416
+ return _make_result("direct_answer", "adults", 0.95, {"audience": "adults"})
 
 
 
 
 
417
  if is_children(t):
418
+ return _make_result("direct_answer", "children", 0.95, {"audience": "children"})
 
 
 
 
 
419
 
420
  if state == "WAITING_PRIOR_STUDY":
421
  if is_yes(t):
422
+ return _make_result("direct_answer", "prior_study_yes", 0.96, {"prior_study": True})
 
 
 
 
 
423
  if is_no(t):
424
+ return _make_result("direct_answer", "prior_study_no", 0.96, {"prior_study": False})
 
 
 
 
 
425
 
426
  if state in [
427
  "WAITING_BEGINNER_SCHEDULE_CHOICE",
 
429
  "WAITING_PLACEMENT_TEST_CONFIRMATION",
430
  ]:
431
  if asks_about_prior_study_case(t):
432
+ return _make_result("state_switch", "switch_to_prior_study_true", 0.92, {"prior_study": True})
 
 
 
 
 
433
 
434
  if asks_about_beginner_case(t):
435
+ return _make_result("state_switch", "switch_to_prior_study_false", 0.92, {"prior_study": False})
 
 
 
 
 
436
 
437
  if state == "WAITING_BEGINNER_SCHEDULE_CHOICE":
438
  if contains_any(t, ["تم", "اخترت", "اختارت", "جاهز", "جاهزة"]):
439
+ return _make_result("direct_answer", "confirm_schedule_reviewed", 0.92, {})
 
 
 
 
 
440
 
441
  if contains_any(t, ["عايز احجز", "عايزة احجز", "احجز", "حجز", "اشترك", "اشتراك"]):
442
+ return _make_result("direct_answer", "proceed_booking", 0.90, {})
 
 
 
 
 
443
 
444
  if is_support_request(t):
445
+ return _make_result("state_switch", "support_needed", 0.88, {})
 
 
 
 
 
446
 
447
  if state == "WAITING_PDF_102_CONFIRMATION":
448
  if contains_any(t, ["تم", "خلصت", "قريت", "اطلعت", "جاهز", "جاهزة"]):
449
+ return _make_result("direct_answer", "confirm_pdf_reviewed", 0.92, {})
 
 
 
 
 
450
 
451
  if is_support_request(t):
452
+ return _make_result("state_switch", "support_needed", 0.88, {})
 
 
 
 
 
453
 
454
  if state == "WAITING_PLACEMENT_TEST_CONFIRMATION":
455
  if contains_any(t, ["تم", "اخترت", "اختارت", "جاهز", "جاهزة"]):
456
+ return _make_result("direct_answer", "confirm_placement_test_reviewed", 0.92, {})
 
 
 
 
 
457
 
458
  if is_support_request(t):
459
+ return _make_result("state_switch", "support_needed", 0.88, {})
 
 
 
 
 
460
 
461
  if state == "WAITING_CURRENT_STUDENT_ACTION":
462
  if is_support_request(t):
463
+ return _make_result("direct_answer", "current_student_support", 0.92, {})
 
 
 
 
 
464
 
465
  if is_next_level_booking(t):
466
+ return _make_result("direct_answer", "current_student_next_level", 0.92, {})
 
 
 
 
 
467
 
468
  if state == "WAITING_SUPPORT_QUESTION":
469
  if t:
470
+ return _make_result(
471
+ "direct_answer",
472
+ "support_question_text",
473
+ 0.85,
474
+ {"support_question": text}
475
+ )
476
 
477
  if state == "WAITING_LEVEL_SELECTION":
478
  level = detect_level(t)
479
  if level:
480
+ return _make_result("direct_answer", "level_selected", 0.95, {"selected_level": level})
 
 
 
 
 
481
 
482
  if is_support_request(t) or contains_any(t, ["مش عارف", "مش متأكد", "مش متاكدة"]):
483
+ return _make_result("state_switch", "support_needed", 0.85, {})
 
 
 
 
 
484
 
485
  if state == "WAITING_PAYMENT_METHOD":
486
  payment_method = detect_payment_method(t)
487
  if payment_method:
488
+ return _make_result(
489
+ "direct_answer",
490
+ "payment_method_selected",
491
+ 0.95,
492
+ {"payment_method": payment_method}
493
+ )
494
 
495
  if is_support_request(t):
496
+ return _make_result("state_switch", "support_needed", 0.85, {})
 
 
 
 
 
497
 
498
  if state == "WAITING_COMPLAINT_FORM":
499
  if contains_any(t, ["تم", "خلصت", "سجلت", "قدمت", "بعت"]):
500
+ return _make_result("direct_answer", "complaint_form_submitted", 0.90, {})
 
 
 
 
 
501
 
502
  if state == "HANDOFF_DONE":
503
  if contains_any(t, ["شكرا", "متشكر", "تسلم", "ميرسي"]):
504
+ return _make_result("direct_answer", "thanks", 0.95, {})
 
 
 
 
 
505
 
506
  if is_support_request(t):
507
+ return _make_result("topic_switch", "direct_support", 0.90, {})
508
+
509
+ return _make_result("unclear", None, 0.30, {})
510
+
511
+
512
+ def classify_message(state: str, text: str, flow_data: dict | None = None):
513
+ """
514
+ Returns structured classification:
515
+ {
516
+ "kind": "direct_answer" | "state_switch" | "topic_switch" | "unclear",
517
+ "value": str | None,
518
+ "confidence": float,
519
+ "entities": dict,
520
+ "source": "model" | "rules"
521
+ }
522
+ """
523
+ flow_data = flow_data or {}
524
+
525
+ # 1) Try model classifier first
526
+ try:
527
+ model_res = classify_message_with_model(
528
+ user_message=text,
529
+ state=state,
530
+ flow_data=flow_data,
531
+ )
532
+
533
+ if model_res and model_res.get("intent"):
534
+ mapped = _map_model_intent_to_result(
535
+ state=state,
536
+ text=text,
537
+ model_intent=model_res["intent"],
538
+ raw_output=model_res.get("raw_output", "")
539
+ )
540
+ if mapped:
541
+ return mapped
542
+
543
+ except Exception as e:
544
+ print(f"[message_understanding] model classifier failed: {repr(e)}")
545
+
546
+ # 2) Fallback to existing rule-based logic
547
+ return _fallback_rule_based_classification(state, text, flow_data)