diff --git a/backend/routers/causal_lab.py b/backend/routers/causal_lab.py index d956407..8972ce7 100644 --- a/backend/routers/causal_lab.py +++ b/backend/routers/causal_lab.py @@ -257,7 +257,11 @@ def _gpt4o_recommend(event: dict, templates: list) -> dict: system_prompt = ( "You are a macro market analyst and causal graph specialist for the GeoOptions platform. " "Given a market event and a library of causal graph templates, recommend the most appropriate template " - "and suggest any coefficient adjustments. Respond with a JSON object only." + "and suggest any coefficient adjustments. Respond with a JSON object only.\n" + "IMPORTANT: Only recommend a template if it is structurally compatible with the event type " + "(same causal mechanism — e.g. do NOT recommend an ECB rate decision template for a sentiment survey). " + "If no template fits well (structural mismatch, confidence < 0.6), set template_id to null " + "and explain in 'notes' what kind of template would be needed." ) user_prompt = f"""Market event to analyze: @@ -875,6 +879,15 @@ def analyze_event(body: AnalyzeRequest): pass # Sources legacy + elif src == "surprise_bps" and event.get("surprise_pct") is not None: + # Valeur brute en bps (ex: écart taux BCE en points de base) + raw = float(event["surprise_pct"]) + node_range = cfg.get("range") + if node_range and len(node_range) == 2: + lo, hi = float(node_range[0]), float(node_range[1]) + inputs[input_id] = round(max(lo, min(hi, raw)), 4) + else: + inputs[input_id] = round(raw, 4) elif src == "surprise" and event.get("surprise_pct") is not None: raw = float(event["surprise_pct"]) node_range = cfg.get("range") diff --git a/frontend/src/pages/MarketEvents.tsx b/frontend/src/pages/MarketEvents.tsx index fbbe59c..73fd406 100644 --- a/frontend/src/pages/MarketEvents.tsx +++ b/frontend/src/pages/MarketEvents.tsx @@ -509,8 +509,32 @@ function EventDetail({ setRecMsg(`✓ ${rec.template_name}${rec.confidence ? ` (${Math.round(rec.confidence * 100)}%)` : ''}`) await instantiate(rec.template_id) } else { - setRecMsg('Aucun template adapté trouvé') - setNoTemplateFound(true) + // Aucun template adapté — création automatique d'un nouveau + setRecMsg('Aucun template adapté — création en cours…') + setNoTemplateFound(false) + setLoadingCreate(true) + try { + const cr = await fetch('/api/causal-lab/create-from-event', { + method: 'POST', headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ market_event_id: detail.id }), + }) + const cd = await cr.json() + if (!cr.ok) throw new Error(cd.detail || cr.statusText) + const newTmplId = cd.template?.id ?? cd.template_id + if (newTmplId) { + setTemplates(prev => [...prev, cd.template].filter(Boolean)) + setSelTmpl(newTmplId) + setRecMsg(`✦ Nouveau template créé : ${cd.template?.name ?? 'template'}`) + await instantiate(newTmplId) + } else { + setRecMsg('Template créé mais ID manquant') + } + } catch (ce: any) { + setRecMsg(`✗ Création échouée : ${ce.message}`) + setNoTemplateFound(true) + } finally { + setLoadingCreate(false) + } } } catch (e: any) { setRecMsg(`✗ ${(e as any).message}`) } finally { setLoadingRec(false) }