From c73bedd7d08647a62e8c5b3f9d19f709ca294cb5 Mon Sep 17 00:00:00 2001 From: OpenSquared Date: Mon, 29 Jun 2026 23:09:49 +0200 Subject: [PATCH] debug: expose full inputs/outputs in Recalculer response for diagnosis _run_auto_analysis now returns a rich dict {ok, inputs, node_values, actual_moves, error} instead of bool. The refresh endpoint captures and forwards: - inputs: what values were fed to evaluate_graph - node_keys: which nodes were computed in prediction - actual_moves: actual price pips fetched - run_error: exception message if it failed Frontend shows all of this after clicking Recalculer so the root cause is visible. Co-Authored-By: Claude Sonnet 4.6 --- backend/routers/causal_lab.py | 30 ++++++++++++++-------- frontend/src/pages/InstrumentDashboard.tsx | 17 +++++++----- 2 files changed, 29 insertions(+), 18 deletions(-) diff --git a/backend/routers/causal_lab.py b/backend/routers/causal_lab.py index ba235f6..c3044c0 100644 --- a/backend/routers/causal_lab.py +++ b/backend/routers/causal_lab.py @@ -1127,11 +1127,10 @@ def _build_graph_json_from_spec(spec: dict) -> dict: } -def _run_auto_analysis(event: dict, template_id: int) -> bool: +def _run_auto_analysis(event: dict, template_id: int) -> dict: """ Causal analysis for the auto_template pipeline. - Builds inputs from event fields, evaluates the graph, fetches actual prices, - and upserts into causal_event_analyses so the instrument frise can score it. + Returns {"ok": bool, "inputs": dict, "node_values": dict, "actual_moves": dict, "error": str}. """ try: from services.database import get_conn @@ -1142,7 +1141,7 @@ def _run_auto_analysis(event: dict, template_id: int) -> bool: if not tmpl: conn.close() logger.warning(f"[auto_analysis] template #{template_id} introuvable") - return False + return {"ok": False, "error": f"template #{template_id} introuvable"} graph = tmpl["graph_json"] inputs = {} @@ -1310,11 +1309,17 @@ def _run_auto_analysis(event: dict, template_id: int) -> bool: conn.commit() conn.close() logger.info(f"[auto_analysis] upserted event #{event_id}, tmpl #{template_id}, actual_moves={actual_moves}") - return True + return { + "ok": True, + "inputs": inputs, + "node_values": node_values, + "actual_moves": actual_moves, + "instruments": instruments, + } except Exception as e: - logger.error(f"[auto_analysis] event #{event.get('id')}: {e}") - return False + logger.error(f"[auto_analysis] event #{event.get('id')}: {e}", exc_info=True) + return {"ok": False, "error": str(e)} def auto_assign_template(event_id: int) -> dict: @@ -1481,13 +1486,16 @@ def refresh_auto_analyses(): detail["actual_value"] = ev.get("actual_value") detail["expected_value"] = ev.get("expected_value") detail["start_date"] = ev.get("start_date") - ok = _run_auto_analysis(ev, row["template_id"]) - detail["result"] = "ok" if ok else "failed" - if ok: + res = _run_auto_analysis(ev, row["template_id"]) + detail["result"] = "ok" if res["ok"] else "failed" + detail["inputs"] = res.get("inputs", {}) + detail["node_keys"] = list(res.get("node_values", {}).keys()) + detail["actual_moves"] = res.get("actual_moves", {}) + detail["run_error"] = res.get("error") + if res["ok"]: refreshed += 1 else: failed += 1 - detail["result"] = "run_returned_false" except Exception as _e: logger.warning(f"[refresh_auto_analyses] cea#{row['cea_id']}: {_e}") detail["result"] = f"exception: {_e}" diff --git a/frontend/src/pages/InstrumentDashboard.tsx b/frontend/src/pages/InstrumentDashboard.tsx index 9416a15..20212d9 100644 --- a/frontend/src/pages/InstrumentDashboard.tsx +++ b/frontend/src/pages/InstrumentDashboard.tsx @@ -1152,13 +1152,16 @@ function ExplanationScore({ : Refresh: {debugInfo.refreshed} ok / {debugInfo.failed} err / {debugInfo.total} total } {debugInfo.details?.map((d: any, i: number) => ( -
- #{d.event_id} {(d.event_name ?? '').slice(0, 22)} → {d.result} - {d.surprise_pct != null - ? surp:{Number(d.surprise_pct).toFixed(1)}% - : d.actual_value != null - ? act:{d.actual_value} exp:{d.expected_value ?? '?'} - : surp=null} +
+ {d.result} + #{d.event_id} {(d.event_name ?? '').slice(0, 20)} + {d.run_error && err:{d.run_error.slice(0, 40)}} +
+ surp:{d.surprise_pct != null ? Number(d.surprise_pct).toFixed(1)+'%' : `null (act=${d.actual_value} exp=${d.expected_value})`} + {' | '}inputs:{JSON.stringify(d.inputs ?? {})} + {' | '}pred_keys:[{(d.node_keys ?? []).join(',')}] + {' | '}actual:{JSON.stringify(d.actual_moves ?? {})} +
))}