|
|
|
|
|
""" |
|
|
FastAPI Router for Unified AI Services |
|
|
""" |
|
|
|
|
|
from fastapi import APIRouter, HTTPException, Query, Body |
|
|
from typing import Dict, Any, Optional, List |
|
|
from pydantic import BaseModel, Field |
|
|
import logging |
|
|
import sys |
|
|
import os |
|
|
|
|
|
|
|
|
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.dirname(__file__)))) |
|
|
|
|
|
from backend.services.ai_service_unified import get_unified_service, analyze_text |
|
|
from backend.services.hf_dataset_loader import HFDatasetService, quick_price_data, quick_crypto_news |
|
|
|
|
|
logger = logging.getLogger(__name__) |
|
|
|
|
|
router = APIRouter(prefix="/api/ai", tags=["AI Services"]) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class SentimentRequest(BaseModel): |
|
|
"""درخواست تحلیل sentiment""" |
|
|
text: str = Field(..., description="متن برای تحلیل", min_length=1, max_length=2000) |
|
|
category: str = Field("crypto", description="دستهبندی: crypto, financial, social") |
|
|
use_ensemble: bool = Field(True, description="استفاده از ensemble") |
|
|
|
|
|
|
|
|
class BulkSentimentRequest(BaseModel): |
|
|
"""درخواست تحلیل چند متن""" |
|
|
texts: List[str] = Field(..., description="لیست متنها", min_items=1, max_items=50) |
|
|
category: str = Field("crypto", description="دستهبندی") |
|
|
use_ensemble: bool = Field(True, description="استفاده از ensemble") |
|
|
|
|
|
|
|
|
class PriceDataRequest(BaseModel): |
|
|
"""درخواست داده قیمت""" |
|
|
symbol: str = Field("BTC", description="نماد کریپتو") |
|
|
days: int = Field(7, description="تعداد روز", ge=1, le=90) |
|
|
timeframe: str = Field("1h", description="بازه زمانی") |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.get("/health") |
|
|
async def health_check(): |
|
|
""" |
|
|
بررسی وضعیت سلامت سرویس AI |
|
|
""" |
|
|
try: |
|
|
service = await get_unified_service() |
|
|
health = service.get_health_status() |
|
|
|
|
|
return { |
|
|
"status": "ok", |
|
|
"service": "AI Unified", |
|
|
"health": health |
|
|
} |
|
|
except Exception as e: |
|
|
logger.error(f"Health check failed: {e}") |
|
|
return { |
|
|
"status": "error", |
|
|
"error": str(e) |
|
|
} |
|
|
|
|
|
|
|
|
@router.get("/info") |
|
|
async def get_service_info(): |
|
|
""" |
|
|
دریافت اطلاعات سرویس |
|
|
""" |
|
|
try: |
|
|
service = await get_unified_service() |
|
|
info = service.get_service_info() |
|
|
|
|
|
return { |
|
|
"status": "ok", |
|
|
"info": info |
|
|
} |
|
|
except Exception as e: |
|
|
logger.error(f"Failed to get service info: {e}") |
|
|
raise HTTPException(status_code=500, detail=str(e)) |
|
|
|
|
|
|
|
|
@router.post("/sentiment") |
|
|
async def analyze_sentiment(request: SentimentRequest): |
|
|
""" |
|
|
تحلیل sentiment یک متن |
|
|
|
|
|
### مثال: |
|
|
```json |
|
|
{ |
|
|
"text": "Bitcoin is showing strong bullish momentum!", |
|
|
"category": "crypto", |
|
|
"use_ensemble": true |
|
|
} |
|
|
``` |
|
|
|
|
|
### پاسخ: |
|
|
```json |
|
|
{ |
|
|
"status": "success", |
|
|
"label": "bullish", |
|
|
"confidence": 0.85, |
|
|
"engine": "hf_inference_api_ensemble" |
|
|
} |
|
|
``` |
|
|
""" |
|
|
try: |
|
|
result = await analyze_text( |
|
|
text=request.text, |
|
|
category=request.category, |
|
|
use_ensemble=request.use_ensemble |
|
|
) |
|
|
|
|
|
return result |
|
|
|
|
|
except Exception as e: |
|
|
logger.error(f"Sentiment analysis failed: {e}") |
|
|
raise HTTPException(status_code=500, detail=str(e)) |
|
|
|
|
|
|
|
|
@router.post("/sentiment/bulk") |
|
|
async def analyze_bulk_sentiment(request: BulkSentimentRequest): |
|
|
""" |
|
|
تحلیل sentiment چند متن به صورت همزمان |
|
|
|
|
|
### مثال: |
|
|
```json |
|
|
{ |
|
|
"texts": [ |
|
|
"Bitcoin is pumping!", |
|
|
"Market is crashing", |
|
|
"Consolidation phase" |
|
|
], |
|
|
"category": "crypto", |
|
|
"use_ensemble": true |
|
|
} |
|
|
``` |
|
|
""" |
|
|
try: |
|
|
import asyncio |
|
|
|
|
|
|
|
|
tasks = [ |
|
|
analyze_text(text, request.category, request.use_ensemble) |
|
|
for text in request.texts |
|
|
] |
|
|
|
|
|
results = await asyncio.gather(*tasks, return_exceptions=True) |
|
|
|
|
|
|
|
|
processed_results = [] |
|
|
for i, result in enumerate(results): |
|
|
if isinstance(result, Exception): |
|
|
processed_results.append({ |
|
|
"text": request.texts[i], |
|
|
"status": "error", |
|
|
"error": str(result) |
|
|
}) |
|
|
else: |
|
|
processed_results.append({ |
|
|
"text": request.texts[i], |
|
|
**result |
|
|
}) |
|
|
|
|
|
|
|
|
successful = sum(1 for r in processed_results if r.get("status") == "success") |
|
|
|
|
|
return { |
|
|
"status": "ok", |
|
|
"total": len(request.texts), |
|
|
"successful": successful, |
|
|
"failed": len(request.texts) - successful, |
|
|
"results": processed_results |
|
|
} |
|
|
|
|
|
except Exception as e: |
|
|
logger.error(f"Bulk sentiment analysis failed: {e}") |
|
|
raise HTTPException(status_code=500, detail=str(e)) |
|
|
|
|
|
|
|
|
@router.get("/sentiment/quick") |
|
|
async def quick_sentiment_analysis( |
|
|
text: str = Query(..., description="متن برای تحلیل", min_length=1), |
|
|
category: str = Query("crypto", description="دستهبندی") |
|
|
): |
|
|
""" |
|
|
تحلیل سریع sentiment (GET request) |
|
|
|
|
|
### مثال: |
|
|
``` |
|
|
GET /api/ai/sentiment/quick?text=Bitcoin%20to%20the%20moon&category=crypto |
|
|
``` |
|
|
""" |
|
|
try: |
|
|
result = await analyze_text(text=text, category=category, use_ensemble=False) |
|
|
return result |
|
|
|
|
|
except Exception as e: |
|
|
logger.error(f"Quick sentiment failed: {e}") |
|
|
raise HTTPException(status_code=500, detail=str(e)) |
|
|
|
|
|
|
|
|
@router.post("/data/prices") |
|
|
async def get_historical_prices(request: PriceDataRequest): |
|
|
""" |
|
|
دریافت داده قیمت تاریخی از HuggingFace Datasets |
|
|
|
|
|
### مثال: |
|
|
```json |
|
|
{ |
|
|
"symbol": "BTC", |
|
|
"days": 7, |
|
|
"timeframe": "1h" |
|
|
} |
|
|
``` |
|
|
""" |
|
|
try: |
|
|
service = HFDatasetService() |
|
|
|
|
|
if not service.is_available(): |
|
|
return { |
|
|
"status": "error", |
|
|
"error": "datasets library not available", |
|
|
"installation": "pip install datasets" |
|
|
} |
|
|
|
|
|
result = await service.get_historical_prices( |
|
|
symbol=request.symbol, |
|
|
days=request.days, |
|
|
timeframe=request.timeframe |
|
|
) |
|
|
|
|
|
return result |
|
|
|
|
|
except Exception as e: |
|
|
logger.error(f"Failed to get historical prices: {e}") |
|
|
raise HTTPException(status_code=500, detail=str(e)) |
|
|
|
|
|
|
|
|
@router.get("/data/prices/quick/{symbol}") |
|
|
async def quick_historical_prices( |
|
|
symbol: str, |
|
|
days: int = Query(7, ge=1, le=90) |
|
|
): |
|
|
""" |
|
|
دریافت سریع داده قیمت |
|
|
|
|
|
### مثال: |
|
|
``` |
|
|
GET /api/ai/data/prices/quick/BTC?days=7 |
|
|
``` |
|
|
""" |
|
|
try: |
|
|
result = await quick_price_data(symbol=symbol.upper(), days=days) |
|
|
return result |
|
|
|
|
|
except Exception as e: |
|
|
logger.error(f"Quick price data failed: {e}") |
|
|
raise HTTPException(status_code=500, detail=str(e)) |
|
|
|
|
|
|
|
|
@router.get("/data/news") |
|
|
async def get_crypto_news( |
|
|
limit: int = Query(10, ge=1, le=100, description="تعداد خبر") |
|
|
): |
|
|
""" |
|
|
دریافت اخبار کریپتو از HuggingFace Datasets |
|
|
|
|
|
### مثال: |
|
|
``` |
|
|
GET /api/ai/data/news?limit=10 |
|
|
``` |
|
|
""" |
|
|
try: |
|
|
news = await quick_crypto_news(limit=limit) |
|
|
|
|
|
return { |
|
|
"status": "ok", |
|
|
"count": len(news), |
|
|
"news": news |
|
|
} |
|
|
|
|
|
except Exception as e: |
|
|
logger.error(f"Failed to get crypto news: {e}") |
|
|
raise HTTPException(status_code=500, detail=str(e)) |
|
|
|
|
|
|
|
|
@router.get("/datasets/available") |
|
|
async def get_available_datasets(): |
|
|
""" |
|
|
لیست Datasetهای موجود |
|
|
""" |
|
|
try: |
|
|
service = HFDatasetService() |
|
|
datasets = service.get_available_datasets() |
|
|
|
|
|
return { |
|
|
"status": "ok", |
|
|
"datasets": datasets |
|
|
} |
|
|
|
|
|
except Exception as e: |
|
|
logger.error(f"Failed to get datasets: {e}") |
|
|
raise HTTPException(status_code=500, detail=str(e)) |
|
|
|
|
|
|
|
|
@router.get("/models/available") |
|
|
async def get_available_models(): |
|
|
""" |
|
|
لیست مدلهای AI موجود |
|
|
""" |
|
|
try: |
|
|
from backend.services.hf_inference_api_client import HFInferenceAPIClient |
|
|
|
|
|
async with HFInferenceAPIClient() as client: |
|
|
models = client.get_available_models() |
|
|
|
|
|
return { |
|
|
"status": "ok", |
|
|
"models": models |
|
|
} |
|
|
|
|
|
except Exception as e: |
|
|
logger.error(f"Failed to get models: {e}") |
|
|
raise HTTPException(status_code=500, detail=str(e)) |
|
|
|
|
|
|
|
|
@router.get("/stats") |
|
|
async def get_service_statistics(): |
|
|
""" |
|
|
آمار استفاده از سرویس |
|
|
""" |
|
|
try: |
|
|
service = await get_unified_service() |
|
|
|
|
|
return { |
|
|
"status": "ok", |
|
|
"stats": service.stats |
|
|
} |
|
|
|
|
|
except Exception as e: |
|
|
logger.error(f"Failed to get stats: {e}") |
|
|
raise HTTPException(status_code=500, detail=str(e)) |
|
|
|
|
|
|
|
|
|
|
|
""" |
|
|
# در فایل app.py یا production_server.py: |
|
|
|
|
|
from backend.routers.ai_unified import router as ai_router |
|
|
|
|
|
app = FastAPI() |
|
|
app.include_router(ai_router) |
|
|
|
|
|
# حالا endpointهای زیر در دسترس هستند: |
|
|
# - POST /api/ai/sentiment |
|
|
# - POST /api/ai/sentiment/bulk |
|
|
# - GET /api/ai/sentiment/quick |
|
|
# - POST /api/ai/data/prices |
|
|
# - GET /api/ai/data/prices/quick/{symbol} |
|
|
# - GET /api/ai/data/news |
|
|
# - GET /api/ai/datasets/available |
|
|
# - GET /api/ai/models/available |
|
|
# - GET /api/ai/health |
|
|
# - GET /api/ai/info |
|
|
# - GET /api/ai/stats |
|
|
""" |
|
|
|