feat: causal lab — éditeur visuel, types nœuds, force/signe arêtes, NFP v2

This commit is contained in:
OpenSquared
2026-06-28 09:28:43 +02:00
parent 83a8e8c5be
commit e8fb7c13aa
3 changed files with 838 additions and 199 deletions

View File

@@ -306,6 +306,16 @@ class UpdateCoefRequest(BaseModel):
coefficients: dict # {coef_key: float}
class CreateTemplateRequest(BaseModel):
name: str
category: str
sub_type: str = ""
description: str = ""
instruments: list = ["EURUSD"]
graph_json: dict
ai_rationale: str = ""
class RecommendRequest(BaseModel):
market_event_id: int
@@ -314,8 +324,8 @@ class AnalyzeRequest(BaseModel):
market_event_id: int
template_id: int
instrument: str = "EURUSD"
inputs: dict = {} # Inputs manuels (ex: tone_score)
coef_overrides: dict = {} # Coefficients overrides pour cet run
inputs: dict = {}
coef_overrides: dict = {}
# ── Endpoints ─────────────────────────────────────────────────────────────────
@@ -359,6 +369,62 @@ def list_templates(category: str = Query("")):
raise HTTPException(500, str(e))
@router.post("/api/causal-lab/template")
def create_template(body: CreateTemplateRequest):
"""Crée un nouveau template utilisateur."""
try:
from services.database import get_conn
from services.causal_graphs import init_tables, seed_templates
conn = get_conn()
init_tables(conn); seed_templates(conn)
existing = conn.execute(
"SELECT id FROM causal_graph_templates WHERE name = ?", (body.name,)
).fetchone()
if existing:
raise HTTPException(409, f"Un template nommé '{body.name}' existe déjà")
cur = conn.execute("""
INSERT INTO causal_graph_templates
(name, category, sub_type, instruments, description, graph_json, ai_rationale, created_by)
VALUES (?, ?, ?, ?, ?, ?, ?, 'user')
""", (
body.name, body.category, body.sub_type,
json.dumps(body.instruments), body.description,
json.dumps(body.graph_json), body.ai_rationale,
))
conn.commit()
new_id = cur.lastrowid
conn.close()
return {"id": new_id, "name": body.name}
except HTTPException:
raise
except Exception as e:
logger.error(f"[causal_lab] create_template: {e}")
raise HTTPException(500, str(e))
@router.delete("/api/causal-lab/template/{template_id}")
def delete_template(template_id: int):
"""Supprime un template utilisateur (les templates system sont protégés)."""
try:
from services.database import get_conn
conn = get_conn()
row = conn.execute(
"SELECT created_by FROM causal_graph_templates WHERE id = ?", (template_id,)
).fetchone()
if not row:
conn.close(); raise HTTPException(404, "Template introuvable")
if row["created_by"] == "system":
conn.close(); raise HTTPException(403, "Les templates système ne peuvent pas être supprimés")
conn.execute("DELETE FROM causal_graph_templates WHERE id = ?", (template_id,))
conn.commit(); conn.close()
return {"ok": True}
except HTTPException:
raise
except Exception as e:
logger.error(f"[causal_lab] delete_template {template_id}: {e}")
raise HTTPException(500, str(e))
@router.get("/api/causal-lab/template/{template_id}")
def get_template_detail(template_id: int):
try: