Spaces:
Sleeping
Sleeping
File size: 7,033 Bytes
e020ac8 99363ed e020ac8 99363ed e020ac8 99363ed e020ac8 b9ed695 267ad85 e020ac8 b9ed695 e020ac8 b9ed695 e020ac8 267ad85 b9ed695 e020ac8 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 |
"""
API v1 Router - Main entry point for all endpoints
Supports dual-mode LLM (MLX or Docker Model Runner)
"""
from fastapi import APIRouter, File, UploadFile, HTTPException
from typing import Optional
from datetime import datetime
import logging
from ...models.schemas import (
ChatRequest, ChatResponse,
FileUploadResponse, AnalysisRequest, AnalysisResponse,
SuggestionRequest, SuggestionsResponse, HealthResponse
)
from ...services.data_processor import DataProcessor
from ...services.analyzer import Analyzer
from ...services.ml_suggester import MLSuggester
from ...config import settings
router = APIRouter(prefix="/api/v1", tags=["v1"])
logger = logging.getLogger(__name__)
# Global service instances
llm_service = None # Will be set from main.py
data_processor: Optional[DataProcessor] = None
analyzer: Optional[Analyzer] = None
ml_suggester: Optional[MLSuggester] = None
async def init_services():
"""Initialize all services on startup (except LLM - done in main.py)"""
global data_processor, analyzer, ml_suggester
logger.info("π Initializing data services...")
data_processor = DataProcessor()
analyzer = Analyzer()
ml_suggester = MLSuggester()
logger.info("β
Data services initialized")
# ============ Chat Endpoint ============
@router.post("/chat", response_model=ChatResponse)
async def chat_endpoint(request: ChatRequest):
"""Chat with LLM about data analysis"""
if not llm_service or not llm_service.is_loaded:
raise HTTPException(
status_code=503,
detail="LLM not ready - still loading or connection failed"
)
try:
logger.info(f"π¬ Chat request with {len(request.messages)} messages")
# Convert Pydantic ChatMessage objects to dictionaries
messages_dict = [
{"role": msg.role, "content": msg.content}
for msg in request.messages
]
response = await llm_service.chat(
messages_dict,
request.system_prompt
)
# Determine which model name to return based on DEBUG mode
model_name = (
settings.llm_model_name_mlx if settings.debug
else settings.llm_model_name_docker
)
return ChatResponse(
response=response,
model=model_name
)
except Exception as e:
logger.error(f"β Chat error: {e}")
raise HTTPException(status_code=500, detail=str(e))
# ============ File Upload Endpoint ============
@router.post("/upload", response_model=FileUploadResponse)
async def upload_file(file: UploadFile = File(...)):
"""Upload and process data file (CSV or Excel)"""
try:
if not data_processor:
raise HTTPException(status_code=503, detail="Service not ready")
logger.info(f"π Processing file: {file.filename}")
data, file_type = await data_processor.process_file(file)
# Get column names from first row (if data exists)
column_names = list(data[0].keys()) if data else []
# Get preview (first 5 rows)
preview = data[:5] if data else []
logger.info(f"β
Upload successful: {len(data)} rows, {len(column_names)} columns")
return FileUploadResponse(
filename=file.filename,
size=len(str(data)),
rows=len(data),
columns=len(column_names),
column_names=column_names,
preview=preview,
file_type=file_type
)
except ValueError as e:
logger.error(f"β Validation error: {e}")
raise HTTPException(status_code=400, detail=str(e))
except Exception as e:
logger.error(f"β Upload error: {e}")
raise HTTPException(status_code=500, detail=str(e))
# ============ Analysis Endpoint ============
@router.post("/analyze", response_model=AnalysisResponse)
async def analyze_data(request: AnalysisRequest):
"""Analyze data - supports multiple analysis types"""
try:
if not analyzer:
raise HTTPException(status_code=503, detail="Service not ready")
logger.info(f"π Analysis: {request.analysis_type} on {len(request.data)} rows")
# Call analyzer with await
results = await analyzer.analyze(
request.data,
request.analysis_type,
request.columns
)
summary = f"Analysis complete: {request.analysis_type} on {len(request.data)} rows"
import pandas as pd
df = pd.DataFrame(request.data)
data_shape = df.shape
return AnalysisResponse(
analysis_type=request.analysis_type,
results=results,
summary=summary,
data_shape=data_shape,
timestamp=datetime.now()
)
except ValueError as e:
logger.error(f"Invalid analysis request: {e}")
raise HTTPException(status_code=400, detail=str(e))
except Exception as e:
logger.error(f"β Analysis error: {e}")
raise HTTPException(status_code=500, detail=str(e))
# ============ ML Suggestions Endpoint ============
@router.post("/suggestions", response_model=SuggestionsResponse)
async def get_suggestions(request: SuggestionRequest):
"""Get ML-based suggestions for data improvement"""
try:
if not ml_suggester:
raise HTTPException(status_code=503, detail="Service not ready")
logger.info(f"π€ Generating suggestions for {len(request.data)} rows")
suggestions_list = ml_suggester.generate(
request.data,
request.analysis_context
)
return SuggestionsResponse(
suggestions=suggestions_list,
total_suggestions=len(suggestions_list),
timestamp=datetime.now()
)
except Exception as e:
logger.error(f"β Suggestion error: {e}")
raise HTTPException(status_code=500, detail=str(e))
# ============ Health Check ============
@router.get("/health", response_model=HealthResponse)
async def health_check():
"""Health check endpoint - shows current LLM mode"""
# Determine LLM status
llm_status = "unknown"
if llm_service:
if llm_service.is_mock:
llm_status = "mock-mode"
elif llm_service.is_loaded:
llm_status = "loaded"
else:
llm_status = "failed"
# Show which mode is active
mode = "MLX (local)" if settings.debug else "Docker Model Runner"
return HealthResponse(
status="healthy",
environment=settings.fastapi_env,
service="llm-data-analyzer-backend",
llm_loaded=llm_service.is_loaded if llm_service else False,
llm_model=(
settings.llm_model_name_mlx if settings.debug
else settings.llm_model_name_docker
),
version="0.2.0"
)
|