import uuid from contextlib import asynccontextmanager from datetime import datetime, timezone from fastapi import FastAPI, Query from pydantic import BaseModel from app.db.store import init_db, save_decision, get_history, get_stats from app.engine.decision import decide from app.engine.explain import explain from app.nlp.emotion import detect_emotion from app.nlp.emoji_map import sentiment_emoji, emotion_emoji from app.nlp.language import detect_language from app.nlp.sentiment import analyze_sentiment from app.nlp.signals import extract_signals from app.engine.priority import compute_priority @asynccontextmanager async def lifespan(app: FastAPI): init_db() yield app = FastAPI(title="AI Decision Maker", lifespan=lifespan) # ── Request / Response models ────────────────────────────────────────────────── class DecideRequest(BaseModel): text: str class SentimentOut(BaseModel): label: str score: float confidence: float class SignalsOut(BaseModel): urgency: str intent: str keywords: list[str] class DecideResponse(BaseModel): id: str timestamp: str text: str sentiment: SentimentOut signals: SignalsOut decision: str confidence: float explanation: str emotion: str emotion_score: float sentiment_emoji: str emotion_emoji: str priority_score: int priority_level: str factors: dict[str, int] detected_language: str class HistoryRecord(BaseModel): id: str text: str sentiment_label: str score: float urgency: str intent: str decision: str confidence: float explanation: str created_at: str emotion: str = "neutral" emotion_score: float = 0.0 priority_score: int = 0 priority_level: str = "LOW" detected_language: str = "en" class StatsResponse(BaseModel): total: int by_decision: dict[str, int] by_sentiment: dict[str, int] avg_priority_score: float = 0.0 by_priority_level: dict[str, int] = {} # ── Endpoints ────────────────────────────────────────────────────────────────── @app.get("/health") def health(): return {"status": "ok"} @app.post("/decide", response_model=DecideResponse) def post_decide(body: DecideRequest): language = detect_language(body.text) sentiment = analyze_sentiment(body.text, language=language) signals = extract_signals(body.text) decision_result = decide(sentiment, signals) explanation_text = explain(sentiment, signals, decision_result) emotion_result = detect_emotion(body.text) priority_result = compute_priority(sentiment, signals, emotion_result) sent_emoji = sentiment_emoji(sentiment["label"], sentiment["confidence"]) emo_emoji = emotion_emoji(emotion_result["emotion"]) record_id = str(uuid.uuid4()) timestamp = datetime.now(timezone.utc).isoformat() save_decision({ "id": record_id, "text": body.text, "sentiment_label": sentiment["label"], "score": sentiment["score"], "urgency": signals["urgency"], "intent": signals["intent"], "decision": decision_result["decision"], "confidence": decision_result["confidence"], "explanation": explanation_text, "created_at": timestamp, "emotion": emotion_result["emotion"], "emotion_score": emotion_result["score"], "priority_score": priority_result["priority_score"], "priority_level": priority_result["priority_level"], "detected_language": language, }) return DecideResponse( id=record_id, timestamp=timestamp, text=body.text, sentiment=SentimentOut(**sentiment), signals=SignalsOut(**signals), decision=decision_result["decision"], confidence=decision_result["confidence"], explanation=explanation_text, emotion=emotion_result["emotion"], emotion_score=emotion_result["score"], sentiment_emoji=sent_emoji, emotion_emoji=emo_emoji, priority_score=priority_result["priority_score"], priority_level=priority_result["priority_level"], factors=priority_result["factors"], detected_language=language, ) @app.get("/history", response_model=list[HistoryRecord]) def history(limit: int = Query(default=50, ge=1, le=500)): return get_history(limit) @app.get("/stats", response_model=StatsResponse) def stats(): return get_stats()