#!/usr/bin/env python3 """ Sentiment Analysis Router POST /api/sentiment, POST /api/news/sentiment, POST /api/economy/analyze """ from fastapi import APIRouter, HTTPException, Body from fastapi.responses import JSONResponse from typing import Optional, Dict, Any, List from datetime import datetime, timezone from pydantic import BaseModel import logging from ai_models import ModelRegistry, call_model_safe, basic_sentiment_fallback from backend.services.persistence_service import PersistenceService logger = logging.getLogger(__name__) router = APIRouter( prefix="/api/sentiment", tags=["Sentiment Analysis"] ) persistence_service = PersistenceService() model_registry = ModelRegistry() if ModelRegistry else None class SentimentRequest(BaseModel): text: str model_key: Optional[str] = "crypto_sent_0" mode: Optional[str] = "auto" class NewsSentimentRequest(BaseModel): title: str content: Optional[str] = None url: Optional[str] = None class EconomyAnalyzeRequest(BaseModel): text: str indicators: Optional[List[str]] = None def create_response(data: Any, source: str) -> Dict[str, Any]: """Create standardized response""" return { "data": data, "meta": { "source": source, "generated_at": datetime.now(timezone.utc).isoformat(), "cache_ttl": 300 } } @router.post("") async def analyze_sentiment(request: SentimentRequest): """Analyze sentiment of text - compatible with frontend expectations""" try: # Try HF models first if model_registry: result = call_model_safe(request.model_key, request.text) if result.get("status") == "success": label = result.get("data", {}).get("label", "neutral") score = result.get("data", {}).get("score", 0.5) # Normalize sentiment label for frontend sentiment_label = "positive" if label in ["positive", "bullish"] else \ "negative" if label in ["negative", "bearish"] else "neutral" sentiment_data = { "available": True, "sentiment": sentiment_label, "confidence": float(score), "model": request.model_key, "text": request.text[:100], "result": { "label": label, "score": score, "model_used": request.model_key } } # Save to database await persistence_service.save_sentiment( request.text, sentiment_label, float(score), request.model_key ) return sentiment_data # Fallback to lexical analysis fallback_result = basic_sentiment_fallback(request.text) label = fallback_result.get("label", "neutral") score = fallback_result.get("confidence", 0.5) # Normalize sentiment label for frontend sentiment_label = "positive" if label in ["positive", "bullish"] else \ "negative" if label in ["negative", "bearish"] else "neutral" sentiment_data = { "available": True, "sentiment": sentiment_label, "confidence": float(score), "model": "lexical_fallback", "text": request.text[:100], "result": { "label": label, "score": score, "model_used": "lexical_fallback" } } await persistence_service.save_sentiment( request.text, sentiment_label, float(score), "lexical_fallback" ) return sentiment_data except Exception as e: logger.error(f"Sentiment analysis failed: {e}") return { "available": False, "error": str(e), "sentiment": "neutral", "confidence": 0.0 } @router.post("/news") async def analyze_news_sentiment(request: NewsSentimentRequest): """Analyze sentiment of news article""" text = f"{request.title} {request.content or ''}" sentiment_request = SentimentRequest(text=text, model_key="news_sent_0") return await analyze_sentiment(sentiment_request) @router.post("/economy/analyze") async def analyze_economy(request: EconomyAnalyzeRequest): """Analyze economic indicators and sentiment""" try: # Use financial sentiment model if model_registry: result = call_model_safe("financial_sent_0", request.text) if result.get("status") == "success": analysis_data = { "sentiment": result.get("data", {}).get("label", "neutral"), "confidence": result.get("data", {}).get("score", 0.5), "indicators": request.indicators or [], "model": "financial_sent_0", "text": request.text[:100] } return create_response(analysis_data, "hf_models") # Fallback fallback_result = basic_sentiment_fallback(request.text) analysis_data = { "sentiment": fallback_result.get("label", "neutral"), "confidence": fallback_result.get("confidence", 0.5), "indicators": request.indicators or [], "model": "lexical_fallback" } return create_response(analysis_data, "lexical_fallback") except Exception as e: logger.error(f"Economy analysis failed: {e}") raise HTTPException(status_code=500, detail=str(e))