mortadhabbb commited on
Commit
6d0e73e
·
1 Parent(s): 5ca3216

Prioritize explicit player payment service routing

Browse files
apps/chat/services/backend_orchestration.py CHANGED
@@ -31,6 +31,17 @@ REQUIRED_FIELDS = {
31
  }
32
 
33
 
 
 
 
 
 
 
 
 
 
 
 
34
  def decide_chatbot_response(message: str, context: Dict[str, Any], bot_payload: Dict[str, Any]) -> Dict[str, Any]:
35
  msg = (message or "").strip()
36
  allowed = set(context.get("allowed_service_ids") or [])
@@ -50,7 +61,7 @@ def decide_chatbot_response(message: str, context: Dict[str, Any], bot_payload:
50
  return {
51
  "type": "CLARIFICATION_REQUIRED",
52
  "service_id": service_id,
53
- "confidence": float(request.get("confidence") or bot_payload.get("score") or 0.8),
54
  "missing_fields": missing,
55
  "question": "Please provide " + ", ".join(missing) + " so I can understand the request.",
56
  "safety": {"safe": True, "risk_level": "LOW"},
@@ -58,7 +69,7 @@ def decide_chatbot_response(message: str, context: Dict[str, Any], bot_payload:
58
  return {
59
  "type": "SERVICE_REQUEST",
60
  "service_id": service_id,
61
- "confidence": float(request.get("confidence") or bot_payload.get("score") or 0.8),
62
  "missing_fields": [],
63
  "extracted_data": extracted,
64
  "user_message": "The service intent and parameters were detected. The backend must authorize and execute it.",
@@ -91,7 +102,7 @@ def decide_chatbot_response(message: str, context: Dict[str, Any], bot_payload:
91
  "type": "KNOWLEDGE_QA",
92
  "answer": bot_payload.get("response") or bot_payload.get("answer") or "I could not find a confident answer.",
93
  "response": bot_payload.get("response") or bot_payload.get("answer") or "I could not find a confident answer.",
94
- "confidence": float(bot_payload.get("score") or bot_payload.get("confidence") or 0.0),
95
  "source": bot_payload.get("source") or "data.csv",
96
  "matched_question": bot_payload.get("matched_question"),
97
  "tenant_scope": context.get("tenant_scope") or "GLOBAL",
@@ -139,4 +150,4 @@ def finalize_service_response(payload: Dict[str, Any]) -> Dict[str, Any]:
139
  message = result.get("safe_summary") or "I need your confirmation before completing this action."
140
  else:
141
  message = result.get("safe_summary") or result.get("error") or f"I could not complete the {service_id.replace('_', ' ')} service."
142
- return {"type": "SERVICE_FINAL_RESPONSE", "message": str(message), "response": str(message), "service_id": service_id, "status": status}
 
31
  }
32
 
33
 
34
+ def _public_confidence(value: Any, default: float = 0.0) -> float:
35
+ """Normalize internal percentage/ranking scores to the public 0..1 contract."""
36
+ try:
37
+ score = float(value)
38
+ except (TypeError, ValueError):
39
+ score = default
40
+ if score > 1.0:
41
+ score /= 100.0
42
+ return round(max(0.0, min(score, 1.0)), 4)
43
+
44
+
45
  def decide_chatbot_response(message: str, context: Dict[str, Any], bot_payload: Dict[str, Any]) -> Dict[str, Any]:
46
  msg = (message or "").strip()
47
  allowed = set(context.get("allowed_service_ids") or [])
 
61
  return {
62
  "type": "CLARIFICATION_REQUIRED",
63
  "service_id": service_id,
64
+ "confidence": _public_confidence(request.get("confidence") or bot_payload.get("score"), 0.8),
65
  "missing_fields": missing,
66
  "question": "Please provide " + ", ".join(missing) + " so I can understand the request.",
67
  "safety": {"safe": True, "risk_level": "LOW"},
 
69
  return {
70
  "type": "SERVICE_REQUEST",
71
  "service_id": service_id,
72
+ "confidence": _public_confidence(request.get("confidence") or bot_payload.get("score"), 0.8),
73
  "missing_fields": [],
74
  "extracted_data": extracted,
75
  "user_message": "The service intent and parameters were detected. The backend must authorize and execute it.",
 
102
  "type": "KNOWLEDGE_QA",
103
  "answer": bot_payload.get("response") or bot_payload.get("answer") or "I could not find a confident answer.",
104
  "response": bot_payload.get("response") or bot_payload.get("answer") or "I could not find a confident answer.",
105
+ "confidence": _public_confidence(bot_payload.get("score") or bot_payload.get("confidence"), 0.0),
106
  "source": bot_payload.get("source") or "data.csv",
107
  "matched_question": bot_payload.get("matched_question"),
108
  "tenant_scope": context.get("tenant_scope") or "GLOBAL",
 
150
  message = result.get("safe_summary") or "I need your confirmation before completing this action."
151
  else:
152
  message = result.get("safe_summary") or result.get("error") or f"I could not complete the {service_id.replace('_', ' ')} service."
153
+ return {"type": "SERVICE_FINAL_RESPONSE", "message": str(message), "response": str(message), "service_id": service_id, "status": status}
apps/chat/services/service_detection.py CHANGED
@@ -618,6 +618,27 @@ class ServiceDetector:
618
  ' pending ',
619
  ' overdue ',
620
  ))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
621
  if wants_personal_payments:
622
  matches.append({
623
  'service_id': 'get-pending-payments' if wants_pending_payment_list else 'get-payment-status',
 
618
  ' pending ',
619
  ' overdue ',
620
  ))
621
+ wants_player_payment_data = (
622
+ any(term in padded for term in (' player ', ' footballer ', ' athlete '))
623
+ and any(term in padded for term in (
624
+ ' payment ',
625
+ ' payments ',
626
+ ' invoice ',
627
+ ' invoices ',
628
+ ' bill ',
629
+ ' bills ',
630
+ ' fee ',
631
+ ' fees ',
632
+ ))
633
+ )
634
+ if wants_player_payment_data:
635
+ matches.append({
636
+ 'service_id': 'get-player-payments',
637
+ 'confidence': 133.0,
638
+ 'source': 'rule_based_operation',
639
+ 'matched_example': 'payments for a player',
640
+ })
641
+
642
  if wants_personal_payments:
643
  matches.append({
644
  'service_id': 'get-pending-payments' if wants_pending_payment_list else 'get-payment-status',
apps/chat/tests/test_service_integration.py CHANGED
@@ -504,6 +504,43 @@ def test_mobile_service_prompts_route_before_kb():
504
  assert detected[0]["service_id"] == service_id
505
 
506
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
507
  def test_model_extraction_enriches_detected_service_parameters():
508
  """Detected services should be enriched by the trained model without replacing context IDs."""
509
  from apps.chat.services.service_detection import get_service_detector
 
504
  assert detected[0]["service_id"] == service_id
505
 
506
 
507
+ def test_explicit_player_payment_terms_beat_player_profile_match():
508
+ """Payment nouns must select payment routing even when a player name is present."""
509
+ from apps.chat.services.service_detection import get_service_detector
510
+
511
+ detector = get_service_detector()
512
+ detected = detector.detect_service_requests(
513
+ "show invoices for player John Smith from 2026-06-01 to 2026-06-30",
514
+ context={"user_id": 1, "role": "ADMIN", "academy_id": 32},
515
+ threshold=70.0,
516
+ )
517
+
518
+ assert detected
519
+ assert detected[0]["service_id"] == "get-player-payments"
520
+
521
+
522
+ def test_public_service_confidence_is_normalized():
523
+ """Internal router ranking scores must not leak outside the 0..1 API contract."""
524
+ from apps.chat.services.backend_orchestration import decide_chatbot_response
525
+
526
+ response = decide_chatbot_response(
527
+ "show player payments",
528
+ {},
529
+ {
530
+ "service_requests": [
531
+ {
532
+ "service_id": "get-player-payments",
533
+ "confidence": 133.0,
534
+ "parameters": {"player": "John Smith"},
535
+ }
536
+ ]
537
+ },
538
+ )
539
+
540
+ assert response["type"] == "SERVICE_REQUEST"
541
+ assert response["confidence"] == 1.0
542
+
543
+
544
  def test_model_extraction_enriches_detected_service_parameters():
545
  """Detected services should be enriched by the trained model without replacing context IDs."""
546
  from apps.chat.services.service_detection import get_service_detector