Spaces:
Sleeping
Sleeping
Update main.py
Browse files
main.py
CHANGED
|
@@ -1345,6 +1345,45 @@ class IrisReportEngine:
|
|
| 1345 |
emit_kpi_debug(self.profile_id, "briefing_done", snapshot["meta"])
|
| 1346 |
return json_safe(snapshot)
|
| 1347 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1348 |
# ------------------------- helpers (outside class) -------------------------
|
| 1349 |
|
| 1350 |
def rfm_to_list(df: pd.DataFrame) -> List[Dict[str, Any]]:
|
|
|
|
| 1345 |
emit_kpi_debug(self.profile_id, "briefing_done", snapshot["meta"])
|
| 1346 |
return json_safe(snapshot)
|
| 1347 |
|
| 1348 |
+
def synthesize_fallback_response(self, briefing: dict, user_question: str) -> str:
|
| 1349 |
+
"""
|
| 1350 |
+
LLM is narration-only. Do NOT invent or recompute numbers here.
|
| 1351 |
+
Use the already-deterministic 'briefing' dict as the single source of truth.
|
| 1352 |
+
Safe for PandasAI exception fallback.
|
| 1353 |
+
"""
|
| 1354 |
+
try:
|
| 1355 |
+
tz = TZ
|
| 1356 |
+
prompt = (
|
| 1357 |
+
"You are Iris, a concise business analyst.\n"
|
| 1358 |
+
"IMPORTANT RULES:\n"
|
| 1359 |
+
"• DO NOT invent numbers. ONLY use values present in the Business Data JSON.\n"
|
| 1360 |
+
"• If a metric is missing, say 'N/A' or 'no data for this period'.\n"
|
| 1361 |
+
"• Harare timezone is the reference for dates/times.\n"
|
| 1362 |
+
"• Keep it brief: headings + bullets; no tables unless clearly helpful.\n"
|
| 1363 |
+
"• If the user asked something specific (e.g., 'top 5 products'), answer directly from the JSON.\n"
|
| 1364 |
+
"• Otherwise, provide a short business briefing (performance, products, timing, customers, branches).\n"
|
| 1365 |
+
"• Never expose internal keys; paraphrase labels.\n"
|
| 1366 |
+
f"User Question: {json.dumps(user_question)}\n\n"
|
| 1367 |
+
"Business Data (authoritative; JSON):\n"
|
| 1368 |
+
f"{json.dumps(json_safe(briefing), ensure_ascii=False)}\n"
|
| 1369 |
+
)
|
| 1370 |
+
|
| 1371 |
+
resp = self.llm.invoke(prompt)
|
| 1372 |
+
# ChatGoogleGenerativeAI returns an object with .content
|
| 1373 |
+
text = getattr(resp, "content", None) or str(resp)
|
| 1374 |
+
# Final safety scrub (remove accidental code fences / tracebacks)
|
| 1375 |
+
return sanitize_answer(text)
|
| 1376 |
+
|
| 1377 |
+
except Exception as e:
|
| 1378 |
+
# Absolute last resort: dump a compact JSON view so the UI shows *something*
|
| 1379 |
+
fallback = {
|
| 1380 |
+
"note": "Narrative fallback failed; returning raw snapshot.",
|
| 1381 |
+
"error": str(e)[:200],
|
| 1382 |
+
"snapshot_keys": list(briefing.keys()) if isinstance(briefing, dict) else "N/A"
|
| 1383 |
+
}
|
| 1384 |
+
return "### Business Snapshot\n\n```\n" + json.dumps(json_safe(briefing), indent=2) + "\n```\n\n" + \
|
| 1385 |
+
"```\n" + json.dumps(fallback, indent=2) + "\n```"
|
| 1386 |
+
|
| 1387 |
# ------------------------- helpers (outside class) -------------------------
|
| 1388 |
|
| 1389 |
def rfm_to_list(df: pd.DataFrame) -> List[Dict[str, Any]]:
|