|
|
|
|
|
"""
|
|
|
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
|
|
|
"""
|
|
|
|