Implement TradingAgents FastAPI microservice

This commit is contained in:
Laurent Kretz 2025-10-25 11:22:12 +02:00 committed by GitHub
parent 13b826a31d
commit 111ebf245a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
1 changed files with 73 additions and 0 deletions

73
app.py Normal file
View File

@ -0,0 +1,73 @@
# app.py — micro-service FastAPI pour TradingAgents
import os, hmac, hashlib, time, datetime as dt
from fastapi import FastAPI, Header, HTTPException, Request
from pydantic import BaseModel
from tradingagents.graph.trading_graph import TradingAgentsGraph
from tradingagents.default_config import DEFAULT_CONFIG
app = FastAPI(title="TradingAgents Service")
# --------- Sécurité ----------
SERVICE_SECRET = os.getenv("SERVICE_SECRET", "")
ALLOWED_DRIFT_SEC = 120 # horodatage valide +/- 2 min
def verify_hmac(x_timestamp: str | None, x_signature: str | None, body_bytes: bytes):
if not SERVICE_SECRET:
return # pas dauth activée
if not x_timestamp or not x_signature:
raise HTTPException(status_code=401, detail="Missing auth headers")
try:
ts = int(x_timestamp)
except Exception:
raise HTTPException(status_code=401, detail="Bad timestamp")
now = int(time.time())
if abs(now - ts) > ALLOWED_DRIFT_SEC:
raise HTTPException(status_code=401, detail="Stale request")
# signature = HMAC_SHA256(secret, f"{timestamp}.{body}")
msg = f"{x_timestamp}.".encode("utf-8") + body_bytes
expected = hmac.new(SERVICE_SECRET.encode("utf-8"), msg, hashlib.sha256).hexdigest()
if not hmac.compare_digest(expected, x_signature):
raise HTTPException(status_code=401, detail="Bad signature")
# --------- Schéma requête ----------
class RunBody(BaseModel):
ticker: str
date: str | None = None # "YYYY-MM-DD" (optionnel)
deep_llm: str = "gpt-4.1-mini"
fast_llm: str = "gpt-4.1-mini"
debates: int = 1
def build_graph(deep_llm: str, fast_llm: str, debates: int):
cfg = DEFAULT_CONFIG.copy()
cfg["deep_think_llm"] = deep_llm
cfg["quick_think_llm"] = fast_llm
cfg["max_debate_rounds"] = debates
return TradingAgentsGraph(debug=False, config=cfg)
@app.get("/")
def health():
return {"ok": True, "service": "TradingAgents"}
@app.post("/run")
async def run(request: Request,
x_secret: str | None = Header(default=None),
x_timestamp: str | None = Header(default=None),
x_signature: str | None = Header(default=None)):
# 1) Secret statique (simple)
if SERVICE_SECRET and x_secret != SERVICE_SECRET:
raise HTTPException(status_code=401, detail="Unauthorized")
# 2) HMAC anti-rejeu (recommandé). Dé-commente si tu veux lactiver en plus du secret:
# body_bytes = await request.body()
# verify_hmac(x_timestamp, x_signature, body_bytes)
# body = RunBody.model_validate_json(body_bytes)
# Si tu n'actives pas l'HMAC, on parse normalement :
body = RunBody.model_validate(await request.json())
date = body.date or dt.date.today().isoformat()
graph = build_graph(body.deep_llm, body.fast_llm, body.debates)
_, decision = graph.propagate(body.ticker.upper(), date)
return decision