Spaces:
Running
Running
Arif
commited on
Commit
·
e020ac8
1
Parent(s):
3f44a73
Updated backend. Added ML system. Both if mlx is running in debug also docker model runner if not debug mode.
Browse files- backend/app/api/v1/router.py +206 -0
- backend/app/config.py +46 -51
- backend/app/main.py +69 -27
- backend/app/services/analyzer.py +244 -8
- backend/app/services/data_processor.py +114 -8
- backend/app/services/llm_service.py +277 -13
- backend/app/services/ml_suggester.py +145 -8
- pyproject.toml +4 -5
- uv.lock +388 -0
backend/app/api/v1/router.py
ADDED
|
@@ -0,0 +1,206 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
API v1 Router - Main entry point for all endpoints
|
| 3 |
+
Supports dual-mode LLM (MLX or Docker Model Runner)
|
| 4 |
+
"""
|
| 5 |
+
from fastapi import APIRouter, File, UploadFile, HTTPException
|
| 6 |
+
from typing import Optional
|
| 7 |
+
from datetime import datetime
|
| 8 |
+
import logging
|
| 9 |
+
|
| 10 |
+
from ...models.schemas import (
|
| 11 |
+
ChatRequest, ChatResponse,
|
| 12 |
+
FileUploadResponse, AnalysisRequest, AnalysisResponse,
|
| 13 |
+
SuggestionRequest, SuggestionsResponse, HealthResponse
|
| 14 |
+
)
|
| 15 |
+
from ...services.data_processor import DataProcessor
|
| 16 |
+
from ...services.analyzer import Analyzer
|
| 17 |
+
from ...services.ml_suggester import MLSuggester
|
| 18 |
+
from ...config import settings
|
| 19 |
+
|
| 20 |
+
router = APIRouter(prefix="/api/v1", tags=["v1"])
|
| 21 |
+
logger = logging.getLogger(__name__)
|
| 22 |
+
|
| 23 |
+
# Global service instances
|
| 24 |
+
llm_service = None # Will be set from main.py
|
| 25 |
+
data_processor: Optional[DataProcessor] = None
|
| 26 |
+
analyzer: Optional[Analyzer] = None
|
| 27 |
+
ml_suggester: Optional[MLSuggester] = None
|
| 28 |
+
|
| 29 |
+
|
| 30 |
+
async def init_services():
|
| 31 |
+
"""Initialize all services on startup (except LLM - done in main.py)"""
|
| 32 |
+
global data_processor, analyzer, ml_suggester
|
| 33 |
+
|
| 34 |
+
logger.info("🚀 Initializing data services...")
|
| 35 |
+
|
| 36 |
+
data_processor = DataProcessor()
|
| 37 |
+
analyzer = Analyzer()
|
| 38 |
+
ml_suggester = MLSuggester()
|
| 39 |
+
|
| 40 |
+
logger.info("✅ Data services initialized")
|
| 41 |
+
|
| 42 |
+
|
| 43 |
+
# ============ Chat Endpoint ============
|
| 44 |
+
|
| 45 |
+
@router.post("/chat", response_model=ChatResponse)
|
| 46 |
+
async def chat_endpoint(request: ChatRequest):
|
| 47 |
+
"""Chat with LLM about data analysis"""
|
| 48 |
+
if not llm_service or not llm_service.is_loaded:
|
| 49 |
+
raise HTTPException(
|
| 50 |
+
status_code=503,
|
| 51 |
+
detail="LLM not ready - still loading or connection failed"
|
| 52 |
+
)
|
| 53 |
+
|
| 54 |
+
try:
|
| 55 |
+
logger.info(f"💬 Chat request with {len(request.messages)} messages")
|
| 56 |
+
|
| 57 |
+
# Convert Pydantic ChatMessage objects to dictionaries
|
| 58 |
+
messages_dict = [
|
| 59 |
+
{"role": msg.role, "content": msg.content}
|
| 60 |
+
for msg in request.messages
|
| 61 |
+
]
|
| 62 |
+
|
| 63 |
+
response = await llm_service.chat(
|
| 64 |
+
messages_dict,
|
| 65 |
+
request.system_prompt
|
| 66 |
+
)
|
| 67 |
+
|
| 68 |
+
# Determine which model name to return based on DEBUG mode
|
| 69 |
+
model_name = (
|
| 70 |
+
settings.llm_model_name_mlx if settings.debug
|
| 71 |
+
else settings.llm_model_name_docker
|
| 72 |
+
)
|
| 73 |
+
|
| 74 |
+
return ChatResponse(
|
| 75 |
+
response=response,
|
| 76 |
+
model=model_name
|
| 77 |
+
)
|
| 78 |
+
except Exception as e:
|
| 79 |
+
logger.error(f"❌ Chat error: {e}")
|
| 80 |
+
raise HTTPException(status_code=500, detail=str(e))
|
| 81 |
+
|
| 82 |
+
|
| 83 |
+
# ============ File Upload Endpoint ============
|
| 84 |
+
|
| 85 |
+
@router.post("/upload", response_model=FileUploadResponse)
|
| 86 |
+
async def upload_file(file: UploadFile = File(...)):
|
| 87 |
+
"""Upload and process data file (CSV or Excel)"""
|
| 88 |
+
try:
|
| 89 |
+
if not data_processor:
|
| 90 |
+
raise HTTPException(status_code=503, detail="Service not ready")
|
| 91 |
+
|
| 92 |
+
logger.info(f"📁 Processing file: {file.filename}")
|
| 93 |
+
data, file_type = await data_processor.process_file(file)
|
| 94 |
+
|
| 95 |
+
# Get preview (first 5 rows)
|
| 96 |
+
preview = data[:5] if data else []
|
| 97 |
+
|
| 98 |
+
# Get column names
|
| 99 |
+
column_names = list(data.keys()) if data else []
|
| 100 |
+
|
| 101 |
+
return FileUploadResponse(
|
| 102 |
+
filename=file.filename,
|
| 103 |
+
size=len(str(data)),
|
| 104 |
+
rows=len(data),
|
| 105 |
+
columns=len(column_names),
|
| 106 |
+
column_names=column_names,
|
| 107 |
+
preview=preview,
|
| 108 |
+
file_type=file_type
|
| 109 |
+
)
|
| 110 |
+
except ValueError as e:
|
| 111 |
+
logger.error(f"Validation error: {e}")
|
| 112 |
+
raise HTTPException(status_code=400, detail=str(e))
|
| 113 |
+
except Exception as e:
|
| 114 |
+
logger.error(f"❌ Upload error: {e}")
|
| 115 |
+
raise HTTPException(status_code=500, detail=str(e))
|
| 116 |
+
|
| 117 |
+
|
| 118 |
+
# ============ Analysis Endpoint ============
|
| 119 |
+
|
| 120 |
+
@router.post("/analyze", response_model=AnalysisResponse)
|
| 121 |
+
async def analyze_data(request: AnalysisRequest):
|
| 122 |
+
"""Analyze data - supports multiple analysis types"""
|
| 123 |
+
try:
|
| 124 |
+
if not analyzer:
|
| 125 |
+
raise HTTPException(status_code=503, detail="Service not ready")
|
| 126 |
+
|
| 127 |
+
logger.info(f"📊 Analysis: {request.analysis_type} on {len(request.data)} rows")
|
| 128 |
+
|
| 129 |
+
results = analyzer.analyze(
|
| 130 |
+
request.data,
|
| 131 |
+
request.analysis_type,
|
| 132 |
+
request.columns
|
| 133 |
+
)
|
| 134 |
+
|
| 135 |
+
summary = analyzer.generate_summary(results)
|
| 136 |
+
|
| 137 |
+
return AnalysisResponse(
|
| 138 |
+
analysis_type=request.analysis_type,
|
| 139 |
+
results=results,
|
| 140 |
+
summary=summary,
|
| 141 |
+
timestamp=datetime.now()
|
| 142 |
+
)
|
| 143 |
+
except ValueError as e:
|
| 144 |
+
logger.error(f"Invalid analysis request: {e}")
|
| 145 |
+
raise HTTPException(status_code=400, detail=str(e))
|
| 146 |
+
except Exception as e:
|
| 147 |
+
logger.error(f"❌ Analysis error: {e}")
|
| 148 |
+
raise HTTPException(status_code=500, detail=str(e))
|
| 149 |
+
|
| 150 |
+
|
| 151 |
+
# ============ ML Suggestions Endpoint ============
|
| 152 |
+
|
| 153 |
+
@router.post("/suggestions", response_model=SuggestionsResponse)
|
| 154 |
+
async def get_suggestions(request: SuggestionRequest):
|
| 155 |
+
"""Get ML-based suggestions for data improvement"""
|
| 156 |
+
try:
|
| 157 |
+
if not ml_suggester:
|
| 158 |
+
raise HTTPException(status_code=503, detail="Service not ready")
|
| 159 |
+
|
| 160 |
+
logger.info(f"🤖 Generating suggestions for {len(request.data)} rows")
|
| 161 |
+
|
| 162 |
+
suggestions_list = ml_suggester.generate(
|
| 163 |
+
request.data,
|
| 164 |
+
request.analysis_context
|
| 165 |
+
)
|
| 166 |
+
|
| 167 |
+
return SuggestionsResponse(
|
| 168 |
+
suggestions=suggestions_list,
|
| 169 |
+
total_suggestions=len(suggestions_list),
|
| 170 |
+
timestamp=datetime.now()
|
| 171 |
+
)
|
| 172 |
+
except Exception as e:
|
| 173 |
+
logger.error(f"❌ Suggestion error: {e}")
|
| 174 |
+
raise HTTPException(status_code=500, detail=str(e))
|
| 175 |
+
|
| 176 |
+
|
| 177 |
+
# ============ Health Check ============
|
| 178 |
+
|
| 179 |
+
@router.get("/health", response_model=HealthResponse)
|
| 180 |
+
async def health_check():
|
| 181 |
+
"""Health check endpoint - shows current LLM mode"""
|
| 182 |
+
|
| 183 |
+
# Determine LLM status
|
| 184 |
+
llm_status = "unknown"
|
| 185 |
+
if llm_service:
|
| 186 |
+
if llm_service.is_mock:
|
| 187 |
+
llm_status = "mock-mode"
|
| 188 |
+
elif llm_service.is_loaded:
|
| 189 |
+
llm_status = "loaded"
|
| 190 |
+
else:
|
| 191 |
+
llm_status = "failed"
|
| 192 |
+
|
| 193 |
+
# Show which mode is active
|
| 194 |
+
mode = "MLX (local)" if settings.debug else "Docker Model Runner"
|
| 195 |
+
|
| 196 |
+
return HealthResponse(
|
| 197 |
+
status="healthy",
|
| 198 |
+
environment=settings.fastapi_env,
|
| 199 |
+
service="llm-data-analyzer-backend",
|
| 200 |
+
llm_loaded=llm_service.is_loaded if llm_service else False,
|
| 201 |
+
llm_model=(
|
| 202 |
+
settings.llm_model_name_mlx if settings.debug
|
| 203 |
+
else settings.llm_model_name_docker
|
| 204 |
+
),
|
| 205 |
+
version="0.2.0"
|
| 206 |
+
)
|
backend/app/config.py
CHANGED
|
@@ -1,74 +1,69 @@
|
|
| 1 |
"""
|
| 2 |
-
Configuration
|
| 3 |
-
|
|
|
|
| 4 |
"""
|
| 5 |
-
from pydantic_settings import BaseSettings
|
| 6 |
-
from pathlib import Path
|
| 7 |
-
from typing import List
|
| 8 |
import logging
|
|
|
|
|
|
|
| 9 |
|
|
|
|
| 10 |
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
possible_paths = [
|
| 14 |
-
Path("/Users/arif/Projects/Personal/LLM-Data-Analyzer/.env.local"),
|
| 15 |
-
Path.cwd() / ".env.local",
|
| 16 |
-
Path(__file__).parent.parent.parent / ".env.local",
|
| 17 |
-
Path(__file__).parent.parent / ".env.local",
|
| 18 |
-
]
|
| 19 |
-
|
| 20 |
-
for path in possible_paths:
|
| 21 |
-
if path.exists():
|
| 22 |
-
return path
|
| 23 |
-
|
| 24 |
-
raise FileNotFoundError(
|
| 25 |
-
f"❌ .env.local not found! Checked:\n" +
|
| 26 |
-
"\n".join([f" - {p}" for p in possible_paths])
|
| 27 |
-
)
|
| 28 |
-
|
| 29 |
|
| 30 |
class Settings(BaseSettings):
|
| 31 |
-
"""
|
| 32 |
|
| 33 |
-
#
|
| 34 |
fastapi_env: str
|
| 35 |
-
|
| 36 |
-
api_port: int
|
| 37 |
log_level: str
|
| 38 |
|
| 39 |
-
# LLM
|
| 40 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 41 |
llm_max_tokens: int
|
| 42 |
llm_temperature: float
|
| 43 |
llm_device: str
|
| 44 |
|
| 45 |
-
#
|
| 46 |
-
|
| 47 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 48 |
|
| 49 |
-
#
|
| 50 |
-
|
| 51 |
|
| 52 |
class Config:
|
| 53 |
-
env_file =
|
| 54 |
case_sensitive = False
|
| 55 |
-
extra = "ignore"
|
| 56 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 57 |
|
| 58 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 59 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 60 |
|
| 61 |
-
def get_logger(name: str) -> logging.Logger:
|
| 62 |
-
"""Get configured logger instance"""
|
| 63 |
-
logger = logging.getLogger(name)
|
| 64 |
-
|
| 65 |
-
if not logger.handlers:
|
| 66 |
-
handler = logging.StreamHandler()
|
| 67 |
-
formatter = logging.Formatter(
|
| 68 |
-
'%(asctime)s - %(name)s - %(levelname)s - %(message)s'
|
| 69 |
-
)
|
| 70 |
-
handler.setFormatter(formatter)
|
| 71 |
-
logger.addHandler(handler)
|
| 72 |
-
|
| 73 |
-
logger.setLevel(settings.log_level)
|
| 74 |
-
return logger
|
|
|
|
| 1 |
"""
|
| 2 |
+
Configuration for LLM Data Analyzer
|
| 3 |
+
Supports both MLX (local) and Docker Model Runner modes
|
| 4 |
+
All values from .env.local - NO hardcoded defaults
|
| 5 |
"""
|
|
|
|
|
|
|
|
|
|
| 6 |
import logging
|
| 7 |
+
from functools import lru_cache
|
| 8 |
+
from pydantic_settings import BaseSettings
|
| 9 |
|
| 10 |
+
logger = logging.getLogger(__name__)
|
| 11 |
|
| 12 |
+
# Conditional MLX import
|
| 13 |
+
HAS_MLX = False
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 14 |
|
| 15 |
class Settings(BaseSettings):
|
| 16 |
+
"""Main settings - all from .env.local"""
|
| 17 |
|
| 18 |
+
# ===== CORE SETTINGS =====
|
| 19 |
fastapi_env: str
|
| 20 |
+
fastapi_debug: bool
|
|
|
|
| 21 |
log_level: str
|
| 22 |
|
| 23 |
+
# ===== LLM MODE SELECTION =====
|
| 24 |
+
# True = Use MLX locally (macOS Apple Silicon)
|
| 25 |
+
# False = Use Docker Model Runner
|
| 26 |
+
debug: bool
|
| 27 |
+
|
| 28 |
+
# ===== MLX MODE (DEBUG=true) =====
|
| 29 |
+
llm_model_name_mlx: str
|
| 30 |
llm_max_tokens: int
|
| 31 |
llm_temperature: float
|
| 32 |
llm_device: str
|
| 33 |
|
| 34 |
+
# ===== DOCKER MODEL RUNNER MODE (DEBUG=false) =====
|
| 35 |
+
docker_model_runner_url: str
|
| 36 |
+
llm_model_name_docker: str
|
| 37 |
+
docker_timeout: int
|
| 38 |
+
|
| 39 |
+
# ===== DATA PROCESSING =====
|
| 40 |
+
max_file_size_mb: int
|
| 41 |
|
| 42 |
+
# Hardcoded (lists can't be parsed from env vars)
|
| 43 |
+
supported_file_types: list = ["csv", "xlsx", "xls"]
|
| 44 |
|
| 45 |
class Config:
|
| 46 |
+
env_file = ".env.local"
|
| 47 |
case_sensitive = False
|
|
|
|
| 48 |
|
| 49 |
+
@lru_cache
|
| 50 |
+
def get_settings():
|
| 51 |
+
"""Get cached settings from .env.local"""
|
| 52 |
+
return Settings()
|
| 53 |
|
| 54 |
+
# Check if MLX is available (only needed for DEBUG=true)
|
| 55 |
+
try:
|
| 56 |
+
import mlx.core
|
| 57 |
+
from mlx_lm import load
|
| 58 |
+
from mlx_lm.generate import generate
|
| 59 |
+
HAS_MLX = True
|
| 60 |
+
logger.info("✅ MLX libraries available")
|
| 61 |
+
except ImportError:
|
| 62 |
+
HAS_MLX = False
|
| 63 |
+
logger.warning("⚠️ MLX not available (will use Docker Model Runner or mock)")
|
| 64 |
|
| 65 |
+
settings = get_settings()
|
| 66 |
+
|
| 67 |
+
# Export both settings and MLX availability
|
| 68 |
+
__all__ = ["settings", "get_settings", "HAS_MLX"]
|
| 69 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
backend/app/main.py
CHANGED
|
@@ -1,54 +1,95 @@
|
|
| 1 |
"""
|
| 2 |
-
|
| 3 |
-
|
| 4 |
"""
|
|
|
|
|
|
|
|
|
|
| 5 |
from fastapi import FastAPI
|
| 6 |
from fastapi.middleware.cors import CORSMiddleware
|
| 7 |
-
from contextlib import asynccontextmanager
|
| 8 |
|
| 9 |
-
from .config import settings
|
|
|
|
|
|
|
| 10 |
|
| 11 |
-
logger =
|
|
|
|
| 12 |
|
| 13 |
|
| 14 |
@asynccontextmanager
|
| 15 |
async def lifespan(app: FastAPI):
|
| 16 |
-
"""Manage
|
| 17 |
-
#
|
| 18 |
logger.info("🚀 FastAPI application starting...")
|
| 19 |
logger.info(f"Environment: {settings.fastapi_env}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 20 |
yield
|
| 21 |
-
|
| 22 |
-
|
|
|
|
| 23 |
|
| 24 |
|
| 25 |
# Create FastAPI app
|
| 26 |
app = FastAPI(
|
| 27 |
-
title="LLM Data Analyzer
|
| 28 |
-
description="
|
| 29 |
-
version="0.
|
| 30 |
lifespan=lifespan
|
| 31 |
)
|
| 32 |
|
| 33 |
-
#
|
| 34 |
app.add_middleware(
|
| 35 |
CORSMiddleware,
|
| 36 |
-
allow_origins=
|
| 37 |
allow_credentials=True,
|
| 38 |
allow_methods=["*"],
|
| 39 |
allow_headers=["*"],
|
| 40 |
)
|
| 41 |
|
|
|
|
|
|
|
| 42 |
|
| 43 |
-
|
| 44 |
-
@app.get("/health")
|
| 45 |
-
async def health_check():
|
| 46 |
-
"""Health check endpoint"""
|
| 47 |
-
return {
|
| 48 |
-
"status": "healthy",
|
| 49 |
-
"environment": settings.fastapi_env,
|
| 50 |
-
"service": "llm-data-analyzer-backend"
|
| 51 |
-
}
|
| 52 |
|
| 53 |
|
| 54 |
# Root endpoint
|
|
@@ -57,9 +98,10 @@ async def root():
|
|
| 57 |
"""Root endpoint with API information"""
|
| 58 |
return {
|
| 59 |
"service": "LLM Data Analyzer API",
|
| 60 |
-
"version": "0.
|
| 61 |
"docs_url": "/docs",
|
| 62 |
-
"health_url": "/health"
|
|
|
|
| 63 |
}
|
| 64 |
|
| 65 |
|
|
@@ -67,7 +109,7 @@ if __name__ == "__main__":
|
|
| 67 |
import uvicorn
|
| 68 |
uvicorn.run(
|
| 69 |
"backend.app.main:app",
|
| 70 |
-
host=
|
| 71 |
-
port=
|
| 72 |
reload=settings.fastapi_env == "development"
|
| 73 |
)
|
|
|
|
| 1 |
"""
|
| 2 |
+
FastAPI Application Entry Point
|
| 3 |
+
Supports MLX (local) and Docker Model Runner (containerized) modes
|
| 4 |
"""
|
| 5 |
+
import logging
|
| 6 |
+
from contextlib import asynccontextmanager
|
| 7 |
+
|
| 8 |
from fastapi import FastAPI
|
| 9 |
from fastapi.middleware.cors import CORSMiddleware
|
|
|
|
| 10 |
|
| 11 |
+
from .config import settings
|
| 12 |
+
from .api.v1.router import router, init_services
|
| 13 |
+
from .services.llm_service import get_llm_service
|
| 14 |
|
| 15 |
+
logger = logging.getLogger(__name__)
|
| 16 |
+
logging.basicConfig(level=settings.log_level)
|
| 17 |
|
| 18 |
|
| 19 |
@asynccontextmanager
|
| 20 |
async def lifespan(app: FastAPI):
|
| 21 |
+
"""Manage application lifecycle"""
|
| 22 |
+
# ===== STARTUP =====
|
| 23 |
logger.info("🚀 FastAPI application starting...")
|
| 24 |
logger.info(f"Environment: {settings.fastapi_env}")
|
| 25 |
+
logger.info(f"Debug mode: {settings.debug}")
|
| 26 |
+
|
| 27 |
+
# Get appropriate LLM service based on DEBUG flag
|
| 28 |
+
logger.info("🚀 Initializing LLM service...")
|
| 29 |
+
|
| 30 |
+
mlx_config = {
|
| 31 |
+
"model_name": settings.llm_model_name_mlx,
|
| 32 |
+
"max_tokens": settings.llm_max_tokens,
|
| 33 |
+
"temperature": settings.llm_temperature,
|
| 34 |
+
"device": settings.llm_device
|
| 35 |
+
}
|
| 36 |
+
|
| 37 |
+
docker_config = {
|
| 38 |
+
"model_name": settings.llm_model_name_docker,
|
| 39 |
+
"max_tokens": settings.llm_max_tokens,
|
| 40 |
+
"temperature": settings.llm_temperature,
|
| 41 |
+
"docker_url": settings.docker_model_runner_url,
|
| 42 |
+
"timeout": settings.docker_timeout
|
| 43 |
+
}
|
| 44 |
+
|
| 45 |
+
llm_service = get_llm_service(
|
| 46 |
+
debug=settings.debug,
|
| 47 |
+
mlx_config=mlx_config,
|
| 48 |
+
docker_config=docker_config
|
| 49 |
+
)
|
| 50 |
+
|
| 51 |
+
# Load model/initialize connection
|
| 52 |
+
if await llm_service.load_model():
|
| 53 |
+
logger.info("✅ LLM service ready")
|
| 54 |
+
else:
|
| 55 |
+
logger.warning("⚠️ LLM service initialization failed - will use fallback")
|
| 56 |
+
|
| 57 |
+
# Pass llm_service to router module
|
| 58 |
+
from .api.v1 import router as router_module
|
| 59 |
+
router_module.llm_service = llm_service
|
| 60 |
+
|
| 61 |
+
# Initialize other services
|
| 62 |
+
logger.info("🚀 Initializing data services...")
|
| 63 |
+
await init_services()
|
| 64 |
+
logger.info("✅ All services initialized")
|
| 65 |
+
|
| 66 |
yield
|
| 67 |
+
|
| 68 |
+
# ===== SHUTDOWN =====
|
| 69 |
+
logger.info("🛑 Application shutting down...")
|
| 70 |
|
| 71 |
|
| 72 |
# Create FastAPI app
|
| 73 |
app = FastAPI(
|
| 74 |
+
title="LLM Data Analyzer",
|
| 75 |
+
description="MLX LLM + Data Analysis Backend (Dual-mode: MLX or Docker Model Runner)",
|
| 76 |
+
version="0.2.0",
|
| 77 |
lifespan=lifespan
|
| 78 |
)
|
| 79 |
|
| 80 |
+
# CORS middleware
|
| 81 |
app.add_middleware(
|
| 82 |
CORSMiddleware,
|
| 83 |
+
allow_origins=["*"],
|
| 84 |
allow_credentials=True,
|
| 85 |
allow_methods=["*"],
|
| 86 |
allow_headers=["*"],
|
| 87 |
)
|
| 88 |
|
| 89 |
+
# Include router
|
| 90 |
+
app.include_router(router)
|
| 91 |
|
| 92 |
+
logger.info("✅ FastAPI application configured")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 93 |
|
| 94 |
|
| 95 |
# Root endpoint
|
|
|
|
| 98 |
"""Root endpoint with API information"""
|
| 99 |
return {
|
| 100 |
"service": "LLM Data Analyzer API",
|
| 101 |
+
"version": "0.2.0",
|
| 102 |
"docs_url": "/docs",
|
| 103 |
+
"health_url": "/api/v1/health",
|
| 104 |
+
"mode": "MLX (local)" if settings.debug else "Docker Model Runner"
|
| 105 |
}
|
| 106 |
|
| 107 |
|
|
|
|
| 109 |
import uvicorn
|
| 110 |
uvicorn.run(
|
| 111 |
"backend.app.main:app",
|
| 112 |
+
host="0.0.0.0",
|
| 113 |
+
port=8000,
|
| 114 |
reload=settings.fastapi_env == "development"
|
| 115 |
)
|
backend/app/services/analyzer.py
CHANGED
|
@@ -1,14 +1,250 @@
|
|
| 1 |
-
"""
|
| 2 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3 |
|
| 4 |
-
logger = get_logger(__name__)
|
| 5 |
|
| 6 |
class Analyzer:
|
| 7 |
-
"""
|
| 8 |
|
| 9 |
def __init__(self):
|
| 10 |
-
logger.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 11 |
|
| 12 |
-
|
| 13 |
-
"""
|
| 14 |
-
return
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Data analysis service - statistical, trend, correlation analysis
|
| 3 |
+
"""
|
| 4 |
+
from typing import List, Dict, Any
|
| 5 |
+
import logging
|
| 6 |
+
import statistics
|
| 7 |
+
|
| 8 |
+
try:
|
| 9 |
+
import numpy as np
|
| 10 |
+
import pandas as pd
|
| 11 |
+
from scipy.stats import skew, kurtosis
|
| 12 |
+
HAS_SCIPY = True
|
| 13 |
+
except ImportError:
|
| 14 |
+
HAS_SCIPY = False
|
| 15 |
|
|
|
|
| 16 |
|
| 17 |
class Analyzer:
|
| 18 |
+
"""Perform statistical and trend analysis on data"""
|
| 19 |
|
| 20 |
def __init__(self):
|
| 21 |
+
self.logger = logging.getLogger(__name__)
|
| 22 |
+
|
| 23 |
+
def analyze(self, data: List[Dict[str, Any]], analysis_type: str, columns: List[str] = None) -> Dict[str, Any]:
|
| 24 |
+
"""Dispatch to appropriate analysis method"""
|
| 25 |
+
analysis_type = analysis_type.lower()
|
| 26 |
+
|
| 27 |
+
if analysis_type == "statistical":
|
| 28 |
+
return self.statistical_analysis(data, columns)
|
| 29 |
+
elif analysis_type == "correlation":
|
| 30 |
+
return self.correlation_analysis(data, columns)
|
| 31 |
+
elif analysis_type == "trend":
|
| 32 |
+
return self.trend_analysis(data, columns)
|
| 33 |
+
elif analysis_type == "outliers":
|
| 34 |
+
return self.outlier_analysis(data, columns)
|
| 35 |
+
elif analysis_type == "distribution":
|
| 36 |
+
return self.distribution_analysis(data, columns)
|
| 37 |
+
elif analysis_type == "summary":
|
| 38 |
+
return self.summary_analysis(data)
|
| 39 |
+
else:
|
| 40 |
+
raise ValueError(f"Unknown analysis type: {analysis_type}")
|
| 41 |
+
|
| 42 |
+
def statistical_analysis(self, data: List[Dict[str, Any]], columns: List[str] = None) -> Dict[str, Any]:
|
| 43 |
+
"""Statistical analysis - mean, median, std, min, max"""
|
| 44 |
+
try:
|
| 45 |
+
numeric_cols = columns or self._get_numeric_columns(data)
|
| 46 |
+
results = {
|
| 47 |
+
"mean": {},
|
| 48 |
+
"median": {},
|
| 49 |
+
"std_dev": {},
|
| 50 |
+
"min": {},
|
| 51 |
+
"max": {},
|
| 52 |
+
"count": len(data)
|
| 53 |
+
}
|
| 54 |
+
|
| 55 |
+
for col in numeric_cols:
|
| 56 |
+
values = [row[col] for row in data if row[col] is not None]
|
| 57 |
+
if values:
|
| 58 |
+
results["mean"][col] = round(statistics.mean(values), 2)
|
| 59 |
+
results["median"][col] = round(statistics.median(values), 2)
|
| 60 |
+
if len(values) > 1:
|
| 61 |
+
results["std_dev"][col] = round(statistics.stdev(values), 2)
|
| 62 |
+
results["min"][col] = round(min(values), 2)
|
| 63 |
+
results["max"][col] = round(max(values), 2)
|
| 64 |
+
|
| 65 |
+
return results
|
| 66 |
+
except Exception as e:
|
| 67 |
+
self.logger.error(f"Statistical analysis failed: {e}")
|
| 68 |
+
raise
|
| 69 |
+
|
| 70 |
+
def correlation_analysis(self, data: List[Dict[str, Any]], columns: List[str] = None) -> Dict[str, Any]:
|
| 71 |
+
"""Correlation analysis between numeric columns"""
|
| 72 |
+
try:
|
| 73 |
+
if not HAS_SCIPY:
|
| 74 |
+
raise RuntimeError("pandas and scipy required for correlation analysis")
|
| 75 |
+
|
| 76 |
+
df = pd.DataFrame(data)
|
| 77 |
+
numeric_cols = columns or df.select_dtypes(include=[np.number]).columns.tolist()
|
| 78 |
+
|
| 79 |
+
corr_matrix = df[numeric_cols].corr().round(2)
|
| 80 |
+
|
| 81 |
+
# Find significant correlations
|
| 82 |
+
significant_pairs = []
|
| 83 |
+
for i, col1 in enumerate(numeric_cols):
|
| 84 |
+
for col2 in numeric_cols[i+1:]:
|
| 85 |
+
corr_value = corr_matrix.loc[col1, col2]
|
| 86 |
+
if abs(corr_value) > 0.5: # Threshold
|
| 87 |
+
significant_pairs.append({
|
| 88 |
+
"col1": col1,
|
| 89 |
+
"col2": col2,
|
| 90 |
+
"correlation": float(corr_value)
|
| 91 |
+
})
|
| 92 |
+
|
| 93 |
+
return {
|
| 94 |
+
"matrix": corr_matrix.to_dict(),
|
| 95 |
+
"significant_pairs": significant_pairs
|
| 96 |
+
}
|
| 97 |
+
except Exception as e:
|
| 98 |
+
self.logger.error(f"Correlation analysis failed: {e}")
|
| 99 |
+
raise
|
| 100 |
+
|
| 101 |
+
def trend_analysis(self, data: List[Dict[str, Any]], columns: List[str] = None) -> Dict[str, Any]:
|
| 102 |
+
"""Trend analysis - increasing, decreasing, stable"""
|
| 103 |
+
try:
|
| 104 |
+
numeric_cols = columns or self._get_numeric_columns(data)
|
| 105 |
+
trends = {}
|
| 106 |
+
trend_strength = {}
|
| 107 |
+
|
| 108 |
+
for col in numeric_cols:
|
| 109 |
+
values = [row[col] for row in data if row[col] is not None]
|
| 110 |
+
if len(values) > 2:
|
| 111 |
+
# Simple trend: compare first half vs second half
|
| 112 |
+
mid = len(values) // 2
|
| 113 |
+
first_half_avg = statistics.mean(values[:mid])
|
| 114 |
+
second_half_avg = statistics.mean(values[mid:])
|
| 115 |
+
|
| 116 |
+
if second_half_avg > first_half_avg * 1.05:
|
| 117 |
+
trends[col] = "increasing"
|
| 118 |
+
strength = (second_half_avg - first_half_avg) / first_half_avg
|
| 119 |
+
elif second_half_avg < first_half_avg * 0.95:
|
| 120 |
+
trends[col] = "decreasing"
|
| 121 |
+
strength = (first_half_avg - second_half_avg) / first_half_avg
|
| 122 |
+
else:
|
| 123 |
+
trends[col] = "stable"
|
| 124 |
+
strength = 0.0
|
| 125 |
+
|
| 126 |
+
trend_strength[col] = round(strength, 2)
|
| 127 |
+
|
| 128 |
+
return {
|
| 129 |
+
"trends": trends,
|
| 130 |
+
"trend_strength": trend_strength
|
| 131 |
+
}
|
| 132 |
+
except Exception as e:
|
| 133 |
+
self.logger.error(f"Trend analysis failed: {e}")
|
| 134 |
+
raise
|
| 135 |
+
|
| 136 |
+
def outlier_analysis(self, data: List[Dict[str, Any]], columns: List[str] = None) -> Dict[str, Any]:
|
| 137 |
+
"""Outlier detection using IQR method"""
|
| 138 |
+
try:
|
| 139 |
+
numeric_cols = columns or self._get_numeric_columns(data)
|
| 140 |
+
outliers = {}
|
| 141 |
+
total_outliers = 0
|
| 142 |
+
|
| 143 |
+
for col in numeric_cols:
|
| 144 |
+
values = sorted([row[col] for row in data if row[col] is not None])
|
| 145 |
+
if len(values) > 4:
|
| 146 |
+
q1 = values[len(values) // 4]
|
| 147 |
+
q3 = values[3 * len(values) // 4]
|
| 148 |
+
iqr = q3 - q1
|
| 149 |
+
lower_bound = q1 - 1.5 * iqr
|
| 150 |
+
upper_bound = q3 + 1.5 * iqr
|
| 151 |
+
|
| 152 |
+
col_outliers = [v for v in values if v < lower_bound or v > upper_bound]
|
| 153 |
+
outliers[col] = col_outliers
|
| 154 |
+
total_outliers += len(col_outliers)
|
| 155 |
+
|
| 156 |
+
return {
|
| 157 |
+
"outliers": outliers,
|
| 158 |
+
"outlier_count": total_outliers,
|
| 159 |
+
"outlier_percentage": round((total_outliers / len(data)) * 100, 2) if data else 0
|
| 160 |
+
}
|
| 161 |
+
except Exception as e:
|
| 162 |
+
self.logger.error(f"Outlier analysis failed: {e}")
|
| 163 |
+
raise
|
| 164 |
+
|
| 165 |
+
def distribution_analysis(self, data: List[Dict[str, Any]], columns: List[str] = None) -> Dict[str, Any]:
|
| 166 |
+
"""Distribution analysis - skewness, kurtosis"""
|
| 167 |
+
try:
|
| 168 |
+
if not HAS_SCIPY:
|
| 169 |
+
return {"error": "scipy required for distribution analysis"}
|
| 170 |
+
|
| 171 |
+
numeric_cols = columns or self._get_numeric_columns(data)
|
| 172 |
+
distributions = {}
|
| 173 |
+
skewness = {}
|
| 174 |
+
kurt = {}
|
| 175 |
+
|
| 176 |
+
for col in numeric_cols:
|
| 177 |
+
values = [row[col] for row in data if row[col] is not None]
|
| 178 |
+
if len(values) > 2:
|
| 179 |
+
distributions[col] = {
|
| 180 |
+
"min": round(min(values), 2),
|
| 181 |
+
"max": round(max(values), 2),
|
| 182 |
+
"range": round(max(values) - min(values), 2)
|
| 183 |
+
}
|
| 184 |
+
skewness[col] = round(float(skew(values)), 2)
|
| 185 |
+
kurt[col] = round(float(kurtosis(values)), 2)
|
| 186 |
+
|
| 187 |
+
return {
|
| 188 |
+
"distributions": distributions,
|
| 189 |
+
"skewness": skewness,
|
| 190 |
+
"kurtosis": kurt
|
| 191 |
+
}
|
| 192 |
+
except Exception as e:
|
| 193 |
+
self.logger.error(f"Distribution analysis failed: {e}")
|
| 194 |
+
raise
|
| 195 |
+
|
| 196 |
+
def summary_analysis(self, data: List[Dict[str, Any]]) -> Dict[str, Any]:
|
| 197 |
+
"""Summary of data"""
|
| 198 |
+
try:
|
| 199 |
+
if not data:
|
| 200 |
+
return {"error": "No data"}
|
| 201 |
+
|
| 202 |
+
cols = list(data.keys())
|
| 203 |
+
return {
|
| 204 |
+
"total_rows": len(data),
|
| 205 |
+
"total_columns": len(cols),
|
| 206 |
+
"columns": cols,
|
| 207 |
+
"data_types": self._infer_types(data)
|
| 208 |
+
}
|
| 209 |
+
except Exception as e:
|
| 210 |
+
self.logger.error(f"Summary analysis failed: {e}")
|
| 211 |
+
raise
|
| 212 |
+
|
| 213 |
+
@staticmethod
|
| 214 |
+
def _get_numeric_columns(data: List[Dict[str, Any]]) -> List[str]:
|
| 215 |
+
"""Get numeric columns"""
|
| 216 |
+
if not data:
|
| 217 |
+
return []
|
| 218 |
+
|
| 219 |
+
numeric = []
|
| 220 |
+
for key in data.keys():
|
| 221 |
+
try:
|
| 222 |
+
for row in data:
|
| 223 |
+
if row[key] is not None:
|
| 224 |
+
float(row[key])
|
| 225 |
+
numeric.append(key)
|
| 226 |
+
except (ValueError, TypeError):
|
| 227 |
+
pass
|
| 228 |
+
return numeric
|
| 229 |
+
|
| 230 |
+
@staticmethod
|
| 231 |
+
def _infer_types(data: List[Dict[str, Any]]) -> Dict[str, str]:
|
| 232 |
+
"""Infer column data types"""
|
| 233 |
+
types = {}
|
| 234 |
+
if not data:
|
| 235 |
+
return types
|
| 236 |
+
|
| 237 |
+
for key in data.keys():
|
| 238 |
+
try:
|
| 239 |
+
for row in data:
|
| 240 |
+
if row[key] is not None:
|
| 241 |
+
float(row[key])
|
| 242 |
+
types[key] = "numeric"
|
| 243 |
+
except (ValueError, TypeError):
|
| 244 |
+
types[key] = "string"
|
| 245 |
+
|
| 246 |
+
return types
|
| 247 |
|
| 248 |
+
def generate_summary(self, results: Dict[str, Any]) -> str:
|
| 249 |
+
"""Generate human-readable summary"""
|
| 250 |
+
return f"Analysis completed with {len(results)} metrics."
|
backend/app/services/data_processor.py
CHANGED
|
@@ -1,14 +1,120 @@
|
|
| 1 |
-
"""
|
| 2 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3 |
|
| 4 |
-
logger = get_logger(__name__)
|
| 5 |
|
| 6 |
class DataProcessor:
|
| 7 |
-
"""
|
| 8 |
|
| 9 |
def __init__(self):
|
| 10 |
-
logger.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 11 |
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Data file processing - CSV and Excel support
|
| 3 |
+
"""
|
| 4 |
+
import csv
|
| 5 |
+
import io
|
| 6 |
+
from typing import List, Dict, Any
|
| 7 |
+
from fastapi import UploadFile
|
| 8 |
+
import logging
|
| 9 |
+
|
| 10 |
+
try:
|
| 11 |
+
import openpyxl
|
| 12 |
+
HAS_OPENPYXL = True
|
| 13 |
+
except ImportError:
|
| 14 |
+
HAS_OPENPYXL = False
|
| 15 |
|
|
|
|
| 16 |
|
| 17 |
class DataProcessor:
|
| 18 |
+
"""Process uploaded data files"""
|
| 19 |
|
| 20 |
def __init__(self):
|
| 21 |
+
self.logger = logging.getLogger(__name__)
|
| 22 |
+
self.max_file_size = 10 * 1024 * 1024 # 10 MB
|
| 23 |
+
|
| 24 |
+
async def process_file(self, file: UploadFile) -> tuple[List[Dict[str, Any]], str]:
|
| 25 |
+
"""Process uploaded file - returns (data, file_type)"""
|
| 26 |
+
content = await file.read()
|
| 27 |
+
|
| 28 |
+
if len(content) > self.max_file_size:
|
| 29 |
+
raise ValueError(f"File too large. Max: {self.max_file_size / 1024 / 1024:.1f} MB")
|
| 30 |
+
|
| 31 |
+
if file.filename.endswith('.csv'):
|
| 32 |
+
return self._process_csv(content), 'csv'
|
| 33 |
+
elif file.filename.endswith('.xlsx') or file.filename.endswith('.xls'):
|
| 34 |
+
return self._process_excel(content), 'excel'
|
| 35 |
+
else:
|
| 36 |
+
raise ValueError(f"Unsupported file format: {file.filename}")
|
| 37 |
+
|
| 38 |
+
def _process_csv(self, content: bytes) -> List[Dict[str, Any]]:
|
| 39 |
+
"""Process CSV file"""
|
| 40 |
+
try:
|
| 41 |
+
text_content = content.decode('utf-8')
|
| 42 |
+
reader = csv.DictReader(io.StringIO(text_content))
|
| 43 |
+
data = []
|
| 44 |
+
for row in reader:
|
| 45 |
+
# Convert numeric strings to numbers
|
| 46 |
+
processed_row = {}
|
| 47 |
+
for key, value in row.items():
|
| 48 |
+
processed_row[key] = self._try_convert_to_number(value)
|
| 49 |
+
data.append(processed_row)
|
| 50 |
+
|
| 51 |
+
if not data:
|
| 52 |
+
raise ValueError("CSV file is empty")
|
| 53 |
+
|
| 54 |
+
self.logger.info(f"✅ Processed CSV: {len(data)} rows")
|
| 55 |
+
return data
|
| 56 |
+
except Exception as e:
|
| 57 |
+
self.logger.error(f"❌ CSV processing failed: {e}")
|
| 58 |
+
raise
|
| 59 |
+
|
| 60 |
+
def _process_excel(self, content: bytes) -> List[Dict[str, Any]]:
|
| 61 |
+
"""Process Excel file"""
|
| 62 |
+
try:
|
| 63 |
+
if not HAS_OPENPYXL:
|
| 64 |
+
raise RuntimeError("openpyxl not installed. Install with: uv add openpyxl")
|
| 65 |
+
|
| 66 |
+
workbook = openpyxl.load_workbook(io.BytesIO(content))
|
| 67 |
+
sheet = workbook.active
|
| 68 |
+
|
| 69 |
+
# Get headers
|
| 70 |
+
headers = [cell.value for cell in sheet]
|
| 71 |
+
|
| 72 |
+
# Get data
|
| 73 |
+
data = []
|
| 74 |
+
for row in sheet.iter_rows(min_row=2, values_only=True):
|
| 75 |
+
if any(cell is not None for cell in row):
|
| 76 |
+
row_dict = {}
|
| 77 |
+
for header, value in zip(headers, row):
|
| 78 |
+
row_dict[header] = value
|
| 79 |
+
data.append(row_dict)
|
| 80 |
+
|
| 81 |
+
if not data:
|
| 82 |
+
raise ValueError("Excel file is empty")
|
| 83 |
+
|
| 84 |
+
self.logger.info(f"✅ Processed Excel: {len(data)} rows")
|
| 85 |
+
return data
|
| 86 |
+
except Exception as e:
|
| 87 |
+
self.logger.error(f"❌ Excel processing failed: {e}")
|
| 88 |
+
raise
|
| 89 |
+
|
| 90 |
+
@staticmethod
|
| 91 |
+
def _try_convert_to_number(value: str) -> Any:
|
| 92 |
+
"""Try converting string to int or float"""
|
| 93 |
+
if value is None or value == "":
|
| 94 |
+
return None
|
| 95 |
+
|
| 96 |
+
try:
|
| 97 |
+
if "." in str(value):
|
| 98 |
+
return float(value)
|
| 99 |
+
else:
|
| 100 |
+
return int(value)
|
| 101 |
+
except (ValueError, TypeError):
|
| 102 |
+
return value
|
| 103 |
|
| 104 |
+
@staticmethod
|
| 105 |
+
def get_numeric_columns(data: List[Dict[str, Any]]) -> List[str]:
|
| 106 |
+
"""Get columns that contain numeric data"""
|
| 107 |
+
if not data:
|
| 108 |
+
return []
|
| 109 |
+
|
| 110 |
+
numeric_cols = []
|
| 111 |
+
for key in data.keys():
|
| 112 |
+
try:
|
| 113 |
+
for row in data:
|
| 114 |
+
if row[key] is not None:
|
| 115 |
+
float(row[key])
|
| 116 |
+
numeric_cols.append(key)
|
| 117 |
+
except (ValueError, TypeError):
|
| 118 |
+
pass
|
| 119 |
+
|
| 120 |
+
return numeric_cols
|
backend/app/services/llm_service.py
CHANGED
|
@@ -1,17 +1,281 @@
|
|
| 1 |
-
"""
|
| 2 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3 |
|
| 4 |
-
logger =
|
| 5 |
|
| 6 |
-
|
| 7 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 8 |
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
logger.info("LLMService initialized (Phase 2 will load actual model)")
|
| 13 |
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Dual-mode LLM Service
|
| 3 |
+
- DEBUG=true: Uses MLX with Apple Silicon GPU
|
| 4 |
+
- DEBUG=false: Uses Docker Model Runner (OpenAI-compatible API)
|
| 5 |
+
- Fallback: Mock mode if neither available
|
| 6 |
+
"""
|
| 7 |
+
import asyncio
|
| 8 |
+
import logging
|
| 9 |
+
from abc import ABC, abstractmethod
|
| 10 |
+
from typing import List, Optional
|
| 11 |
+
import httpx
|
| 12 |
|
| 13 |
+
logger = logging.getLogger(__name__)
|
| 14 |
|
| 15 |
+
# Import MLX conditionally
|
| 16 |
+
try:
|
| 17 |
+
from mlx_lm import load
|
| 18 |
+
from mlx_lm.generate import generate
|
| 19 |
+
HAS_MLX = True
|
| 20 |
+
except ImportError:
|
| 21 |
+
HAS_MLX = False
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
class BaseLLMService(ABC):
|
| 25 |
+
"""Abstract base class for LLM services"""
|
| 26 |
+
|
| 27 |
+
def __init__(self, model_name: str, max_tokens: int, temperature: float):
|
| 28 |
+
self.model_name = model_name
|
| 29 |
+
self.max_tokens = max_tokens
|
| 30 |
+
self.temperature = temperature
|
| 31 |
+
self.is_loaded = False
|
| 32 |
+
self.is_mock = False
|
| 33 |
+
self.logger = logging.getLogger(__name__)
|
| 34 |
+
|
| 35 |
+
@abstractmethod
|
| 36 |
+
async def load_model(self) -> bool:
|
| 37 |
+
"""Load/initialize the model"""
|
| 38 |
+
pass
|
| 39 |
+
|
| 40 |
+
@abstractmethod
|
| 41 |
+
async def generate(self, prompt: str) -> str:
|
| 42 |
+
"""Generate text from prompt"""
|
| 43 |
+
pass
|
| 44 |
+
|
| 45 |
+
async def chat(self, messages: List[dict], system_prompt: str = None) -> str:
|
| 46 |
+
"""Chat interface"""
|
| 47 |
+
prompt = self._build_prompt(messages, system_prompt)
|
| 48 |
+
return await self.generate(prompt)
|
| 49 |
+
|
| 50 |
+
def _build_prompt(self, messages: List[dict], system_prompt: str = None) -> str:
|
| 51 |
+
"""Build prompt from chat messages"""
|
| 52 |
+
prompt_parts = []
|
| 53 |
+
|
| 54 |
+
if system_prompt:
|
| 55 |
+
prompt_parts.append(f"System: {system_prompt}\n\n")
|
| 56 |
+
|
| 57 |
+
for msg in messages:
|
| 58 |
+
role = msg.get("role", "user")
|
| 59 |
+
content = msg.get("content", "")
|
| 60 |
+
prompt_parts.append(f"{role.capitalize()}: {content}\n")
|
| 61 |
+
|
| 62 |
+
prompt_parts.append("Assistant: ")
|
| 63 |
+
return "".join(prompt_parts)
|
| 64 |
+
|
| 65 |
+
|
| 66 |
+
class LLMServiceMLX(BaseLLMService):
|
| 67 |
+
"""MLX implementation for Apple Silicon (DEBUG=true)"""
|
| 68 |
+
|
| 69 |
+
def __init__(self, model_name: str, max_tokens: int, temperature: float, device: str):
|
| 70 |
+
super().__init__(model_name, max_tokens, temperature)
|
| 71 |
+
self.device = device
|
| 72 |
+
self.model = None
|
| 73 |
+
self.tokenizer = None
|
| 74 |
+
|
| 75 |
+
async def load_model(self) -> bool:
|
| 76 |
+
"""Load MLX model"""
|
| 77 |
+
if self.is_loaded:
|
| 78 |
+
return True
|
| 79 |
+
|
| 80 |
+
if not HAS_MLX:
|
| 81 |
+
self.logger.error("❌ MLX not available")
|
| 82 |
+
return False
|
| 83 |
+
|
| 84 |
+
try:
|
| 85 |
+
self.logger.info(f"🔄 Loading MLX model: {self.model_name}")
|
| 86 |
+
loop = asyncio.get_event_loop()
|
| 87 |
+
self.model, self.tokenizer = await loop.run_in_executor(
|
| 88 |
+
None,
|
| 89 |
+
self._load_model_sync
|
| 90 |
+
)
|
| 91 |
+
self.is_loaded = True
|
| 92 |
+
self.logger.info(f"✅ MLX model loaded: {self.model_name}")
|
| 93 |
+
return True
|
| 94 |
+
except Exception as e:
|
| 95 |
+
self.logger.error(f"❌ MLX model loading failed: {e}")
|
| 96 |
+
return False
|
| 97 |
+
|
| 98 |
+
def _load_model_sync(self):
|
| 99 |
+
"""Synchronous MLX model loading"""
|
| 100 |
+
if not HAS_MLX:
|
| 101 |
+
raise RuntimeError("MLX not installed")
|
| 102 |
+
|
| 103 |
+
self.logger.info("🔄 Starting model download/load...")
|
| 104 |
+
model, tokenizer = load(self.model_name)
|
| 105 |
+
self.logger.info("✅ Model download/load complete")
|
| 106 |
+
return model, tokenizer
|
| 107 |
+
|
| 108 |
+
async def generate(self, prompt: str) -> str:
|
| 109 |
+
"""Generate with MLX"""
|
| 110 |
+
if not self.is_loaded:
|
| 111 |
+
raise RuntimeError("Model not loaded")
|
| 112 |
+
|
| 113 |
+
try:
|
| 114 |
+
loop = asyncio.get_event_loop()
|
| 115 |
+
response = await loop.run_in_executor(
|
| 116 |
+
None,
|
| 117 |
+
self._generate_sync,
|
| 118 |
+
prompt
|
| 119 |
+
)
|
| 120 |
+
return response
|
| 121 |
+
except Exception as e:
|
| 122 |
+
self.logger.error(f"❌ MLX generation failed: {e}")
|
| 123 |
+
raise
|
| 124 |
+
|
| 125 |
+
def _generate_sync(self, prompt: str) -> str:
|
| 126 |
+
"""Synchronous text generation with MLX"""
|
| 127 |
+
response = generate(
|
| 128 |
+
model=self.model,
|
| 129 |
+
tokenizer=self.tokenizer,
|
| 130 |
+
prompt=prompt,
|
| 131 |
+
max_tokens=self.max_tokens,
|
| 132 |
+
temperature=self.temperature,
|
| 133 |
+
verbose=False
|
| 134 |
+
)
|
| 135 |
+
return response
|
| 136 |
+
|
| 137 |
+
|
| 138 |
+
class LLMServiceDockerModelRunner(BaseLLMService):
|
| 139 |
+
"""Docker Model Runner implementation (DEBUG=false)"""
|
| 140 |
+
|
| 141 |
+
def __init__(self, model_name: str, max_tokens: int, temperature: float, docker_url: str, timeout: int = 300):
|
| 142 |
+
super().__init__(model_name, max_tokens, temperature)
|
| 143 |
+
self.docker_url = docker_url
|
| 144 |
+
self.timeout = timeout
|
| 145 |
+
self.client = None
|
| 146 |
+
|
| 147 |
+
async def load_model(self) -> bool:
|
| 148 |
+
"""Initialize Docker Model Runner connection"""
|
| 149 |
+
if self.is_loaded:
|
| 150 |
+
return True
|
| 151 |
+
|
| 152 |
+
try:
|
| 153 |
+
self.logger.info(f"🔄 Connecting to Docker Model Runner: {self.docker_url}")
|
| 154 |
+
# Create async HTTP client
|
| 155 |
+
self.client = httpx.AsyncClient(timeout=self.timeout)
|
| 156 |
+
|
| 157 |
+
# Test connection with health check
|
| 158 |
+
response = await self.client.get(f"{self.docker_url}/models")
|
| 159 |
+
|
| 160 |
+
if response.status_code == 200:
|
| 161 |
+
self.is_loaded = True
|
| 162 |
+
self.logger.info(f"✅ Docker Model Runner connected")
|
| 163 |
+
return True
|
| 164 |
+
else:
|
| 165 |
+
self.logger.error(f"❌ Docker Model Runner returned {response.status_code}")
|
| 166 |
+
return False
|
| 167 |
+
except Exception as e:
|
| 168 |
+
self.logger.error(f"❌ Docker Model Runner connection failed: {e}")
|
| 169 |
+
return False
|
| 170 |
+
|
| 171 |
+
async def generate(self, prompt: str) -> str:
|
| 172 |
+
"""Generate with Docker Model Runner (OpenAI-compatible API)"""
|
| 173 |
+
if not self.is_loaded:
|
| 174 |
+
raise RuntimeError("Docker Model Runner not connected")
|
| 175 |
+
|
| 176 |
+
try:
|
| 177 |
+
payload = {
|
| 178 |
+
"model": self.model_name,
|
| 179 |
+
"messages": [{"role": "user", "content": prompt}],
|
| 180 |
+
"temperature": self.temperature,
|
| 181 |
+
"max_tokens": self.max_tokens,
|
| 182 |
+
}
|
| 183 |
+
|
| 184 |
+
response = await self.client.post(
|
| 185 |
+
f"{self.docker_url}/chat/completions",
|
| 186 |
+
json=payload
|
| 187 |
+
)
|
| 188 |
+
|
| 189 |
+
if response.status_code == 200:
|
| 190 |
+
result = response.json()
|
| 191 |
+
return result["choices"]["message"]["content"]
|
| 192 |
+
else:
|
| 193 |
+
self.logger.error(f"❌ Docker Model Runner error: {response.text}")
|
| 194 |
+
raise RuntimeError(f"Model Runner error: {response.status_code}")
|
| 195 |
+
except Exception as e:
|
| 196 |
+
self.logger.error(f"❌ Docker Model Runner generation failed: {e}")
|
| 197 |
+
raise
|
| 198 |
+
|
| 199 |
+
async def __aenter__(self):
|
| 200 |
+
return self
|
| 201 |
+
|
| 202 |
+
async def __aexit__(self, exc_type, exc_val, exc_tb):
|
| 203 |
+
if self.client:
|
| 204 |
+
await self.client.aclose()
|
| 205 |
+
|
| 206 |
+
|
| 207 |
+
class LLMServiceMock(BaseLLMService):
|
| 208 |
+
"""Mock implementation as fallback"""
|
| 209 |
+
|
| 210 |
+
def __init__(self, model_name: str, max_tokens: int, temperature: float):
|
| 211 |
+
super().__init__(model_name, max_tokens, temperature)
|
| 212 |
+
self.is_mock = True
|
| 213 |
+
|
| 214 |
+
async def load_model(self) -> bool:
|
| 215 |
+
"""Mock loading"""
|
| 216 |
+
self.logger.warning("⚠️ Using MOCK mode (no real LLM available)")
|
| 217 |
+
self.is_loaded = True
|
| 218 |
+
return True
|
| 219 |
+
|
| 220 |
+
async def generate(self, prompt: str) -> str:
|
| 221 |
+
"""Generate mock response"""
|
| 222 |
+
return self._generate_mock_response(prompt)
|
| 223 |
+
|
| 224 |
+
def _generate_mock_response(self, prompt: str) -> str:
|
| 225 |
+
"""Generate intelligent mock responses"""
|
| 226 |
+
prompt_lower = prompt.lower()
|
| 227 |
+
|
| 228 |
+
if "hello" in prompt_lower or "hi" in prompt_lower:
|
| 229 |
+
return "Hello! I'm running in mock mode (no LLM available). I can still help you analyze CSV and Excel files!"
|
| 230 |
+
elif "analyze" in prompt_lower or "data" in prompt_lower:
|
| 231 |
+
return "I can analyze your data with statistical analysis, trend detection, outlier detection, and correlation matrices."
|
| 232 |
+
elif "what can" in prompt_lower or "help" in prompt_lower:
|
| 233 |
+
return "I can help with: 1) Chatting, 2) Uploading files (CSV/Excel), 3) Statistical analysis, 4) Trend detection, 5) Anomaly detection."
|
| 234 |
+
elif "machine learning" in prompt_lower:
|
| 235 |
+
return "Machine learning is about creating algorithms that can learn from data and make predictions without being explicitly programmed."
|
| 236 |
+
else:
|
| 237 |
+
return f"Mock response: I processed your prompt about '{prompt[:40]}...' - please note I'm in mock mode with no real LLM."
|
| 238 |
+
|
| 239 |
+
|
| 240 |
+
def get_llm_service(debug: bool, mlx_config: dict = None, docker_config: dict = None) -> BaseLLMService:
|
| 241 |
+
"""
|
| 242 |
+
Factory function to get appropriate LLM service
|
| 243 |
+
|
| 244 |
+
Args:
|
| 245 |
+
debug: If True, use MLX; if False, use Docker Model Runner
|
| 246 |
+
mlx_config: Config dict for MLX (model_name, max_tokens, temperature, device)
|
| 247 |
+
docker_config: Config dict for Docker Model Runner (model_name, max_tokens, temperature, url, timeout)
|
| 248 |
|
| 249 |
+
Returns:
|
| 250 |
+
Appropriate LLM service instance
|
| 251 |
+
"""
|
|
|
|
| 252 |
|
| 253 |
+
if debug:
|
| 254 |
+
# Try MLX first
|
| 255 |
+
if HAS_MLX:
|
| 256 |
+
config = mlx_config or {
|
| 257 |
+
"model_name": "mlx-community/Llama-3.2-3B-Instruct-4bit",
|
| 258 |
+
"max_tokens": 512,
|
| 259 |
+
"temperature": 0.7,
|
| 260 |
+
"device": "auto"
|
| 261 |
+
}
|
| 262 |
+
logger.info("📌 Mode: MLX (DEBUG=true)")
|
| 263 |
+
return LLMServiceMLX(**config)
|
| 264 |
+
else:
|
| 265 |
+
logger.warning("⚠️ MLX not available, falling back to mock")
|
| 266 |
+
return LLMServiceMock(
|
| 267 |
+
model_name="mock-mlx",
|
| 268 |
+
max_tokens=512,
|
| 269 |
+
temperature=0.7
|
| 270 |
+
)
|
| 271 |
+
else:
|
| 272 |
+
# Use Docker Model Runner
|
| 273 |
+
config = docker_config or {
|
| 274 |
+
"model_name": "Llama-3.2-3B-Instruct",
|
| 275 |
+
"max_tokens": 512,
|
| 276 |
+
"temperature": 0.7,
|
| 277 |
+
"docker_url": "http://model-runner.docker.internal/engines/llama.cpp/v1",
|
| 278 |
+
"timeout": 300
|
| 279 |
+
}
|
| 280 |
+
logger.info("📌 Mode: Docker Model Runner (DEBUG=false)")
|
| 281 |
+
return LLMServiceDockerModelRunner(**config)
|
backend/app/services/ml_suggester.py
CHANGED
|
@@ -1,14 +1,151 @@
|
|
| 1 |
-
"""
|
| 2 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3 |
|
| 4 |
-
logger = get_logger(__name__)
|
| 5 |
|
| 6 |
class MLSuggester:
|
| 7 |
-
"""
|
| 8 |
|
| 9 |
def __init__(self):
|
| 10 |
-
logger.
|
| 11 |
|
| 12 |
-
|
| 13 |
-
"""
|
| 14 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
ML suggestions - anomaly detection and insights
|
| 3 |
+
"""
|
| 4 |
+
from typing import List, Dict, Any
|
| 5 |
+
import logging
|
| 6 |
+
import statistics
|
| 7 |
|
|
|
|
| 8 |
|
| 9 |
class MLSuggester:
|
| 10 |
+
"""Generate ML-based suggestions from data"""
|
| 11 |
|
| 12 |
def __init__(self):
|
| 13 |
+
self.logger = logging.getLogger(__name__)
|
| 14 |
|
| 15 |
+
def generate(self, data: List[Dict[str, Any]], context: str = None) -> List[Dict[str, Any]]:
|
| 16 |
+
"""Generate ML suggestions"""
|
| 17 |
+
suggestions = []
|
| 18 |
+
|
| 19 |
+
if not data:
|
| 20 |
+
return suggestions
|
| 21 |
+
|
| 22 |
+
# Detect missing values
|
| 23 |
+
suggestions.extend(self._check_missing_values(data))
|
| 24 |
+
|
| 25 |
+
# Detect outliers
|
| 26 |
+
suggestions.extend(self._detect_outliers(data))
|
| 27 |
+
|
| 28 |
+
# Detect imbalances
|
| 29 |
+
suggestions.extend(self._detect_imbalances(data))
|
| 30 |
+
|
| 31 |
+
# Detect data quality issues
|
| 32 |
+
suggestions.extend(self._detect_quality_issues(data))
|
| 33 |
+
|
| 34 |
+
# Sort by confidence
|
| 35 |
+
suggestions.sort(key=lambda x: x["confidence"], reverse=True)
|
| 36 |
+
|
| 37 |
+
return suggestions[:10] # Top 10 suggestions
|
| 38 |
+
|
| 39 |
+
def _check_missing_values(self, data: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
|
| 40 |
+
"""Check for missing values"""
|
| 41 |
+
suggestions = []
|
| 42 |
+
|
| 43 |
+
for col in data.keys():
|
| 44 |
+
missing_count = sum(1 for row in data if row[col] is None or row[col] == "")
|
| 45 |
+
if missing_count > 0:
|
| 46 |
+
percentage = (missing_count / len(data)) * 100
|
| 47 |
+
|
| 48 |
+
if percentage > 50:
|
| 49 |
+
suggestions.append({
|
| 50 |
+
"title": f"High Missing Values in {col}",
|
| 51 |
+
"description": f"{percentage:.1f}% of {col} is missing. Consider data imputation or removal.",
|
| 52 |
+
"confidence": min(0.95, percentage / 100),
|
| 53 |
+
"action": "impute_or_remove",
|
| 54 |
+
"category": "data_quality"
|
| 55 |
+
})
|
| 56 |
+
elif percentage > 10:
|
| 57 |
+
suggestions.append({
|
| 58 |
+
"title": f"Missing Values in {col}",
|
| 59 |
+
"description": f"{percentage:.1f}% of {col} is missing. Consider handling these values.",
|
| 60 |
+
"confidence": min(0.8, percentage / 100),
|
| 61 |
+
"action": "handle_missing",
|
| 62 |
+
"category": "data_quality"
|
| 63 |
+
})
|
| 64 |
+
|
| 65 |
+
return suggestions
|
| 66 |
+
|
| 67 |
+
def _detect_outliers(self, data: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
|
| 68 |
+
"""Detect outliers"""
|
| 69 |
+
suggestions = []
|
| 70 |
+
numeric_cols = self._get_numeric_columns(data)
|
| 71 |
+
|
| 72 |
+
for col in numeric_cols:
|
| 73 |
+
values = [row[col] for row in data if row[col] is not None]
|
| 74 |
+
if len(values) > 4:
|
| 75 |
+
try:
|
| 76 |
+
q1 = sorted(values)[len(values) // 4]
|
| 77 |
+
q3 = sorted(values)[3 * len(values) // 4]
|
| 78 |
+
iqr = q3 - q1
|
| 79 |
+
outliers = [v for v in values if v < q1 - 1.5 * iqr or v > q3 + 1.5 * iqr]
|
| 80 |
+
|
| 81 |
+
if outliers:
|
| 82 |
+
outlier_percentage = (len(outliers) / len(values)) * 100
|
| 83 |
+
if outlier_percentage > 5:
|
| 84 |
+
suggestions.append({
|
| 85 |
+
"title": f"Outliers Detected in {col}",
|
| 86 |
+
"description": f"{len(outliers)} outlier(s) ({outlier_percentage:.1f}%) found. Review and handle appropriately.",
|
| 87 |
+
"confidence": min(0.85, outlier_percentage / 100),
|
| 88 |
+
"action": "review_outliers",
|
| 89 |
+
"category": "anomaly"
|
| 90 |
+
})
|
| 91 |
+
except:
|
| 92 |
+
pass
|
| 93 |
+
|
| 94 |
+
return suggestions
|
| 95 |
+
|
| 96 |
+
def _detect_imbalances(self, data: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
|
| 97 |
+
"""Detect data imbalances"""
|
| 98 |
+
suggestions = []
|
| 99 |
+
|
| 100 |
+
for col in data.keys():
|
| 101 |
+
values = [row[col] for row in data if row[col] is not None]
|
| 102 |
+
if len(set(values)) < len(values) * 0.1: # Few unique values
|
| 103 |
+
from collections import Counter
|
| 104 |
+
counts = Counter(values)
|
| 105 |
+
most_common_count = counts.most_common(1)
|
| 106 |
+
imbalance_ratio = most_common_count / len(values)
|
| 107 |
+
|
| 108 |
+
if imbalance_ratio > 0.8:
|
| 109 |
+
suggestions.append({
|
| 110 |
+
"title": f"Class Imbalance in {col}",
|
| 111 |
+
"description": f"One value appears {imbalance_ratio*100:.1f}% of the time. Data is highly imbalanced.",
|
| 112 |
+
"confidence": min(0.9, imbalance_ratio),
|
| 113 |
+
"action": "rebalance_data",
|
| 114 |
+
"category": "pattern"
|
| 115 |
+
})
|
| 116 |
+
|
| 117 |
+
return suggestions
|
| 118 |
+
|
| 119 |
+
def _detect_quality_issues(self, data: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
|
| 120 |
+
"""Detect data quality issues"""
|
| 121 |
+
suggestions = []
|
| 122 |
+
|
| 123 |
+
# Check for duplicates
|
| 124 |
+
if len(data) != len(set(str(row) for row in data)):
|
| 125 |
+
duplicate_count = len(data) - len(set(str(row) for row in data))
|
| 126 |
+
suggestions.append({
|
| 127 |
+
"title": "Duplicate Records Found",
|
| 128 |
+
"description": f"{duplicate_count} duplicate row(s) detected. Consider deduplication.",
|
| 129 |
+
"confidence": 0.9,
|
| 130 |
+
"action": "deduplicate",
|
| 131 |
+
"category": "data_quality"
|
| 132 |
+
})
|
| 133 |
+
|
| 134 |
+
return suggestions
|
| 135 |
+
|
| 136 |
+
@staticmethod
|
| 137 |
+
def _get_numeric_columns(data: List[Dict[str, Any]]) -> List[str]:
|
| 138 |
+
"""Get numeric columns"""
|
| 139 |
+
if not data:
|
| 140 |
+
return []
|
| 141 |
+
|
| 142 |
+
numeric = []
|
| 143 |
+
for key in data.keys():
|
| 144 |
+
try:
|
| 145 |
+
for row in data:
|
| 146 |
+
if row[key] is not None:
|
| 147 |
+
float(row[key])
|
| 148 |
+
numeric.append(key)
|
| 149 |
+
except (ValueError, TypeError):
|
| 150 |
+
pass
|
| 151 |
+
return numeric
|
pyproject.toml
CHANGED
|
@@ -10,28 +10,27 @@ dependencies = [
|
|
| 10 |
# Backend - FastAPI & Server
|
| 11 |
"fastapi==0.109.0",
|
| 12 |
"uvicorn[standard]==0.27.0",
|
| 13 |
-
|
| 14 |
# Data Processing
|
| 15 |
"pandas==2.1.4",
|
| 16 |
"numpy>=1.26.0,<2.0.0",
|
| 17 |
"scikit-learn==1.3.2",
|
| 18 |
-
|
| 19 |
# File Handling & Parsing
|
| 20 |
"openpyxl==3.1.5",
|
| 21 |
"python-multipart==0.0.6",
|
| 22 |
"aiofiles==23.2.1",
|
| 23 |
-
|
| 24 |
# Validation & Configuration
|
| 25 |
"pydantic==2.5.0",
|
| 26 |
"pydantic-settings==2.1.0",
|
| 27 |
"python-dotenv==1.0.0",
|
| 28 |
-
|
| 29 |
# Visualization
|
| 30 |
"plotly==5.18.0",
|
| 31 |
-
|
| 32 |
# Frontend - Streamlit
|
| 33 |
"streamlit==1.28.1",
|
| 34 |
"requests==2.31.0",
|
|
|
|
|
|
|
|
|
|
|
|
|
| 35 |
]
|
| 36 |
|
| 37 |
[project.optional-dependencies]
|
|
|
|
| 10 |
# Backend - FastAPI & Server
|
| 11 |
"fastapi==0.109.0",
|
| 12 |
"uvicorn[standard]==0.27.0",
|
|
|
|
| 13 |
# Data Processing
|
| 14 |
"pandas==2.1.4",
|
| 15 |
"numpy>=1.26.0,<2.0.0",
|
| 16 |
"scikit-learn==1.3.2",
|
|
|
|
| 17 |
# File Handling & Parsing
|
| 18 |
"openpyxl==3.1.5",
|
| 19 |
"python-multipart==0.0.6",
|
| 20 |
"aiofiles==23.2.1",
|
|
|
|
| 21 |
# Validation & Configuration
|
| 22 |
"pydantic==2.5.0",
|
| 23 |
"pydantic-settings==2.1.0",
|
| 24 |
"python-dotenv==1.0.0",
|
|
|
|
| 25 |
# Visualization
|
| 26 |
"plotly==5.18.0",
|
|
|
|
| 27 |
# Frontend - Streamlit
|
| 28 |
"streamlit==1.28.1",
|
| 29 |
"requests==2.31.0",
|
| 30 |
+
"mlx-lm>=0.28.4",
|
| 31 |
+
"mlx>=0.30.0",
|
| 32 |
+
"scipy>=1.16.3",
|
| 33 |
+
"httpx>=0.28.1",
|
| 34 |
]
|
| 35 |
|
| 36 |
[project.optional-dependencies]
|
uv.lock
CHANGED
|
@@ -447,6 +447,15 @@ wheels = [
|
|
| 447 |
{ url = "https://files.pythonhosted.org/packages/e5/80/ddbf524c6169072ab5e8dd4e106d4eb482bf920da1996dde9f308f90aa8c/fastapi-0.109.0-py3-none-any.whl", hash = "sha256:8c77515984cd8e8cfeb58364f8cc7a28f0692088475e2614f7bf03275eba9093", size = 92049, upload-time = "2024-01-11T15:36:31.271Z" },
|
| 448 |
]
|
| 449 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 450 |
[[package]]
|
| 451 |
name = "frozenlist"
|
| 452 |
version = "1.8.0"
|
|
@@ -552,6 +561,15 @@ wheels = [
|
|
| 552 |
{ url = "https://files.pythonhosted.org/packages/9a/9a/e35b4a917281c0b8419d4207f4334c8e8c5dbf4f3f5f9ada73958d937dcc/frozenlist-1.8.0-py3-none-any.whl", hash = "sha256:0c18a16eab41e82c295618a77502e17b195883241c563b00f0aa5106fc4eaa0d", size = 13409, upload-time = "2025-10-06T05:38:16.721Z" },
|
| 553 |
]
|
| 554 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 555 |
[[package]]
|
| 556 |
name = "gitdb"
|
| 557 |
version = "4.0.12"
|
|
@@ -585,6 +603,48 @@ wheels = [
|
|
| 585 |
{ url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515, upload-time = "2025-04-24T03:35:24.344Z" },
|
| 586 |
]
|
| 587 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 588 |
[[package]]
|
| 589 |
name = "httptools"
|
| 590 |
version = "0.7.1"
|
|
@@ -621,6 +681,40 @@ wheels = [
|
|
| 621 |
{ url = "https://files.pythonhosted.org/packages/53/cf/878f3b91e4e6e011eff6d1fa9ca39f7eb17d19c9d7971b04873734112f30/httptools-0.7.1-cp314-cp314-win_amd64.whl", hash = "sha256:cfabda2a5bb85aa2a904ce06d974a3f30fb36cc63d7feaddec05d2050acede96", size = 88205, upload-time = "2025-10-10T03:55:00.389Z" },
|
| 622 |
]
|
| 623 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 624 |
[[package]]
|
| 625 |
name = "idna"
|
| 626 |
version = "3.11"
|
|
@@ -706,6 +800,9 @@ source = { virtual = "." }
|
|
| 706 |
dependencies = [
|
| 707 |
{ name = "aiofiles" },
|
| 708 |
{ name = "fastapi" },
|
|
|
|
|
|
|
|
|
|
| 709 |
{ name = "numpy" },
|
| 710 |
{ name = "openpyxl" },
|
| 711 |
{ name = "pandas" },
|
|
@@ -716,6 +813,7 @@ dependencies = [
|
|
| 716 |
{ name = "python-multipart" },
|
| 717 |
{ name = "requests" },
|
| 718 |
{ name = "scikit-learn" },
|
|
|
|
| 719 |
{ name = "streamlit" },
|
| 720 |
{ name = "uvicorn", extra = ["standard"] },
|
| 721 |
]
|
|
@@ -734,6 +832,9 @@ requires-dist = [
|
|
| 734 |
{ name = "aiofiles", specifier = "==23.2.1" },
|
| 735 |
{ name = "black", marker = "extra == 'dev'", specifier = "==23.12.0" },
|
| 736 |
{ name = "fastapi", specifier = "==0.109.0" },
|
|
|
|
|
|
|
|
|
|
| 737 |
{ name = "numpy", specifier = ">=1.26.0,<2.0.0" },
|
| 738 |
{ name = "openpyxl", specifier = "==3.1.5" },
|
| 739 |
{ name = "pandas", specifier = "==2.1.4" },
|
|
@@ -748,6 +849,7 @@ requires-dist = [
|
|
| 748 |
{ name = "requests", specifier = "==2.31.0" },
|
| 749 |
{ name = "ruff", marker = "extra == 'dev'", specifier = "==0.1.11" },
|
| 750 |
{ name = "scikit-learn", specifier = "==1.3.2" },
|
|
|
|
| 751 |
{ name = "streamlit", specifier = "==1.28.1" },
|
| 752 |
{ name = "uvicorn", extras = ["standard"], specifier = "==0.27.0" },
|
| 753 |
]
|
|
@@ -848,6 +950,64 @@ wheels = [
|
|
| 848 |
{ url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979, upload-time = "2022-08-14T12:40:09.779Z" },
|
| 849 |
]
|
| 850 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 851 |
[[package]]
|
| 852 |
name = "multidict"
|
| 853 |
version = "6.7.0"
|
|
@@ -1532,6 +1692,98 @@ wheels = [
|
|
| 1532 |
{ url = "https://files.pythonhosted.org/packages/2c/58/ca301544e1fa93ed4f80d724bf5b194f6e4b945841c5bfd555878eea9fcb/referencing-0.37.0-py3-none-any.whl", hash = "sha256:381329a9f99628c9069361716891d34ad94af76e461dcb0335825aecc7692231", size = 26766, upload-time = "2025-10-13T15:30:47.625Z" },
|
| 1533 |
]
|
| 1534 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1535 |
[[package]]
|
| 1536 |
name = "requests"
|
| 1537 |
version = "2.31.0"
|
|
@@ -1692,6 +1944,28 @@ wheels = [
|
|
| 1692 |
{ url = "https://files.pythonhosted.org/packages/a9/9b/770da4f22ea69fdd19ac6cb183f02abf8fc6f8d16a9dc00dfaf667649ee7/ruff-0.1.11-py3-none-win_arm64.whl", hash = "sha256:97ce4d752f964ba559c7023a86e5f8e97f026d511e48013987623915431c7ea9", size = 6827536, upload-time = "2024-01-02T22:59:08.394Z" },
|
| 1693 |
]
|
| 1694 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1695 |
[[package]]
|
| 1696 |
name = "scikit-learn"
|
| 1697 |
version = "1.3.2"
|
|
@@ -1787,6 +2061,62 @@ wheels = [
|
|
| 1787 |
{ url = "https://files.pythonhosted.org/packages/64/47/a494741db7280eae6dc033510c319e34d42dd41b7ac0c7ead39354d1a2b5/scipy-1.16.3-cp314-cp314t-win_arm64.whl", hash = "sha256:21d9d6b197227a12dcbf9633320a4e34c6b0e51c57268df255a0942983bac562", size = 26464127, upload-time = "2025-10-28T17:38:11.34Z" },
|
| 1788 |
]
|
| 1789 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1790 |
[[package]]
|
| 1791 |
name = "six"
|
| 1792 |
version = "1.17.0"
|
|
@@ -1869,6 +2199,31 @@ wheels = [
|
|
| 1869 |
{ url = "https://files.pythonhosted.org/packages/32/d5/f9a850d79b0851d1d4ef6456097579a9005b31fea68726a4ae5f2d82ddd9/threadpoolctl-3.6.0-py3-none-any.whl", hash = "sha256:43a0b8fd5a2928500110039e43a5eed8480b918967083ea48dc3ab9f13c4a7fb", size = 18638, upload-time = "2025-03-13T13:49:21.846Z" },
|
| 1870 |
]
|
| 1871 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1872 |
[[package]]
|
| 1873 |
name = "toml"
|
| 1874 |
version = "0.10.2"
|
|
@@ -1946,6 +2301,39 @@ wheels = [
|
|
| 1946 |
{ url = "https://files.pythonhosted.org/packages/5e/4f/e1f65e8f8c76d73658b33d33b81eed4322fb5085350e4328d5c956f0c8f9/tornado-6.5.2-cp39-abi3-win_arm64.whl", hash = "sha256:d6c33dc3672e3a1f3618eb63b7ef4683a7688e7b9e6e8f0d9aa5726360a004af", size = 444456, upload-time = "2025-08-08T18:26:59.207Z" },
|
| 1947 |
]
|
| 1948 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1949 |
[[package]]
|
| 1950 |
name = "typing-extensions"
|
| 1951 |
version = "4.15.0"
|
|
|
|
| 447 |
{ url = "https://files.pythonhosted.org/packages/e5/80/ddbf524c6169072ab5e8dd4e106d4eb482bf920da1996dde9f308f90aa8c/fastapi-0.109.0-py3-none-any.whl", hash = "sha256:8c77515984cd8e8cfeb58364f8cc7a28f0692088475e2614f7bf03275eba9093", size = 92049, upload-time = "2024-01-11T15:36:31.271Z" },
|
| 448 |
]
|
| 449 |
|
| 450 |
+
[[package]]
|
| 451 |
+
name = "filelock"
|
| 452 |
+
version = "3.20.0"
|
| 453 |
+
source = { registry = "https://pypi.org/simple" }
|
| 454 |
+
sdist = { url = "https://files.pythonhosted.org/packages/58/46/0028a82567109b5ef6e4d2a1f04a583fb513e6cf9527fcdd09afd817deeb/filelock-3.20.0.tar.gz", hash = "sha256:711e943b4ec6be42e1d4e6690b48dc175c822967466bb31c0c293f34334c13f4", size = 18922, upload-time = "2025-10-08T18:03:50.056Z" }
|
| 455 |
+
wheels = [
|
| 456 |
+
{ url = "https://files.pythonhosted.org/packages/76/91/7216b27286936c16f5b4d0c530087e4a54eead683e6b0b73dd0c64844af6/filelock-3.20.0-py3-none-any.whl", hash = "sha256:339b4732ffda5cd79b13f4e2711a31b0365ce445d95d243bb996273d072546a2", size = 16054, upload-time = "2025-10-08T18:03:48.35Z" },
|
| 457 |
+
]
|
| 458 |
+
|
| 459 |
[[package]]
|
| 460 |
name = "frozenlist"
|
| 461 |
version = "1.8.0"
|
|
|
|
| 561 |
{ url = "https://files.pythonhosted.org/packages/9a/9a/e35b4a917281c0b8419d4207f4334c8e8c5dbf4f3f5f9ada73958d937dcc/frozenlist-1.8.0-py3-none-any.whl", hash = "sha256:0c18a16eab41e82c295618a77502e17b195883241c563b00f0aa5106fc4eaa0d", size = 13409, upload-time = "2025-10-06T05:38:16.721Z" },
|
| 562 |
]
|
| 563 |
|
| 564 |
+
[[package]]
|
| 565 |
+
name = "fsspec"
|
| 566 |
+
version = "2025.12.0"
|
| 567 |
+
source = { registry = "https://pypi.org/simple" }
|
| 568 |
+
sdist = { url = "https://files.pythonhosted.org/packages/b6/27/954057b0d1f53f086f681755207dda6de6c660ce133c829158e8e8fe7895/fsspec-2025.12.0.tar.gz", hash = "sha256:c505de011584597b1060ff778bb664c1bc022e87921b0e4f10cc9c44f9635973", size = 309748, upload-time = "2025-12-03T15:23:42.687Z" }
|
| 569 |
+
wheels = [
|
| 570 |
+
{ url = "https://files.pythonhosted.org/packages/51/c7/b64cae5dba3a1b138d7123ec36bb5ccd39d39939f18454407e5468f4763f/fsspec-2025.12.0-py3-none-any.whl", hash = "sha256:8bf1fe301b7d8acfa6e8571e3b1c3d158f909666642431cc78a1b7b4dbc5ec5b", size = 201422, upload-time = "2025-12-03T15:23:41.434Z" },
|
| 571 |
+
]
|
| 572 |
+
|
| 573 |
[[package]]
|
| 574 |
name = "gitdb"
|
| 575 |
version = "4.0.12"
|
|
|
|
| 603 |
{ url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515, upload-time = "2025-04-24T03:35:24.344Z" },
|
| 604 |
]
|
| 605 |
|
| 606 |
+
[[package]]
|
| 607 |
+
name = "hf-xet"
|
| 608 |
+
version = "1.2.0"
|
| 609 |
+
source = { registry = "https://pypi.org/simple" }
|
| 610 |
+
sdist = { url = "https://files.pythonhosted.org/packages/5e/6e/0f11bacf08a67f7fb5ee09740f2ca54163863b07b70d579356e9222ce5d8/hf_xet-1.2.0.tar.gz", hash = "sha256:a8c27070ca547293b6890c4bf389f713f80e8c478631432962bb7f4bc0bd7d7f", size = 506020, upload-time = "2025-10-24T19:04:32.129Z" }
|
| 611 |
+
wheels = [
|
| 612 |
+
{ url = "https://files.pythonhosted.org/packages/9e/a5/85ef910a0aa034a2abcfadc360ab5ac6f6bc4e9112349bd40ca97551cff0/hf_xet-1.2.0-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:ceeefcd1b7aed4956ae8499e2199607765fbd1c60510752003b6cc0b8413b649", size = 2861870, upload-time = "2025-10-24T19:04:11.422Z" },
|
| 613 |
+
{ url = "https://files.pythonhosted.org/packages/ea/40/e2e0a7eb9a51fe8828ba2d47fe22a7e74914ea8a0db68a18c3aa7449c767/hf_xet-1.2.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:b70218dd548e9840224df5638fdc94bd033552963cfa97f9170829381179c813", size = 2717584, upload-time = "2025-10-24T19:04:09.586Z" },
|
| 614 |
+
{ url = "https://files.pythonhosted.org/packages/a5/7d/daf7f8bc4594fdd59a8a596f9e3886133fdc68e675292218a5e4c1b7e834/hf_xet-1.2.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7d40b18769bb9a8bc82a9ede575ce1a44c75eb80e7375a01d76259089529b5dc", size = 3315004, upload-time = "2025-10-24T19:04:00.314Z" },
|
| 615 |
+
{ url = "https://files.pythonhosted.org/packages/b1/ba/45ea2f605fbf6d81c8b21e4d970b168b18a53515923010c312c06cd83164/hf_xet-1.2.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:cd3a6027d59cfb60177c12d6424e31f4b5ff13d8e3a1247b3a584bf8977e6df5", size = 3222636, upload-time = "2025-10-24T19:03:58.111Z" },
|
| 616 |
+
{ url = "https://files.pythonhosted.org/packages/4a/1d/04513e3cab8f29ab8c109d309ddd21a2705afab9d52f2ba1151e0c14f086/hf_xet-1.2.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6de1fc44f58f6dd937956c8d304d8c2dea264c80680bcfa61ca4a15e7b76780f", size = 3408448, upload-time = "2025-10-24T19:04:20.951Z" },
|
| 617 |
+
{ url = "https://files.pythonhosted.org/packages/f0/7c/60a2756d7feec7387db3a1176c632357632fbe7849fce576c5559d4520c7/hf_xet-1.2.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:f182f264ed2acd566c514e45da9f2119110e48a87a327ca271027904c70c5832", size = 3503401, upload-time = "2025-10-24T19:04:22.549Z" },
|
| 618 |
+
{ url = "https://files.pythonhosted.org/packages/4e/64/48fffbd67fb418ab07451e4ce641a70de1c40c10a13e25325e24858ebe5a/hf_xet-1.2.0-cp313-cp313t-win_amd64.whl", hash = "sha256:293a7a3787e5c95d7be1857358a9130694a9c6021de3f27fa233f37267174382", size = 2900866, upload-time = "2025-10-24T19:04:33.461Z" },
|
| 619 |
+
{ url = "https://files.pythonhosted.org/packages/e2/51/f7e2caae42f80af886db414d4e9885fac959330509089f97cccb339c6b87/hf_xet-1.2.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:10bfab528b968c70e062607f663e21e34e2bba349e8038db546646875495179e", size = 2861861, upload-time = "2025-10-24T19:04:19.01Z" },
|
| 620 |
+
{ url = "https://files.pythonhosted.org/packages/6e/1d/a641a88b69994f9371bd347f1dd35e5d1e2e2460a2e350c8d5165fc62005/hf_xet-1.2.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:2a212e842647b02eb6a911187dc878e79c4aa0aa397e88dd3b26761676e8c1f8", size = 2717699, upload-time = "2025-10-24T19:04:17.306Z" },
|
| 621 |
+
{ url = "https://files.pythonhosted.org/packages/df/e0/e5e9bba7d15f0318955f7ec3f4af13f92e773fbb368c0b8008a5acbcb12f/hf_xet-1.2.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:30e06daccb3a7d4c065f34fc26c14c74f4653069bb2b194e7f18f17cbe9939c0", size = 3314885, upload-time = "2025-10-24T19:04:07.642Z" },
|
| 622 |
+
{ url = "https://files.pythonhosted.org/packages/21/90/b7fe5ff6f2b7b8cbdf1bd56145f863c90a5807d9758a549bf3d916aa4dec/hf_xet-1.2.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:29c8fc913a529ec0a91867ce3d119ac1aac966e098cf49501800c870328cc090", size = 3221550, upload-time = "2025-10-24T19:04:05.55Z" },
|
| 623 |
+
{ url = "https://files.pythonhosted.org/packages/6f/cb/73f276f0a7ce46cc6a6ec7d6c7d61cbfe5f2e107123d9bbd0193c355f106/hf_xet-1.2.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e159cbfcfbb29f920db2c09ed8b660eb894640d284f102ada929b6e3dc410a", size = 3408010, upload-time = "2025-10-24T19:04:28.598Z" },
|
| 624 |
+
{ url = "https://files.pythonhosted.org/packages/b8/1e/d642a12caa78171f4be64f7cd9c40e3ca5279d055d0873188a58c0f5fbb9/hf_xet-1.2.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:9c91d5ae931510107f148874e9e2de8a16052b6f1b3ca3c1b12f15ccb491390f", size = 3503264, upload-time = "2025-10-24T19:04:30.397Z" },
|
| 625 |
+
{ url = "https://files.pythonhosted.org/packages/17/b5/33764714923fa1ff922770f7ed18c2daae034d21ae6e10dbf4347c854154/hf_xet-1.2.0-cp314-cp314t-win_amd64.whl", hash = "sha256:210d577732b519ac6ede149d2f2f34049d44e8622bf14eb3d63bbcd2d4b332dc", size = 2901071, upload-time = "2025-10-24T19:04:37.463Z" },
|
| 626 |
+
{ url = "https://files.pythonhosted.org/packages/96/2d/22338486473df5923a9ab7107d375dbef9173c338ebef5098ef593d2b560/hf_xet-1.2.0-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:46740d4ac024a7ca9b22bebf77460ff43332868b661186a8e46c227fdae01848", size = 2866099, upload-time = "2025-10-24T19:04:15.366Z" },
|
| 627 |
+
{ url = "https://files.pythonhosted.org/packages/7f/8c/c5becfa53234299bc2210ba314eaaae36c2875e0045809b82e40a9544f0c/hf_xet-1.2.0-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:27df617a076420d8845bea087f59303da8be17ed7ec0cd7ee3b9b9f579dff0e4", size = 2722178, upload-time = "2025-10-24T19:04:13.695Z" },
|
| 628 |
+
{ url = "https://files.pythonhosted.org/packages/9a/92/cf3ab0b652b082e66876d08da57fcc6fa2f0e6c70dfbbafbd470bb73eb47/hf_xet-1.2.0-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3651fd5bfe0281951b988c0facbe726aa5e347b103a675f49a3fa8144c7968fd", size = 3320214, upload-time = "2025-10-24T19:04:03.596Z" },
|
| 629 |
+
{ url = "https://files.pythonhosted.org/packages/46/92/3f7ec4a1b6a65bf45b059b6d4a5d38988f63e193056de2f420137e3c3244/hf_xet-1.2.0-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:d06fa97c8562fb3ee7a378dd9b51e343bc5bc8190254202c9771029152f5e08c", size = 3229054, upload-time = "2025-10-24T19:04:01.949Z" },
|
| 630 |
+
{ url = "https://files.pythonhosted.org/packages/0b/dd/7ac658d54b9fb7999a0ccb07ad863b413cbaf5cf172f48ebcd9497ec7263/hf_xet-1.2.0-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:4c1428c9ae73ec0939410ec73023c4f842927f39db09b063b9482dac5a3bb737", size = 3413812, upload-time = "2025-10-24T19:04:24.585Z" },
|
| 631 |
+
{ url = "https://files.pythonhosted.org/packages/92/68/89ac4e5b12a9ff6286a12174c8538a5930e2ed662091dd2572bbe0a18c8a/hf_xet-1.2.0-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a55558084c16b09b5ed32ab9ed38421e2d87cf3f1f89815764d1177081b99865", size = 3508920, upload-time = "2025-10-24T19:04:26.927Z" },
|
| 632 |
+
{ url = "https://files.pythonhosted.org/packages/cb/44/870d44b30e1dcfb6a65932e3e1506c103a8a5aea9103c337e7a53180322c/hf_xet-1.2.0-cp37-abi3-win_amd64.whl", hash = "sha256:e6584a52253f72c9f52f9e549d5895ca7a471608495c4ecaa6cc73dba2b24d69", size = 2905735, upload-time = "2025-10-24T19:04:35.928Z" },
|
| 633 |
+
]
|
| 634 |
+
|
| 635 |
+
[[package]]
|
| 636 |
+
name = "httpcore"
|
| 637 |
+
version = "1.0.9"
|
| 638 |
+
source = { registry = "https://pypi.org/simple" }
|
| 639 |
+
dependencies = [
|
| 640 |
+
{ name = "certifi" },
|
| 641 |
+
{ name = "h11" },
|
| 642 |
+
]
|
| 643 |
+
sdist = { url = "https://files.pythonhosted.org/packages/06/94/82699a10bca87a5556c9c59b5963f2d039dbd239f25bc2a63907a05a14cb/httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8", size = 85484, upload-time = "2025-04-24T22:06:22.219Z" }
|
| 644 |
+
wheels = [
|
| 645 |
+
{ url = "https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55", size = 78784, upload-time = "2025-04-24T22:06:20.566Z" },
|
| 646 |
+
]
|
| 647 |
+
|
| 648 |
[[package]]
|
| 649 |
name = "httptools"
|
| 650 |
version = "0.7.1"
|
|
|
|
| 681 |
{ url = "https://files.pythonhosted.org/packages/53/cf/878f3b91e4e6e011eff6d1fa9ca39f7eb17d19c9d7971b04873734112f30/httptools-0.7.1-cp314-cp314-win_amd64.whl", hash = "sha256:cfabda2a5bb85aa2a904ce06d974a3f30fb36cc63d7feaddec05d2050acede96", size = 88205, upload-time = "2025-10-10T03:55:00.389Z" },
|
| 682 |
]
|
| 683 |
|
| 684 |
+
[[package]]
|
| 685 |
+
name = "httpx"
|
| 686 |
+
version = "0.28.1"
|
| 687 |
+
source = { registry = "https://pypi.org/simple" }
|
| 688 |
+
dependencies = [
|
| 689 |
+
{ name = "anyio" },
|
| 690 |
+
{ name = "certifi" },
|
| 691 |
+
{ name = "httpcore" },
|
| 692 |
+
{ name = "idna" },
|
| 693 |
+
]
|
| 694 |
+
sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406, upload-time = "2024-12-06T15:37:23.222Z" }
|
| 695 |
+
wheels = [
|
| 696 |
+
{ url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517, upload-time = "2024-12-06T15:37:21.509Z" },
|
| 697 |
+
]
|
| 698 |
+
|
| 699 |
+
[[package]]
|
| 700 |
+
name = "huggingface-hub"
|
| 701 |
+
version = "0.36.0"
|
| 702 |
+
source = { registry = "https://pypi.org/simple" }
|
| 703 |
+
dependencies = [
|
| 704 |
+
{ name = "filelock" },
|
| 705 |
+
{ name = "fsspec" },
|
| 706 |
+
{ name = "hf-xet", marker = "platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'arm64' or platform_machine == 'x86_64'" },
|
| 707 |
+
{ name = "packaging" },
|
| 708 |
+
{ name = "pyyaml" },
|
| 709 |
+
{ name = "requests" },
|
| 710 |
+
{ name = "tqdm" },
|
| 711 |
+
{ name = "typing-extensions" },
|
| 712 |
+
]
|
| 713 |
+
sdist = { url = "https://files.pythonhosted.org/packages/98/63/4910c5fa9128fdadf6a9c5ac138e8b1b6cee4ca44bf7915bbfbce4e355ee/huggingface_hub-0.36.0.tar.gz", hash = "sha256:47b3f0e2539c39bf5cde015d63b72ec49baff67b6931c3d97f3f84532e2b8d25", size = 463358, upload-time = "2025-10-23T12:12:01.413Z" }
|
| 714 |
+
wheels = [
|
| 715 |
+
{ url = "https://files.pythonhosted.org/packages/cb/bd/1a875e0d592d447cbc02805fd3fe0f497714d6a2583f59d14fa9ebad96eb/huggingface_hub-0.36.0-py3-none-any.whl", hash = "sha256:7bcc9ad17d5b3f07b57c78e79d527102d08313caa278a641993acddcb894548d", size = 566094, upload-time = "2025-10-23T12:11:59.557Z" },
|
| 716 |
+
]
|
| 717 |
+
|
| 718 |
[[package]]
|
| 719 |
name = "idna"
|
| 720 |
version = "3.11"
|
|
|
|
| 800 |
dependencies = [
|
| 801 |
{ name = "aiofiles" },
|
| 802 |
{ name = "fastapi" },
|
| 803 |
+
{ name = "httpx" },
|
| 804 |
+
{ name = "mlx" },
|
| 805 |
+
{ name = "mlx-lm" },
|
| 806 |
{ name = "numpy" },
|
| 807 |
{ name = "openpyxl" },
|
| 808 |
{ name = "pandas" },
|
|
|
|
| 813 |
{ name = "python-multipart" },
|
| 814 |
{ name = "requests" },
|
| 815 |
{ name = "scikit-learn" },
|
| 816 |
+
{ name = "scipy" },
|
| 817 |
{ name = "streamlit" },
|
| 818 |
{ name = "uvicorn", extra = ["standard"] },
|
| 819 |
]
|
|
|
|
| 832 |
{ name = "aiofiles", specifier = "==23.2.1" },
|
| 833 |
{ name = "black", marker = "extra == 'dev'", specifier = "==23.12.0" },
|
| 834 |
{ name = "fastapi", specifier = "==0.109.0" },
|
| 835 |
+
{ name = "httpx", specifier = ">=0.28.1" },
|
| 836 |
+
{ name = "mlx", specifier = ">=0.30.0" },
|
| 837 |
+
{ name = "mlx-lm", specifier = ">=0.28.4" },
|
| 838 |
{ name = "numpy", specifier = ">=1.26.0,<2.0.0" },
|
| 839 |
{ name = "openpyxl", specifier = "==3.1.5" },
|
| 840 |
{ name = "pandas", specifier = "==2.1.4" },
|
|
|
|
| 849 |
{ name = "requests", specifier = "==2.31.0" },
|
| 850 |
{ name = "ruff", marker = "extra == 'dev'", specifier = "==0.1.11" },
|
| 851 |
{ name = "scikit-learn", specifier = "==1.3.2" },
|
| 852 |
+
{ name = "scipy", specifier = ">=1.16.3" },
|
| 853 |
{ name = "streamlit", specifier = "==1.28.1" },
|
| 854 |
{ name = "uvicorn", extras = ["standard"], specifier = "==0.27.0" },
|
| 855 |
]
|
|
|
|
| 950 |
{ url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979, upload-time = "2022-08-14T12:40:09.779Z" },
|
| 951 |
]
|
| 952 |
|
| 953 |
+
[[package]]
|
| 954 |
+
name = "mlx"
|
| 955 |
+
version = "0.30.0"
|
| 956 |
+
source = { registry = "https://pypi.org/simple" }
|
| 957 |
+
dependencies = [
|
| 958 |
+
{ name = "mlx-metal", marker = "sys_platform == 'darwin'" },
|
| 959 |
+
]
|
| 960 |
+
wheels = [
|
| 961 |
+
{ url = "https://files.pythonhosted.org/packages/4a/e8/69ebac29536c026489ded1ad58a6f5163b8fc10ab5eac21228f57ea9e83f/mlx-0.30.0-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:d5871a5f1ce5cba2f690d2c630a6672cc65a62326bcaa6db258185957a1f073f", size = 554825, upload-time = "2025-11-20T16:45:11.135Z" },
|
| 962 |
+
{ url = "https://files.pythonhosted.org/packages/2b/c7/db80b1e9f613baf99745e9920a3a7fe7b6c61398420ed308f24f60877a15/mlx-0.30.0-cp311-cp311-macosx_15_0_arm64.whl", hash = "sha256:302c3d52b4f68b80a8ee259489369524a3381fcc9aed12b17c1c537870c81fce", size = 554821, upload-time = "2025-11-20T01:16:38.53Z" },
|
| 963 |
+
{ url = "https://files.pythonhosted.org/packages/8a/68/55cd5cab5a9b8b958ea995295aa3428ad65b3e893f04c27e2cdfcdc176af/mlx-0.30.0-cp311-cp311-macosx_26_0_arm64.whl", hash = "sha256:d72443a461f2e92329edf344ded8460df53fd7ec3c04ddd2353ee4ceb34c0ff2", size = 554791, upload-time = "2025-11-20T05:33:23.851Z" },
|
| 964 |
+
{ url = "https://files.pythonhosted.org/packages/16/91/5b79b6febcbb7e1051ecf408a8c30226ebfdf19a6e304b4cfa32309059ab/mlx-0.30.0-cp311-cp311-manylinux_2_35_aarch64.whl", hash = "sha256:9de2ab05cc9721c99a0802721aa1f0d6c305e97acd2f8ec4c05af3449ee7700d", size = 625176, upload-time = "2025-12-01T15:23:40.991Z" },
|
| 965 |
+
{ url = "https://files.pythonhosted.org/packages/80/17/16868ac1ea36ea3baf61f84721b4dfd98dd247a230b951ddd715981db79b/mlx-0.30.0-cp311-cp311-manylinux_2_35_x86_64.whl", hash = "sha256:0beb015a6afef2f86dfbefe65c9022dce91dbe1651e1b4330ef32434602323e0", size = 659735, upload-time = "2025-11-20T01:16:40.594Z" },
|
| 966 |
+
{ url = "https://files.pythonhosted.org/packages/94/a3/32c4c05d8967591e2a1a1e7e3fc9cece8821f5aea8ac8f3bcfdb203f4722/mlx-0.30.0-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:11fbae58b1e992afdec6709d5e281932871a0138582a794cdcc82ff895a28670", size = 554567, upload-time = "2025-11-20T16:45:12.73Z" },
|
| 967 |
+
{ url = "https://files.pythonhosted.org/packages/aa/b3/b6143f1c078fbc873e40e624dc428a3ada240721001414955f584afa866d/mlx-0.30.0-cp312-cp312-macosx_15_0_arm64.whl", hash = "sha256:fc1994a4c9e60ccfe2ab9211c846be2ef7abc6fc3e53e1addb2cdf7468f61e7b", size = 554566, upload-time = "2025-11-20T01:16:41.877Z" },
|
| 968 |
+
{ url = "https://files.pythonhosted.org/packages/96/cb/aec36297ef76b4b190cc6d4cea1ed995b458bbc21cb91c58b0862f8cae7d/mlx-0.30.0-cp312-cp312-macosx_26_0_arm64.whl", hash = "sha256:a9f30c5c94b30b65f026162e91256473187e1de57c13dcc2aedb2fc33c07f2c1", size = 554593, upload-time = "2025-11-20T05:33:25.312Z" },
|
| 969 |
+
{ url = "https://files.pythonhosted.org/packages/40/d3/575eee4ef4b5f3dad9076a78f287affe046fd32b3bdba7a2e0af31f0d9d3/mlx-0.30.0-cp312-cp312-manylinux_2_35_aarch64.whl", hash = "sha256:f88d11e2719fdab08dbe68c9666003c8644ccd01dff973bc86e949cc16195eac", size = 612086, upload-time = "2025-12-01T15:23:42.317Z" },
|
| 970 |
+
{ url = "https://files.pythonhosted.org/packages/23/37/b5dd68da0e79e258f5d6a0e9f5fd4f9e5452e92c20b13b64948a965ea429/mlx-0.30.0-cp312-cp312-manylinux_2_35_x86_64.whl", hash = "sha256:1c4a2f5285bdd585aa6485a4fb5759ebc2721ba9381404ff867c136e84764e9b", size = 653994, upload-time = "2025-11-20T01:16:43.492Z" },
|
| 971 |
+
{ url = "https://files.pythonhosted.org/packages/33/a5/e171b2caa69b346bc1abc1bfd0b139f631f68a0ff602862dd255e7dd95ec/mlx-0.30.0-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:f46aaa6c562ba183e2a64a0e6ba15ed54f9027d9b7b1822e9eec7f59b13d610c", size = 554595, upload-time = "2025-11-20T16:45:13.919Z" },
|
| 972 |
+
{ url = "https://files.pythonhosted.org/packages/3e/f4/aeb8980bbef08fc031ab1a2d043a1d76e60d49bf46728bef66cf25b26dfa/mlx-0.30.0-cp313-cp313-macosx_15_0_arm64.whl", hash = "sha256:5edee9f1452703c0804e067f212c61d488b3ad7419b9fcacf337ffd35842f576", size = 554593, upload-time = "2025-11-20T01:16:45.033Z" },
|
| 973 |
+
{ url = "https://files.pythonhosted.org/packages/0b/b3/904235e11610c7cf2ba39eb39f32587d34048f7293d386f19d42d1e3dabc/mlx-0.30.0-cp313-cp313-macosx_26_0_arm64.whl", hash = "sha256:b8ae92b61353d756bbd0668b065a4ee5ee35867b03d63c0c61134d656ed236fe", size = 554330, upload-time = "2025-11-20T05:33:26.653Z" },
|
| 974 |
+
{ url = "https://files.pythonhosted.org/packages/de/0f/bb956ec9926596fe771ec67677c049e358c43f0506699b59dbed7ba9fedc/mlx-0.30.0-cp313-cp313-manylinux_2_35_aarch64.whl", hash = "sha256:f3ae4c99308ff4c006c3062ff73108b6f39414bcd90416930b917160070f759b", size = 612083, upload-time = "2025-12-01T15:23:43.369Z" },
|
| 975 |
+
{ url = "https://files.pythonhosted.org/packages/5e/87/3f3505d3fbf0f977b2930b3596f590f0079c3a2a253d01349f936c40985a/mlx-0.30.0-cp313-cp313-manylinux_2_35_x86_64.whl", hash = "sha256:431009c531f8bdbf56f46b85d458d5526b40ea43178dbc85f37ed55e03e716be", size = 653971, upload-time = "2025-11-20T01:16:46.292Z" },
|
| 976 |
+
{ url = "https://files.pythonhosted.org/packages/43/25/6fa174632f5beb583eda80902af80dc39a63e5b4b6e66c7831301751d82e/mlx-0.30.0-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:384819dccfd551aa1444acacedb9ed2619724b0e67fc361ab80d73b8a8a7618f", size = 558834, upload-time = "2025-11-20T16:45:15.303Z" },
|
| 977 |
+
{ url = "https://files.pythonhosted.org/packages/b9/00/8c93f6ba5dc37a459b378bd22d2303824aa341595ed6c4958c6a48870677/mlx-0.30.0-cp314-cp314-macosx_15_0_arm64.whl", hash = "sha256:8982a7d7fd078988c12c3fc53da1670aa910dd8a7df1fc0b9fee7303394ac8ff", size = 558833, upload-time = "2025-11-20T01:16:48.748Z" },
|
| 978 |
+
{ url = "https://files.pythonhosted.org/packages/58/65/0ecb3a858142d7b250b46d1e5c2bb4ebff9f57b735643626b7d4653b0f11/mlx-0.30.0-cp314-cp314-macosx_26_0_arm64.whl", hash = "sha256:392af721cb0bcf600162d7258923c15d39e197a64e23b6abd171e1aea79771c5", size = 558510, upload-time = "2025-11-20T05:33:28.286Z" },
|
| 979 |
+
{ url = "https://files.pythonhosted.org/packages/27/a2/df61dc58caf6238d90cfa2535018fa2c52745e76421dd3ff11c034aae26f/mlx-0.30.0-cp314-cp314-manylinux_2_35_aarch64.whl", hash = "sha256:a4dae4c0404560d3df9832ee709e502022a9e5f89c4d432c576af8405d9a45ef", size = 614193, upload-time = "2025-12-01T15:23:44.688Z" },
|
| 980 |
+
{ url = "https://files.pythonhosted.org/packages/e1/dd/38f465477f996bb24ab133aaf79a3eb0fa13a9bd9d19504aad30c3be7ffc/mlx-0.30.0-cp314-cp314-manylinux_2_35_x86_64.whl", hash = "sha256:cf688c9dda18f2521d48f712515a47b145b37d5d0ab245eaf1a68f286af7ae66", size = 654400, upload-time = "2025-11-20T01:16:50.018Z" },
|
| 981 |
+
]
|
| 982 |
+
|
| 983 |
+
[[package]]
|
| 984 |
+
name = "mlx-lm"
|
| 985 |
+
version = "0.28.4"
|
| 986 |
+
source = { registry = "https://pypi.org/simple" }
|
| 987 |
+
dependencies = [
|
| 988 |
+
{ name = "jinja2" },
|
| 989 |
+
{ name = "mlx", marker = "sys_platform == 'darwin'" },
|
| 990 |
+
{ name = "numpy" },
|
| 991 |
+
{ name = "protobuf" },
|
| 992 |
+
{ name = "pyyaml" },
|
| 993 |
+
{ name = "sentencepiece" },
|
| 994 |
+
{ name = "transformers" },
|
| 995 |
+
]
|
| 996 |
+
sdist = { url = "https://files.pythonhosted.org/packages/f2/7f/94b3f7e00c4681a4fe2d47b519458245bd8a8f0506b1ce018d1850bbcf79/mlx_lm-0.28.4.tar.gz", hash = "sha256:3661d8ef5f0e2695d52993e0df1ed2c1f93ca1d094258146c18d9cec0c50514e", size = 232455, upload-time = "2025-12-03T22:39:59.122Z" }
|
| 997 |
+
wheels = [
|
| 998 |
+
{ url = "https://files.pythonhosted.org/packages/69/35/5767098993834582e8a34a0317d666ed53498f58940fad8bffa0a4662eb1/mlx_lm-0.28.4-py3-none-any.whl", hash = "sha256:64ff7bff02e902f1df7daafdbf27ffc836b9a7c9b332b9fc3ea40c19e31147ca", size = 323307, upload-time = "2025-12-03T22:39:57.711Z" },
|
| 999 |
+
]
|
| 1000 |
+
|
| 1001 |
+
[[package]]
|
| 1002 |
+
name = "mlx-metal"
|
| 1003 |
+
version = "0.30.0"
|
| 1004 |
+
source = { registry = "https://pypi.org/simple" }
|
| 1005 |
+
wheels = [
|
| 1006 |
+
{ url = "https://files.pythonhosted.org/packages/64/9f/47ebb6e9b2c33371c6ca3733e70324ed064f49e790ee4e194b713d6d7d84/mlx_metal-0.30.0-py3-none-macosx_14_0_arm64.whl", hash = "sha256:f48543b10d13bf0591b3f99c6eb585dd2c2e5db379edae5df0f19a728cb41742", size = 36819863, upload-time = "2025-11-20T06:42:23.927Z" },
|
| 1007 |
+
{ url = "https://files.pythonhosted.org/packages/7f/91/c04e420326390c37d4c13f58960956d698cd34c7432ae3860bc5c6be71a0/mlx_metal-0.30.0-py3-none-macosx_15_0_arm64.whl", hash = "sha256:74bb5d10e0f24e21973d39430557bbd5d733c2a6599c3f1b87f9a0ff73fed2c8", size = 36817633, upload-time = "2025-11-20T01:16:27.77Z" },
|
| 1008 |
+
{ url = "https://files.pythonhosted.org/packages/86/18/7af11eac0f488b68c436d249ffcf4f76003326e691994db3f720d21f21bb/mlx_metal-0.30.0-py3-none-macosx_26_0_arm64.whl", hash = "sha256:310fda8b2f7345865f3dd75a9b478e974f28d22e8ebf05f26af5adc0e8979cee", size = 44878409, upload-time = "2025-11-20T05:33:20.221Z" },
|
| 1009 |
+
]
|
| 1010 |
+
|
| 1011 |
[[package]]
|
| 1012 |
name = "multidict"
|
| 1013 |
version = "6.7.0"
|
|
|
|
| 1692 |
{ url = "https://files.pythonhosted.org/packages/2c/58/ca301544e1fa93ed4f80d724bf5b194f6e4b945841c5bfd555878eea9fcb/referencing-0.37.0-py3-none-any.whl", hash = "sha256:381329a9f99628c9069361716891d34ad94af76e461dcb0335825aecc7692231", size = 26766, upload-time = "2025-10-13T15:30:47.625Z" },
|
| 1693 |
]
|
| 1694 |
|
| 1695 |
+
[[package]]
|
| 1696 |
+
name = "regex"
|
| 1697 |
+
version = "2025.11.3"
|
| 1698 |
+
source = { registry = "https://pypi.org/simple" }
|
| 1699 |
+
sdist = { url = "https://files.pythonhosted.org/packages/cc/a9/546676f25e573a4cf00fe8e119b78a37b6a8fe2dc95cda877b30889c9c45/regex-2025.11.3.tar.gz", hash = "sha256:1fedc720f9bb2494ce31a58a1631f9c82df6a09b49c19517ea5cc280b4541e01", size = 414669, upload-time = "2025-11-03T21:34:22.089Z" }
|
| 1700 |
+
wheels = [
|
| 1701 |
+
{ url = "https://files.pythonhosted.org/packages/f7/90/4fb5056e5f03a7048abd2b11f598d464f0c167de4f2a51aa868c376b8c70/regex-2025.11.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:eadade04221641516fa25139273505a1c19f9bf97589a05bc4cfcd8b4a618031", size = 488081, upload-time = "2025-11-03T21:31:11.946Z" },
|
| 1702 |
+
{ url = "https://files.pythonhosted.org/packages/85/23/63e481293fac8b069d84fba0299b6666df720d875110efd0338406b5d360/regex-2025.11.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:feff9e54ec0dd3833d659257f5c3f5322a12eee58ffa360984b716f8b92983f4", size = 290554, upload-time = "2025-11-03T21:31:13.387Z" },
|
| 1703 |
+
{ url = "https://files.pythonhosted.org/packages/2b/9d/b101d0262ea293a0066b4522dfb722eb6a8785a8c3e084396a5f2c431a46/regex-2025.11.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3b30bc921d50365775c09a7ed446359e5c0179e9e2512beec4a60cbcef6ddd50", size = 288407, upload-time = "2025-11-03T21:31:14.809Z" },
|
| 1704 |
+
{ url = "https://files.pythonhosted.org/packages/0c/64/79241c8209d5b7e00577ec9dca35cd493cc6be35b7d147eda367d6179f6d/regex-2025.11.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f99be08cfead2020c7ca6e396c13543baea32343b7a9a5780c462e323bd8872f", size = 793418, upload-time = "2025-11-03T21:31:16.556Z" },
|
| 1705 |
+
{ url = "https://files.pythonhosted.org/packages/3d/e2/23cd5d3573901ce8f9757c92ca4db4d09600b865919b6d3e7f69f03b1afd/regex-2025.11.3-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6dd329a1b61c0ee95ba95385fb0c07ea0d3fe1a21e1349fa2bec272636217118", size = 860448, upload-time = "2025-11-03T21:31:18.12Z" },
|
| 1706 |
+
{ url = "https://files.pythonhosted.org/packages/2a/4c/aecf31beeaa416d0ae4ecb852148d38db35391aac19c687b5d56aedf3a8b/regex-2025.11.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:4c5238d32f3c5269d9e87be0cf096437b7622b6920f5eac4fd202468aaeb34d2", size = 907139, upload-time = "2025-11-03T21:31:20.753Z" },
|
| 1707 |
+
{ url = "https://files.pythonhosted.org/packages/61/22/b8cb00df7d2b5e0875f60628594d44dba283e951b1ae17c12f99e332cc0a/regex-2025.11.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:10483eefbfb0adb18ee9474498c9a32fcf4e594fbca0543bb94c48bac6183e2e", size = 800439, upload-time = "2025-11-03T21:31:22.069Z" },
|
| 1708 |
+
{ url = "https://files.pythonhosted.org/packages/02/a8/c4b20330a5cdc7a8eb265f9ce593f389a6a88a0c5f280cf4d978f33966bc/regex-2025.11.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:78c2d02bb6e1da0720eedc0bad578049cad3f71050ef8cd065ecc87691bed2b0", size = 782965, upload-time = "2025-11-03T21:31:23.598Z" },
|
| 1709 |
+
{ url = "https://files.pythonhosted.org/packages/b4/4c/ae3e52988ae74af4b04d2af32fee4e8077f26e51b62ec2d12d246876bea2/regex-2025.11.3-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:e6b49cd2aad93a1790ce9cffb18964f6d3a4b0b3dbdbd5de094b65296fce6e58", size = 854398, upload-time = "2025-11-03T21:31:25.008Z" },
|
| 1710 |
+
{ url = "https://files.pythonhosted.org/packages/06/d1/a8b9cf45874eda14b2e275157ce3b304c87e10fb38d9fc26a6e14eb18227/regex-2025.11.3-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:885b26aa3ee56433b630502dc3d36ba78d186a00cc535d3806e6bfd9ed3c70ab", size = 845897, upload-time = "2025-11-03T21:31:26.427Z" },
|
| 1711 |
+
{ url = "https://files.pythonhosted.org/packages/ea/fe/1830eb0236be93d9b145e0bd8ab499f31602fe0999b1f19e99955aa8fe20/regex-2025.11.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ddd76a9f58e6a00f8772e72cff8ebcff78e022be95edf018766707c730593e1e", size = 788906, upload-time = "2025-11-03T21:31:28.078Z" },
|
| 1712 |
+
{ url = "https://files.pythonhosted.org/packages/66/47/dc2577c1f95f188c1e13e2e69d8825a5ac582ac709942f8a03af42ed6e93/regex-2025.11.3-cp311-cp311-win32.whl", hash = "sha256:3e816cc9aac1cd3cc9a4ec4d860f06d40f994b5c7b4d03b93345f44e08cc68bf", size = 265812, upload-time = "2025-11-03T21:31:29.72Z" },
|
| 1713 |
+
{ url = "https://files.pythonhosted.org/packages/50/1e/15f08b2f82a9bbb510621ec9042547b54d11e83cb620643ebb54e4eb7d71/regex-2025.11.3-cp311-cp311-win_amd64.whl", hash = "sha256:087511f5c8b7dfbe3a03f5d5ad0c2a33861b1fc387f21f6f60825a44865a385a", size = 277737, upload-time = "2025-11-03T21:31:31.422Z" },
|
| 1714 |
+
{ url = "https://files.pythonhosted.org/packages/f4/fc/6500eb39f5f76c5e47a398df82e6b535a5e345f839581012a418b16f9cc3/regex-2025.11.3-cp311-cp311-win_arm64.whl", hash = "sha256:1ff0d190c7f68ae7769cd0313fe45820ba07ffebfddfaa89cc1eb70827ba0ddc", size = 270290, upload-time = "2025-11-03T21:31:33.041Z" },
|
| 1715 |
+
{ url = "https://files.pythonhosted.org/packages/e8/74/18f04cb53e58e3fb107439699bd8375cf5a835eec81084e0bddbd122e4c2/regex-2025.11.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:bc8ab71e2e31b16e40868a40a69007bc305e1109bd4658eb6cad007e0bf67c41", size = 489312, upload-time = "2025-11-03T21:31:34.343Z" },
|
| 1716 |
+
{ url = "https://files.pythonhosted.org/packages/78/3f/37fcdd0d2b1e78909108a876580485ea37c91e1acf66d3bb8e736348f441/regex-2025.11.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:22b29dda7e1f7062a52359fca6e58e548e28c6686f205e780b02ad8ef710de36", size = 291256, upload-time = "2025-11-03T21:31:35.675Z" },
|
| 1717 |
+
{ url = "https://files.pythonhosted.org/packages/bf/26/0a575f58eb23b7ebd67a45fccbc02ac030b737b896b7e7a909ffe43ffd6a/regex-2025.11.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3a91e4a29938bc1a082cc28fdea44be420bf2bebe2665343029723892eb073e1", size = 288921, upload-time = "2025-11-03T21:31:37.07Z" },
|
| 1718 |
+
{ url = "https://files.pythonhosted.org/packages/ea/98/6a8dff667d1af907150432cf5abc05a17ccd32c72a3615410d5365ac167a/regex-2025.11.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:08b884f4226602ad40c5d55f52bf91a9df30f513864e0054bad40c0e9cf1afb7", size = 798568, upload-time = "2025-11-03T21:31:38.784Z" },
|
| 1719 |
+
{ url = "https://files.pythonhosted.org/packages/64/15/92c1db4fa4e12733dd5a526c2dd2b6edcbfe13257e135fc0f6c57f34c173/regex-2025.11.3-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:3e0b11b2b2433d1c39c7c7a30e3f3d0aeeea44c2a8d0bae28f6b95f639927a69", size = 864165, upload-time = "2025-11-03T21:31:40.559Z" },
|
| 1720 |
+
{ url = "https://files.pythonhosted.org/packages/f9/e7/3ad7da8cdee1ce66c7cd37ab5ab05c463a86ffeb52b1a25fe7bd9293b36c/regex-2025.11.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:87eb52a81ef58c7ba4d45c3ca74e12aa4b4e77816f72ca25258a85b3ea96cb48", size = 912182, upload-time = "2025-11-03T21:31:42.002Z" },
|
| 1721 |
+
{ url = "https://files.pythonhosted.org/packages/84/bd/9ce9f629fcb714ffc2c3faf62b6766ecb7a585e1e885eb699bcf130a5209/regex-2025.11.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a12ab1f5c29b4e93db518f5e3872116b7e9b1646c9f9f426f777b50d44a09e8c", size = 803501, upload-time = "2025-11-03T21:31:43.815Z" },
|
| 1722 |
+
{ url = "https://files.pythonhosted.org/packages/7c/0f/8dc2e4349d8e877283e6edd6c12bdcebc20f03744e86f197ab6e4492bf08/regex-2025.11.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:7521684c8c7c4f6e88e35ec89680ee1aa8358d3f09d27dfbdf62c446f5d4c695", size = 787842, upload-time = "2025-11-03T21:31:45.353Z" },
|
| 1723 |
+
{ url = "https://files.pythonhosted.org/packages/f9/73/cff02702960bc185164d5619c0c62a2f598a6abff6695d391b096237d4ab/regex-2025.11.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:7fe6e5440584e94cc4b3f5f4d98a25e29ca12dccf8873679a635638349831b98", size = 858519, upload-time = "2025-11-03T21:31:46.814Z" },
|
| 1724 |
+
{ url = "https://files.pythonhosted.org/packages/61/83/0e8d1ae71e15bc1dc36231c90b46ee35f9d52fab2e226b0e039e7ea9c10a/regex-2025.11.3-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:8e026094aa12b43f4fd74576714e987803a315c76edb6b098b9809db5de58f74", size = 850611, upload-time = "2025-11-03T21:31:48.289Z" },
|
| 1725 |
+
{ url = "https://files.pythonhosted.org/packages/c8/f5/70a5cdd781dcfaa12556f2955bf170cd603cb1c96a1827479f8faea2df97/regex-2025.11.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:435bbad13e57eb5606a68443af62bed3556de2f46deb9f7d4237bc2f1c9fb3a0", size = 789759, upload-time = "2025-11-03T21:31:49.759Z" },
|
| 1726 |
+
{ url = "https://files.pythonhosted.org/packages/59/9b/7c29be7903c318488983e7d97abcf8ebd3830e4c956c4c540005fcfb0462/regex-2025.11.3-cp312-cp312-win32.whl", hash = "sha256:3839967cf4dc4b985e1570fd8d91078f0c519f30491c60f9ac42a8db039be204", size = 266194, upload-time = "2025-11-03T21:31:51.53Z" },
|
| 1727 |
+
{ url = "https://files.pythonhosted.org/packages/1a/67/3b92df89f179d7c367be654ab5626ae311cb28f7d5c237b6bb976cd5fbbb/regex-2025.11.3-cp312-cp312-win_amd64.whl", hash = "sha256:e721d1b46e25c481dc5ded6f4b3f66c897c58d2e8cfdf77bbced84339108b0b9", size = 277069, upload-time = "2025-11-03T21:31:53.151Z" },
|
| 1728 |
+
{ url = "https://files.pythonhosted.org/packages/d7/55/85ba4c066fe5094d35b249c3ce8df0ba623cfd35afb22d6764f23a52a1c5/regex-2025.11.3-cp312-cp312-win_arm64.whl", hash = "sha256:64350685ff08b1d3a6fff33f45a9ca183dc1d58bbfe4981604e70ec9801bbc26", size = 270330, upload-time = "2025-11-03T21:31:54.514Z" },
|
| 1729 |
+
{ url = "https://files.pythonhosted.org/packages/e1/a7/dda24ebd49da46a197436ad96378f17df30ceb40e52e859fc42cac45b850/regex-2025.11.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:c1e448051717a334891f2b9a620fe36776ebf3dd8ec46a0b877c8ae69575feb4", size = 489081, upload-time = "2025-11-03T21:31:55.9Z" },
|
| 1730 |
+
{ url = "https://files.pythonhosted.org/packages/19/22/af2dc751aacf88089836aa088a1a11c4f21a04707eb1b0478e8e8fb32847/regex-2025.11.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:9b5aca4d5dfd7fbfbfbdaf44850fcc7709a01146a797536a8f84952e940cca76", size = 291123, upload-time = "2025-11-03T21:31:57.758Z" },
|
| 1731 |
+
{ url = "https://files.pythonhosted.org/packages/a3/88/1a3ea5672f4b0a84802ee9891b86743438e7c04eb0b8f8c4e16a42375327/regex-2025.11.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:04d2765516395cf7dda331a244a3282c0f5ae96075f728629287dfa6f76ba70a", size = 288814, upload-time = "2025-11-03T21:32:01.12Z" },
|
| 1732 |
+
{ url = "https://files.pythonhosted.org/packages/fb/8c/f5987895bf42b8ddeea1b315c9fedcfe07cadee28b9c98cf50d00adcb14d/regex-2025.11.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5d9903ca42bfeec4cebedba8022a7c97ad2aab22e09573ce9976ba01b65e4361", size = 798592, upload-time = "2025-11-03T21:32:03.006Z" },
|
| 1733 |
+
{ url = "https://files.pythonhosted.org/packages/99/2a/6591ebeede78203fa77ee46a1c36649e02df9eaa77a033d1ccdf2fcd5d4e/regex-2025.11.3-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:639431bdc89d6429f6721625e8129413980ccd62e9d3f496be618a41d205f160", size = 864122, upload-time = "2025-11-03T21:32:04.553Z" },
|
| 1734 |
+
{ url = "https://files.pythonhosted.org/packages/94/d6/be32a87cf28cf8ed064ff281cfbd49aefd90242a83e4b08b5a86b38e8eb4/regex-2025.11.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f117efad42068f9715677c8523ed2be1518116d1c49b1dd17987716695181efe", size = 912272, upload-time = "2025-11-03T21:32:06.148Z" },
|
| 1735 |
+
{ url = "https://files.pythonhosted.org/packages/62/11/9bcef2d1445665b180ac7f230406ad80671f0fc2a6ffb93493b5dd8cd64c/regex-2025.11.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4aecb6f461316adf9f1f0f6a4a1a3d79e045f9b71ec76055a791affa3b285850", size = 803497, upload-time = "2025-11-03T21:32:08.162Z" },
|
| 1736 |
+
{ url = "https://files.pythonhosted.org/packages/e5/a7/da0dc273d57f560399aa16d8a68ae7f9b57679476fc7ace46501d455fe84/regex-2025.11.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:3b3a5f320136873cc5561098dfab677eea139521cb9a9e8db98b7e64aef44cbc", size = 787892, upload-time = "2025-11-03T21:32:09.769Z" },
|
| 1737 |
+
{ url = "https://files.pythonhosted.org/packages/da/4b/732a0c5a9736a0b8d6d720d4945a2f1e6f38f87f48f3173559f53e8d5d82/regex-2025.11.3-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:75fa6f0056e7efb1f42a1c34e58be24072cb9e61a601340cc1196ae92326a4f9", size = 858462, upload-time = "2025-11-03T21:32:11.769Z" },
|
| 1738 |
+
{ url = "https://files.pythonhosted.org/packages/0c/f5/a2a03df27dc4c2d0c769220f5110ba8c4084b0bfa9ab0f9b4fcfa3d2b0fc/regex-2025.11.3-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:dbe6095001465294f13f1adcd3311e50dd84e5a71525f20a10bd16689c61ce0b", size = 850528, upload-time = "2025-11-03T21:32:13.906Z" },
|
| 1739 |
+
{ url = "https://files.pythonhosted.org/packages/d6/09/e1cd5bee3841c7f6eb37d95ca91cdee7100b8f88b81e41c2ef426910891a/regex-2025.11.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:454d9b4ae7881afbc25015b8627c16d88a597479b9dea82b8c6e7e2e07240dc7", size = 789866, upload-time = "2025-11-03T21:32:15.748Z" },
|
| 1740 |
+
{ url = "https://files.pythonhosted.org/packages/eb/51/702f5ea74e2a9c13d855a6a85b7f80c30f9e72a95493260193c07f3f8d74/regex-2025.11.3-cp313-cp313-win32.whl", hash = "sha256:28ba4d69171fc6e9896337d4fc63a43660002b7da53fc15ac992abcf3410917c", size = 266189, upload-time = "2025-11-03T21:32:17.493Z" },
|
| 1741 |
+
{ url = "https://files.pythonhosted.org/packages/8b/00/6e29bb314e271a743170e53649db0fdb8e8ff0b64b4f425f5602f4eb9014/regex-2025.11.3-cp313-cp313-win_amd64.whl", hash = "sha256:bac4200befe50c670c405dc33af26dad5a3b6b255dd6c000d92fe4629f9ed6a5", size = 277054, upload-time = "2025-11-03T21:32:19.042Z" },
|
| 1742 |
+
{ url = "https://files.pythonhosted.org/packages/25/f1/b156ff9f2ec9ac441710764dda95e4edaf5f36aca48246d1eea3f1fd96ec/regex-2025.11.3-cp313-cp313-win_arm64.whl", hash = "sha256:2292cd5a90dab247f9abe892ac584cb24f0f54680c73fcb4a7493c66c2bf2467", size = 270325, upload-time = "2025-11-03T21:32:21.338Z" },
|
| 1743 |
+
{ url = "https://files.pythonhosted.org/packages/20/28/fd0c63357caefe5680b8ea052131acbd7f456893b69cc2a90cc3e0dc90d4/regex-2025.11.3-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:1eb1ebf6822b756c723e09f5186473d93236c06c579d2cc0671a722d2ab14281", size = 491984, upload-time = "2025-11-03T21:32:23.466Z" },
|
| 1744 |
+
{ url = "https://files.pythonhosted.org/packages/df/ec/7014c15626ab46b902b3bcc4b28a7bae46d8f281fc7ea9c95e22fcaaa917/regex-2025.11.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:1e00ec2970aab10dc5db34af535f21fcf32b4a31d99e34963419636e2f85ae39", size = 292673, upload-time = "2025-11-03T21:32:25.034Z" },
|
| 1745 |
+
{ url = "https://files.pythonhosted.org/packages/23/ab/3b952ff7239f20d05f1f99e9e20188513905f218c81d52fb5e78d2bf7634/regex-2025.11.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a4cb042b615245d5ff9b3794f56be4138b5adc35a4166014d31d1814744148c7", size = 291029, upload-time = "2025-11-03T21:32:26.528Z" },
|
| 1746 |
+
{ url = "https://files.pythonhosted.org/packages/21/7e/3dc2749fc684f455f162dcafb8a187b559e2614f3826877d3844a131f37b/regex-2025.11.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:44f264d4bf02f3176467d90b294d59bf1db9fe53c141ff772f27a8b456b2a9ed", size = 807437, upload-time = "2025-11-03T21:32:28.363Z" },
|
| 1747 |
+
{ url = "https://files.pythonhosted.org/packages/1b/0b/d529a85ab349c6a25d1ca783235b6e3eedf187247eab536797021f7126c6/regex-2025.11.3-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7be0277469bf3bd7a34a9c57c1b6a724532a0d235cd0dc4e7f4316f982c28b19", size = 873368, upload-time = "2025-11-03T21:32:30.4Z" },
|
| 1748 |
+
{ url = "https://files.pythonhosted.org/packages/7d/18/2d868155f8c9e3e9d8f9e10c64e9a9f496bb8f7e037a88a8bed26b435af6/regex-2025.11.3-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0d31e08426ff4b5b650f68839f5af51a92a5b51abd8554a60c2fbc7c71f25d0b", size = 914921, upload-time = "2025-11-03T21:32:32.123Z" },
|
| 1749 |
+
{ url = "https://files.pythonhosted.org/packages/2d/71/9d72ff0f354fa783fe2ba913c8734c3b433b86406117a8db4ea2bf1c7a2f/regex-2025.11.3-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e43586ce5bd28f9f285a6e729466841368c4a0353f6fd08d4ce4630843d3648a", size = 812708, upload-time = "2025-11-03T21:32:34.305Z" },
|
| 1750 |
+
{ url = "https://files.pythonhosted.org/packages/e7/19/ce4bf7f5575c97f82b6e804ffb5c4e940c62609ab2a0d9538d47a7fdf7d4/regex-2025.11.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:0f9397d561a4c16829d4e6ff75202c1c08b68a3bdbfe29dbfcdb31c9830907c6", size = 795472, upload-time = "2025-11-03T21:32:36.364Z" },
|
| 1751 |
+
{ url = "https://files.pythonhosted.org/packages/03/86/fd1063a176ffb7b2315f9a1b08d17b18118b28d9df163132615b835a26ee/regex-2025.11.3-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:dd16e78eb18ffdb25ee33a0682d17912e8cc8a770e885aeee95020046128f1ce", size = 868341, upload-time = "2025-11-03T21:32:38.042Z" },
|
| 1752 |
+
{ url = "https://files.pythonhosted.org/packages/12/43/103fb2e9811205e7386366501bc866a164a0430c79dd59eac886a2822950/regex-2025.11.3-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:ffcca5b9efe948ba0661e9df0fa50d2bc4b097c70b9810212d6b62f05d83b2dd", size = 854666, upload-time = "2025-11-03T21:32:40.079Z" },
|
| 1753 |
+
{ url = "https://files.pythonhosted.org/packages/7d/22/e392e53f3869b75804762c7c848bd2dd2abf2b70fb0e526f58724638bd35/regex-2025.11.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:c56b4d162ca2b43318ac671c65bd4d563e841a694ac70e1a976ac38fcf4ca1d2", size = 799473, upload-time = "2025-11-03T21:32:42.148Z" },
|
| 1754 |
+
{ url = "https://files.pythonhosted.org/packages/4f/f9/8bd6b656592f925b6845fcbb4d57603a3ac2fb2373344ffa1ed70aa6820a/regex-2025.11.3-cp313-cp313t-win32.whl", hash = "sha256:9ddc42e68114e161e51e272f667d640f97e84a2b9ef14b7477c53aac20c2d59a", size = 268792, upload-time = "2025-11-03T21:32:44.13Z" },
|
| 1755 |
+
{ url = "https://files.pythonhosted.org/packages/e5/87/0e7d603467775ff65cd2aeabf1b5b50cc1c3708556a8b849a2fa4dd1542b/regex-2025.11.3-cp313-cp313t-win_amd64.whl", hash = "sha256:7a7c7fdf755032ffdd72c77e3d8096bdcb0eb92e89e17571a196f03d88b11b3c", size = 280214, upload-time = "2025-11-03T21:32:45.853Z" },
|
| 1756 |
+
{ url = "https://files.pythonhosted.org/packages/8d/d0/2afc6f8e94e2b64bfb738a7c2b6387ac1699f09f032d363ed9447fd2bb57/regex-2025.11.3-cp313-cp313t-win_arm64.whl", hash = "sha256:df9eb838c44f570283712e7cff14c16329a9f0fb19ca492d21d4b7528ee6821e", size = 271469, upload-time = "2025-11-03T21:32:48.026Z" },
|
| 1757 |
+
{ url = "https://files.pythonhosted.org/packages/31/e9/f6e13de7e0983837f7b6d238ad9458800a874bf37c264f7923e63409944c/regex-2025.11.3-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:9697a52e57576c83139d7c6f213d64485d3df5bf84807c35fa409e6c970801c6", size = 489089, upload-time = "2025-11-03T21:32:50.027Z" },
|
| 1758 |
+
{ url = "https://files.pythonhosted.org/packages/a3/5c/261f4a262f1fa65141c1b74b255988bd2fa020cc599e53b080667d591cfc/regex-2025.11.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:e18bc3f73bd41243c9b38a6d9f2366cd0e0137a9aebe2d8ff76c5b67d4c0a3f4", size = 291059, upload-time = "2025-11-03T21:32:51.682Z" },
|
| 1759 |
+
{ url = "https://files.pythonhosted.org/packages/8e/57/f14eeb7f072b0e9a5a090d1712741fd8f214ec193dba773cf5410108bb7d/regex-2025.11.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:61a08bcb0ec14ff4e0ed2044aad948d0659604f824cbd50b55e30b0ec6f09c73", size = 288900, upload-time = "2025-11-03T21:32:53.569Z" },
|
| 1760 |
+
{ url = "https://files.pythonhosted.org/packages/3c/6b/1d650c45e99a9b327586739d926a1cd4e94666b1bd4af90428b36af66dc7/regex-2025.11.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c9c30003b9347c24bcc210958c5d167b9e4f9be786cb380a7d32f14f9b84674f", size = 799010, upload-time = "2025-11-03T21:32:55.222Z" },
|
| 1761 |
+
{ url = "https://files.pythonhosted.org/packages/99/ee/d66dcbc6b628ce4e3f7f0cbbb84603aa2fc0ffc878babc857726b8aab2e9/regex-2025.11.3-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4e1e592789704459900728d88d41a46fe3969b82ab62945560a31732ffc19a6d", size = 864893, upload-time = "2025-11-03T21:32:57.239Z" },
|
| 1762 |
+
{ url = "https://files.pythonhosted.org/packages/bf/2d/f238229f1caba7ac87a6c4153d79947fb0261415827ae0f77c304260c7d3/regex-2025.11.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:6538241f45eb5a25aa575dbba1069ad786f68a4f2773a29a2bd3dd1f9de787be", size = 911522, upload-time = "2025-11-03T21:32:59.274Z" },
|
| 1763 |
+
{ url = "https://files.pythonhosted.org/packages/bd/3d/22a4eaba214a917c80e04f6025d26143690f0419511e0116508e24b11c9b/regex-2025.11.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bce22519c989bb72a7e6b36a199384c53db7722fe669ba891da75907fe3587db", size = 803272, upload-time = "2025-11-03T21:33:01.393Z" },
|
| 1764 |
+
{ url = "https://files.pythonhosted.org/packages/84/b1/03188f634a409353a84b5ef49754b97dbcc0c0f6fd6c8ede505a8960a0a4/regex-2025.11.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:66d559b21d3640203ab9075797a55165d79017520685fb407b9234d72ab63c62", size = 787958, upload-time = "2025-11-03T21:33:03.379Z" },
|
| 1765 |
+
{ url = "https://files.pythonhosted.org/packages/99/6a/27d072f7fbf6fadd59c64d210305e1ff865cc3b78b526fd147db768c553b/regex-2025.11.3-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:669dcfb2e38f9e8c69507bace46f4889e3abbfd9b0c29719202883c0a603598f", size = 859289, upload-time = "2025-11-03T21:33:05.374Z" },
|
| 1766 |
+
{ url = "https://files.pythonhosted.org/packages/9a/70/1b3878f648e0b6abe023172dacb02157e685564853cc363d9961bcccde4e/regex-2025.11.3-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:32f74f35ff0f25a5021373ac61442edcb150731fbaa28286bbc8bb1582c89d02", size = 850026, upload-time = "2025-11-03T21:33:07.131Z" },
|
| 1767 |
+
{ url = "https://files.pythonhosted.org/packages/dd/d5/68e25559b526b8baab8e66839304ede68ff6727237a47727d240006bd0ff/regex-2025.11.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:e6c7a21dffba883234baefe91bc3388e629779582038f75d2a5be918e250f0ed", size = 789499, upload-time = "2025-11-03T21:33:09.141Z" },
|
| 1768 |
+
{ url = "https://files.pythonhosted.org/packages/fc/df/43971264857140a350910d4e33df725e8c94dd9dee8d2e4729fa0d63d49e/regex-2025.11.3-cp314-cp314-win32.whl", hash = "sha256:795ea137b1d809eb6836b43748b12634291c0ed55ad50a7d72d21edf1cd565c4", size = 271604, upload-time = "2025-11-03T21:33:10.9Z" },
|
| 1769 |
+
{ url = "https://files.pythonhosted.org/packages/01/6f/9711b57dc6894a55faf80a4c1b5aa4f8649805cb9c7aef46f7d27e2b9206/regex-2025.11.3-cp314-cp314-win_amd64.whl", hash = "sha256:9f95fbaa0ee1610ec0fc6b26668e9917a582ba80c52cc6d9ada15e30aa9ab9ad", size = 280320, upload-time = "2025-11-03T21:33:12.572Z" },
|
| 1770 |
+
{ url = "https://files.pythonhosted.org/packages/f1/7e/f6eaa207d4377481f5e1775cdeb5a443b5a59b392d0065f3417d31d80f87/regex-2025.11.3-cp314-cp314-win_arm64.whl", hash = "sha256:dfec44d532be4c07088c3de2876130ff0fbeeacaa89a137decbbb5f665855a0f", size = 273372, upload-time = "2025-11-03T21:33:14.219Z" },
|
| 1771 |
+
{ url = "https://files.pythonhosted.org/packages/c3/06/49b198550ee0f5e4184271cee87ba4dfd9692c91ec55289e6282f0f86ccf/regex-2025.11.3-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:ba0d8a5d7f04f73ee7d01d974d47c5834f8a1b0224390e4fe7c12a3a92a78ecc", size = 491985, upload-time = "2025-11-03T21:33:16.555Z" },
|
| 1772 |
+
{ url = "https://files.pythonhosted.org/packages/ce/bf/abdafade008f0b1c9da10d934034cb670432d6cf6cbe38bbb53a1cfd6cf8/regex-2025.11.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:442d86cf1cfe4faabf97db7d901ef58347efd004934da045c745e7b5bd57ac49", size = 292669, upload-time = "2025-11-03T21:33:18.32Z" },
|
| 1773 |
+
{ url = "https://files.pythonhosted.org/packages/f9/ef/0c357bb8edbd2ad8e273fcb9e1761bc37b8acbc6e1be050bebd6475f19c1/regex-2025.11.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:fd0a5e563c756de210bb964789b5abe4f114dacae9104a47e1a649b910361536", size = 291030, upload-time = "2025-11-03T21:33:20.048Z" },
|
| 1774 |
+
{ url = "https://files.pythonhosted.org/packages/79/06/edbb67257596649b8fb088d6aeacbcb248ac195714b18a65e018bf4c0b50/regex-2025.11.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bf3490bcbb985a1ae97b2ce9ad1c0f06a852d5b19dde9b07bdf25bf224248c95", size = 807674, upload-time = "2025-11-03T21:33:21.797Z" },
|
| 1775 |
+
{ url = "https://files.pythonhosted.org/packages/f4/d9/ad4deccfce0ea336296bd087f1a191543bb99ee1c53093dcd4c64d951d00/regex-2025.11.3-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:3809988f0a8b8c9dcc0f92478d6501fac7200b9ec56aecf0ec21f4a2ec4b6009", size = 873451, upload-time = "2025-11-03T21:33:23.741Z" },
|
| 1776 |
+
{ url = "https://files.pythonhosted.org/packages/13/75/a55a4724c56ef13e3e04acaab29df26582f6978c000ac9cd6810ad1f341f/regex-2025.11.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f4ff94e58e84aedb9c9fce66d4ef9f27a190285b451420f297c9a09f2b9abee9", size = 914980, upload-time = "2025-11-03T21:33:25.999Z" },
|
| 1777 |
+
{ url = "https://files.pythonhosted.org/packages/67/1e/a1657ee15bd9116f70d4a530c736983eed997b361e20ecd8f5ca3759d5c5/regex-2025.11.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7eb542fd347ce61e1321b0a6b945d5701528dca0cd9759c2e3bb8bd57e47964d", size = 812852, upload-time = "2025-11-03T21:33:27.852Z" },
|
| 1778 |
+
{ url = "https://files.pythonhosted.org/packages/b8/6f/f7516dde5506a588a561d296b2d0044839de06035bb486b326065b4c101e/regex-2025.11.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:d6c2d5919075a1f2e413c00b056ea0c2f065b3f5fe83c3d07d325ab92dce51d6", size = 795566, upload-time = "2025-11-03T21:33:32.364Z" },
|
| 1779 |
+
{ url = "https://files.pythonhosted.org/packages/d9/dd/3d10b9e170cc16fb34cb2cef91513cf3df65f440b3366030631b2984a264/regex-2025.11.3-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:3f8bf11a4827cc7ce5a53d4ef6cddd5ad25595d3c1435ef08f76825851343154", size = 868463, upload-time = "2025-11-03T21:33:34.459Z" },
|
| 1780 |
+
{ url = "https://files.pythonhosted.org/packages/f5/8e/935e6beff1695aa9085ff83195daccd72acc82c81793df480f34569330de/regex-2025.11.3-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:22c12d837298651e5550ac1d964e4ff57c3f56965fc1812c90c9fb2028eaf267", size = 854694, upload-time = "2025-11-03T21:33:36.793Z" },
|
| 1781 |
+
{ url = "https://files.pythonhosted.org/packages/92/12/10650181a040978b2f5720a6a74d44f841371a3d984c2083fc1752e4acf6/regex-2025.11.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:62ba394a3dda9ad41c7c780f60f6e4a70988741415ae96f6d1bf6c239cf01379", size = 799691, upload-time = "2025-11-03T21:33:39.079Z" },
|
| 1782 |
+
{ url = "https://files.pythonhosted.org/packages/67/90/8f37138181c9a7690e7e4cb388debbd389342db3c7381d636d2875940752/regex-2025.11.3-cp314-cp314t-win32.whl", hash = "sha256:4bf146dca15cdd53224a1bf46d628bd7590e4a07fbb69e720d561aea43a32b38", size = 274583, upload-time = "2025-11-03T21:33:41.302Z" },
|
| 1783 |
+
{ url = "https://files.pythonhosted.org/packages/8f/cd/867f5ec442d56beb56f5f854f40abcfc75e11d10b11fdb1869dd39c63aaf/regex-2025.11.3-cp314-cp314t-win_amd64.whl", hash = "sha256:adad1a1bcf1c9e76346e091d22d23ac54ef28e1365117d99521631078dfec9de", size = 284286, upload-time = "2025-11-03T21:33:43.324Z" },
|
| 1784 |
+
{ url = "https://files.pythonhosted.org/packages/20/31/32c0c4610cbc070362bf1d2e4ea86d1ea29014d400a6d6c2486fcfd57766/regex-2025.11.3-cp314-cp314t-win_arm64.whl", hash = "sha256:c54f768482cef41e219720013cd05933b6f971d9562544d691c68699bf2b6801", size = 274741, upload-time = "2025-11-03T21:33:45.557Z" },
|
| 1785 |
+
]
|
| 1786 |
+
|
| 1787 |
[[package]]
|
| 1788 |
name = "requests"
|
| 1789 |
version = "2.31.0"
|
|
|
|
| 1944 |
{ url = "https://files.pythonhosted.org/packages/a9/9b/770da4f22ea69fdd19ac6cb183f02abf8fc6f8d16a9dc00dfaf667649ee7/ruff-0.1.11-py3-none-win_arm64.whl", hash = "sha256:97ce4d752f964ba559c7023a86e5f8e97f026d511e48013987623915431c7ea9", size = 6827536, upload-time = "2024-01-02T22:59:08.394Z" },
|
| 1945 |
]
|
| 1946 |
|
| 1947 |
+
[[package]]
|
| 1948 |
+
name = "safetensors"
|
| 1949 |
+
version = "0.7.0"
|
| 1950 |
+
source = { registry = "https://pypi.org/simple" }
|
| 1951 |
+
sdist = { url = "https://files.pythonhosted.org/packages/29/9c/6e74567782559a63bd040a236edca26fd71bc7ba88de2ef35d75df3bca5e/safetensors-0.7.0.tar.gz", hash = "sha256:07663963b67e8bd9f0b8ad15bb9163606cd27cc5a1b96235a50d8369803b96b0", size = 200878, upload-time = "2025-11-19T15:18:43.199Z" }
|
| 1952 |
+
wheels = [
|
| 1953 |
+
{ url = "https://files.pythonhosted.org/packages/fa/47/aef6c06649039accf914afef490268e1067ed82be62bcfa5b7e886ad15e8/safetensors-0.7.0-cp38-abi3-macosx_10_12_x86_64.whl", hash = "sha256:c82f4d474cf725255d9e6acf17252991c3c8aac038d6ef363a4bf8be2f6db517", size = 467781, upload-time = "2025-11-19T15:18:35.84Z" },
|
| 1954 |
+
{ url = "https://files.pythonhosted.org/packages/e8/00/374c0c068e30cd31f1e1b46b4b5738168ec79e7689ca82ee93ddfea05109/safetensors-0.7.0-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:94fd4858284736bb67a897a41608b5b0c2496c9bdb3bf2af1fa3409127f20d57", size = 447058, upload-time = "2025-11-19T15:18:34.416Z" },
|
| 1955 |
+
{ url = "https://files.pythonhosted.org/packages/f1/06/578ffed52c2296f93d7fd2d844cabfa92be51a587c38c8afbb8ae449ca89/safetensors-0.7.0-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e07d91d0c92a31200f25351f4acb2bc6aff7f48094e13ebb1d0fb995b54b6542", size = 491748, upload-time = "2025-11-19T15:18:09.79Z" },
|
| 1956 |
+
{ url = "https://files.pythonhosted.org/packages/ae/33/1debbbb70e4791dde185edb9413d1fe01619255abb64b300157d7f15dddd/safetensors-0.7.0-cp38-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8469155f4cb518bafb4acf4865e8bb9d6804110d2d9bdcaa78564b9fd841e104", size = 503881, upload-time = "2025-11-19T15:18:16.145Z" },
|
| 1957 |
+
{ url = "https://files.pythonhosted.org/packages/8e/1c/40c2ca924d60792c3be509833df711b553c60effbd91da6f5284a83f7122/safetensors-0.7.0-cp38-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:54bef08bf00a2bff599982f6b08e8770e09cc012d7bba00783fc7ea38f1fb37d", size = 623463, upload-time = "2025-11-19T15:18:21.11Z" },
|
| 1958 |
+
{ url = "https://files.pythonhosted.org/packages/9b/3a/13784a9364bd43b0d61eef4bea2845039bc2030458b16594a1bd787ae26e/safetensors-0.7.0-cp38-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:42cb091236206bb2016d245c377ed383aa7f78691748f3bb6ee1bfa51ae2ce6a", size = 532855, upload-time = "2025-11-19T15:18:25.719Z" },
|
| 1959 |
+
{ url = "https://files.pythonhosted.org/packages/a0/60/429e9b1cb3fc651937727befe258ea24122d9663e4d5709a48c9cbfceecb/safetensors-0.7.0-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dac7252938f0696ddea46f5e855dd3138444e82236e3be475f54929f0c510d48", size = 507152, upload-time = "2025-11-19T15:18:33.023Z" },
|
| 1960 |
+
{ url = "https://files.pythonhosted.org/packages/3c/a8/4b45e4e059270d17af60359713ffd83f97900d45a6afa73aaa0d737d48b6/safetensors-0.7.0-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1d060c70284127fa805085d8f10fbd0962792aed71879d00864acda69dbab981", size = 541856, upload-time = "2025-11-19T15:18:31.075Z" },
|
| 1961 |
+
{ url = "https://files.pythonhosted.org/packages/06/87/d26d8407c44175d8ae164a95b5a62707fcc445f3c0c56108e37d98070a3d/safetensors-0.7.0-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:cdab83a366799fa730f90a4ebb563e494f28e9e92c4819e556152ad55e43591b", size = 674060, upload-time = "2025-11-19T15:18:37.211Z" },
|
| 1962 |
+
{ url = "https://files.pythonhosted.org/packages/11/f5/57644a2ff08dc6325816ba7217e5095f17269dada2554b658442c66aed51/safetensors-0.7.0-cp38-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:672132907fcad9f2aedcb705b2d7b3b93354a2aec1b2f706c4db852abe338f85", size = 771715, upload-time = "2025-11-19T15:18:38.689Z" },
|
| 1963 |
+
{ url = "https://files.pythonhosted.org/packages/86/31/17883e13a814bd278ae6e266b13282a01049b0c81341da7fd0e3e71a80a3/safetensors-0.7.0-cp38-abi3-musllinux_1_2_i686.whl", hash = "sha256:5d72abdb8a4d56d4020713724ba81dac065fedb7f3667151c4a637f1d3fb26c0", size = 714377, upload-time = "2025-11-19T15:18:40.162Z" },
|
| 1964 |
+
{ url = "https://files.pythonhosted.org/packages/4a/d8/0c8a7dc9b41dcac53c4cbf9df2b9c83e0e0097203de8b37a712b345c0be5/safetensors-0.7.0-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:b0f6d66c1c538d5a94a73aa9ddca8ccc4227e6c9ff555322ea40bdd142391dd4", size = 677368, upload-time = "2025-11-19T15:18:41.627Z" },
|
| 1965 |
+
{ url = "https://files.pythonhosted.org/packages/05/e5/cb4b713c8a93469e3c5be7c3f8d77d307e65fe89673e731f5c2bfd0a9237/safetensors-0.7.0-cp38-abi3-win32.whl", hash = "sha256:c74af94bf3ac15ac4d0f2a7c7b4663a15f8c2ab15ed0fc7531ca61d0835eccba", size = 326423, upload-time = "2025-11-19T15:18:45.74Z" },
|
| 1966 |
+
{ url = "https://files.pythonhosted.org/packages/5d/e6/ec8471c8072382cb91233ba7267fd931219753bb43814cbc71757bfd4dab/safetensors-0.7.0-cp38-abi3-win_amd64.whl", hash = "sha256:d1239932053f56f3456f32eb9625590cc7582e905021f94636202a864d470755", size = 341380, upload-time = "2025-11-19T15:18:44.427Z" },
|
| 1967 |
+
]
|
| 1968 |
+
|
| 1969 |
[[package]]
|
| 1970 |
name = "scikit-learn"
|
| 1971 |
version = "1.3.2"
|
|
|
|
| 2061 |
{ url = "https://files.pythonhosted.org/packages/64/47/a494741db7280eae6dc033510c319e34d42dd41b7ac0c7ead39354d1a2b5/scipy-1.16.3-cp314-cp314t-win_arm64.whl", hash = "sha256:21d9d6b197227a12dcbf9633320a4e34c6b0e51c57268df255a0942983bac562", size = 26464127, upload-time = "2025-10-28T17:38:11.34Z" },
|
| 2062 |
]
|
| 2063 |
|
| 2064 |
+
[[package]]
|
| 2065 |
+
name = "sentencepiece"
|
| 2066 |
+
version = "0.2.1"
|
| 2067 |
+
source = { registry = "https://pypi.org/simple" }
|
| 2068 |
+
sdist = { url = "https://files.pythonhosted.org/packages/15/15/2e7a025fc62d764b151ae6d0f2a92f8081755ebe8d4a64099accc6f77ba6/sentencepiece-0.2.1.tar.gz", hash = "sha256:8138cec27c2f2282f4a34d9a016e3374cd40e5c6e9cb335063db66a0a3b71fad", size = 3228515, upload-time = "2025-08-12T07:00:51.718Z" }
|
| 2069 |
+
wheels = [
|
| 2070 |
+
{ url = "https://files.pythonhosted.org/packages/d8/15/46afbab00733d81788b64be430ca1b93011bb9388527958e26cc31832de5/sentencepiece-0.2.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6356d0986b8b8dc351b943150fcd81a1c6e6e4d439772e8584c64230e58ca987", size = 1942560, upload-time = "2025-08-12T06:59:25.82Z" },
|
| 2071 |
+
{ url = "https://files.pythonhosted.org/packages/fa/79/7c01b8ef98a0567e9d84a4e7a910f8e7074fcbf398a5cd76f93f4b9316f9/sentencepiece-0.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8f8ba89a3acb3dc1ae90f65ec1894b0b9596fdb98ab003ff38e058f898b39bc7", size = 1325385, upload-time = "2025-08-12T06:59:27.722Z" },
|
| 2072 |
+
{ url = "https://files.pythonhosted.org/packages/bb/88/2b41e07bd24f33dcf2f18ec3b74247aa4af3526bad8907b8727ea3caba03/sentencepiece-0.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:02593eca45440ef39247cee8c47322a34bdcc1d8ae83ad28ba5a899a2cf8d79a", size = 1253319, upload-time = "2025-08-12T06:59:29.306Z" },
|
| 2073 |
+
{ url = "https://files.pythonhosted.org/packages/a0/54/38a1af0c6210a3c6f95aa46d23d6640636d020fba7135cd0d9a84ada05a7/sentencepiece-0.2.1-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0a0d15781a171d188b661ae4bde1d998c303f6bd8621498c50c671bd45a4798e", size = 1316162, upload-time = "2025-08-12T06:59:30.914Z" },
|
| 2074 |
+
{ url = "https://files.pythonhosted.org/packages/ef/66/fb191403ade791ad2c3c1e72fe8413e63781b08cfa3aa4c9dfc536d6e795/sentencepiece-0.2.1-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4f5a3e0d9f445ed9d66c0fec47d4b23d12cfc858b407a03c194c1b26c2ac2a63", size = 1387785, upload-time = "2025-08-12T06:59:32.491Z" },
|
| 2075 |
+
{ url = "https://files.pythonhosted.org/packages/a9/2d/3bd9b08e70067b2124518b308db6a84a4f8901cc8a4317e2e4288cdd9b4d/sentencepiece-0.2.1-cp311-cp311-win32.whl", hash = "sha256:6d297a1748d429ba8534eebe5535448d78b8acc32d00a29b49acf28102eeb094", size = 999555, upload-time = "2025-08-12T06:59:34.475Z" },
|
| 2076 |
+
{ url = "https://files.pythonhosted.org/packages/32/b8/f709977f5fda195ae1ea24f24e7c581163b6f142b1005bc3d0bbfe4d7082/sentencepiece-0.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:82d9ead6591015f009cb1be1cb1c015d5e6f04046dbb8c9588b931e869a29728", size = 1054617, upload-time = "2025-08-12T06:59:36.461Z" },
|
| 2077 |
+
{ url = "https://files.pythonhosted.org/packages/7a/40/a1fc23be23067da0f703709797b464e8a30a1c78cc8a687120cd58d4d509/sentencepiece-0.2.1-cp311-cp311-win_arm64.whl", hash = "sha256:39f8651bd10974eafb9834ce30d9bcf5b73e1fc798a7f7d2528f9820ca86e119", size = 1033877, upload-time = "2025-08-12T06:59:38.391Z" },
|
| 2078 |
+
{ url = "https://files.pythonhosted.org/packages/4a/be/32ce495aa1d0e0c323dcb1ba87096037358edee539cac5baf8755a6bd396/sentencepiece-0.2.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:57cae326c8727de58c85977b175af132a7138d84c764635d7e71bbee7e774133", size = 1943152, upload-time = "2025-08-12T06:59:40.048Z" },
|
| 2079 |
+
{ url = "https://files.pythonhosted.org/packages/88/7e/ff23008899a58678e98c6ff592bf4d368eee5a71af96d0df6b38a039dd4f/sentencepiece-0.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:56dd39a3c4d6493db3cdca7e8cc68c6b633f0d4195495cbadfcf5af8a22d05a6", size = 1325651, upload-time = "2025-08-12T06:59:41.536Z" },
|
| 2080 |
+
{ url = "https://files.pythonhosted.org/packages/19/84/42eb3ce4796777a1b5d3699dfd4dca85113e68b637f194a6c8d786f16a04/sentencepiece-0.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d9381351182ff9888cc80e41c632e7e274b106f450de33d67a9e8f6043da6f76", size = 1253645, upload-time = "2025-08-12T06:59:42.903Z" },
|
| 2081 |
+
{ url = "https://files.pythonhosted.org/packages/89/fa/d3d5ebcba3cb9e6d3775a096251860c41a6bc53a1b9461151df83fe93255/sentencepiece-0.2.1-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:99f955df238021bf11f0fc37cdb54fd5e5b5f7fd30ecc3d93fb48b6815437167", size = 1316273, upload-time = "2025-08-12T06:59:44.476Z" },
|
| 2082 |
+
{ url = "https://files.pythonhosted.org/packages/04/88/14f2f4a2b922d8b39be45bf63d79e6cd3a9b2f248b2fcb98a69b12af12f5/sentencepiece-0.2.1-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0cdfecef430d985f1c2bcbfff3defd1d95dae876fbd0173376012d2d7d24044b", size = 1387881, upload-time = "2025-08-12T06:59:46.09Z" },
|
| 2083 |
+
{ url = "https://files.pythonhosted.org/packages/fd/b8/903e5ccb77b4ef140605d5d71b4f9e0ad95d456d6184688073ed11712809/sentencepiece-0.2.1-cp312-cp312-win32.whl", hash = "sha256:a483fd29a34c3e34c39ac5556b0a90942bec253d260235729e50976f5dba1068", size = 999540, upload-time = "2025-08-12T06:59:48.023Z" },
|
| 2084 |
+
{ url = "https://files.pythonhosted.org/packages/2d/81/92df5673c067148c2545b1bfe49adfd775bcc3a169a047f5a0e6575ddaca/sentencepiece-0.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:4cdc7c36234fda305e85c32949c5211faaf8dd886096c7cea289ddc12a2d02de", size = 1054671, upload-time = "2025-08-12T06:59:49.895Z" },
|
| 2085 |
+
{ url = "https://files.pythonhosted.org/packages/fe/02/c5e3bc518655d714622bec87d83db9cdba1cd0619a4a04e2109751c4f47f/sentencepiece-0.2.1-cp312-cp312-win_arm64.whl", hash = "sha256:daeb5e9e9fcad012324807856113708614d534f596d5008638eb9b40112cd9e4", size = 1033923, upload-time = "2025-08-12T06:59:51.952Z" },
|
| 2086 |
+
{ url = "https://files.pythonhosted.org/packages/ba/4a/85fbe1706d4d04a7e826b53f327c4b80f849cf1c7b7c5e31a20a97d8f28b/sentencepiece-0.2.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:dcd8161eee7b41aae57ded06272905dbd680a0a04b91edd0f64790c796b2f706", size = 1943150, upload-time = "2025-08-12T06:59:53.588Z" },
|
| 2087 |
+
{ url = "https://files.pythonhosted.org/packages/c2/83/4cfb393e287509fc2155480b9d184706ef8d9fa8cbf5505d02a5792bf220/sentencepiece-0.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:c6c8f42949f419ff8c7e9960dbadcfbc982d7b5efc2f6748210d3dd53a7de062", size = 1325651, upload-time = "2025-08-12T06:59:55.073Z" },
|
| 2088 |
+
{ url = "https://files.pythonhosted.org/packages/8d/de/5a007fb53b1ab0aafc69d11a5a3dd72a289d5a3e78dcf2c3a3d9b14ffe93/sentencepiece-0.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:097f3394e99456e9e4efba1737c3749d7e23563dd1588ce71a3d007f25475fff", size = 1253641, upload-time = "2025-08-12T06:59:56.562Z" },
|
| 2089 |
+
{ url = "https://files.pythonhosted.org/packages/2c/d2/f552be5928105588f4f4d66ee37dd4c61460d8097e62d0e2e0eec41bc61d/sentencepiece-0.2.1-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d7b670879c370d350557edabadbad1f6561a9e6968126e6debca4029e5547820", size = 1316271, upload-time = "2025-08-12T06:59:58.109Z" },
|
| 2090 |
+
{ url = "https://files.pythonhosted.org/packages/96/df/0cfe748ace5485be740fed9476dee7877f109da32ed0d280312c94ec259f/sentencepiece-0.2.1-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c7f0fd2f2693309e6628aeeb2e2faf6edd221134dfccac3308ca0de01f8dab47", size = 1387882, upload-time = "2025-08-12T07:00:00.701Z" },
|
| 2091 |
+
{ url = "https://files.pythonhosted.org/packages/ac/dd/f7774d42a881ced8e1739f393ab1e82ece39fc9abd4779e28050c2e975b5/sentencepiece-0.2.1-cp313-cp313-win32.whl", hash = "sha256:92b3816aa2339355fda2c8c4e021a5de92180b00aaccaf5e2808972e77a4b22f", size = 999541, upload-time = "2025-08-12T07:00:02.709Z" },
|
| 2092 |
+
{ url = "https://files.pythonhosted.org/packages/dd/e9/932b9eae6fd7019548321eee1ab8d5e3b3d1294df9d9a0c9ac517c7b636d/sentencepiece-0.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:10ed3dab2044c47f7a2e7b4969b0c430420cdd45735d78c8f853191fa0e3148b", size = 1054669, upload-time = "2025-08-12T07:00:04.915Z" },
|
| 2093 |
+
{ url = "https://files.pythonhosted.org/packages/c9/3a/76488a00ea7d6931689cda28726a1447d66bf1a4837943489314593d5596/sentencepiece-0.2.1-cp313-cp313-win_arm64.whl", hash = "sha256:ac650534e2251083c5f75dde4ff28896ce7c8904133dc8fef42780f4d5588fcd", size = 1033922, upload-time = "2025-08-12T07:00:06.496Z" },
|
| 2094 |
+
{ url = "https://files.pythonhosted.org/packages/4a/b6/08fe2ce819e02ccb0296f4843e3f195764ce9829cbda61b7513f29b95718/sentencepiece-0.2.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:8dd4b477a7b069648d19363aad0cab9bad2f4e83b2d179be668efa672500dc94", size = 1946052, upload-time = "2025-08-12T07:00:08.136Z" },
|
| 2095 |
+
{ url = "https://files.pythonhosted.org/packages/ab/d9/1ea0e740591ff4c6fc2b6eb1d7510d02f3fb885093f19b2f3abd1363b402/sentencepiece-0.2.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0c0f672da370cc490e4c59d89e12289778310a0e71d176c541e4834759e1ae07", size = 1327408, upload-time = "2025-08-12T07:00:09.572Z" },
|
| 2096 |
+
{ url = "https://files.pythonhosted.org/packages/99/7e/1fb26e8a21613f6200e1ab88824d5d203714162cf2883248b517deb500b7/sentencepiece-0.2.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:ad8493bea8432dae8d6830365352350f3b4144415a1d09c4c8cb8d30cf3b6c3c", size = 1254857, upload-time = "2025-08-12T07:00:11.021Z" },
|
| 2097 |
+
{ url = "https://files.pythonhosted.org/packages/bc/85/c72fd1f3c7a6010544d6ae07f8ddb38b5e2a7e33bd4318f87266c0bbafbf/sentencepiece-0.2.1-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b81a24733726e3678d2db63619acc5a8dccd074f7aa7a54ecd5ca33ca6d2d596", size = 1315722, upload-time = "2025-08-12T07:00:12.989Z" },
|
| 2098 |
+
{ url = "https://files.pythonhosted.org/packages/4a/e8/661e5bd82a8aa641fd6c1020bd0e890ef73230a2b7215ddf9c8cd8e941c2/sentencepiece-0.2.1-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0a81799d0a68d618e89063fb423c3001a034c893069135ffe51fee439ae474d6", size = 1387452, upload-time = "2025-08-12T07:00:15.088Z" },
|
| 2099 |
+
{ url = "https://files.pythonhosted.org/packages/99/5e/ae66c361023a470afcbc1fbb8da722c72ea678a2fcd9a18f1a12598c7501/sentencepiece-0.2.1-cp313-cp313t-win32.whl", hash = "sha256:89a3ea015517c42c0341d0d962f3e6aaf2cf10d71b1932d475c44ba48d00aa2b", size = 1002501, upload-time = "2025-08-12T07:00:16.966Z" },
|
| 2100 |
+
{ url = "https://files.pythonhosted.org/packages/c1/03/d332828c4ff764e16c1b56c2c8f9a33488bbe796b53fb6b9c4205ddbf167/sentencepiece-0.2.1-cp313-cp313t-win_amd64.whl", hash = "sha256:33f068c9382dc2e7c228eedfd8163b52baa86bb92f50d0488bf2b7da7032e484", size = 1057555, upload-time = "2025-08-12T07:00:18.573Z" },
|
| 2101 |
+
{ url = "https://files.pythonhosted.org/packages/88/14/5aee0bf0864df9bd82bd59e7711362908e4935e3f9cdc1f57246b5d5c9b9/sentencepiece-0.2.1-cp313-cp313t-win_arm64.whl", hash = "sha256:b3616ad246f360e52c85781e47682d31abfb6554c779e42b65333d4b5f44ecc0", size = 1036042, upload-time = "2025-08-12T07:00:20.209Z" },
|
| 2102 |
+
{ url = "https://files.pythonhosted.org/packages/24/9c/89eb8b2052f720a612478baf11c8227dcf1dc28cd4ea4c0c19506b5af2a2/sentencepiece-0.2.1-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:5d0350b686c320068702116276cfb26c066dc7e65cfef173980b11bb4d606719", size = 1943147, upload-time = "2025-08-12T07:00:21.809Z" },
|
| 2103 |
+
{ url = "https://files.pythonhosted.org/packages/82/0b/a1432bc87f97c2ace36386ca23e8bd3b91fb40581b5e6148d24b24186419/sentencepiece-0.2.1-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:c7f54a31cde6fa5cb030370566f68152a742f433f8d2be458463d06c208aef33", size = 1325624, upload-time = "2025-08-12T07:00:23.289Z" },
|
| 2104 |
+
{ url = "https://files.pythonhosted.org/packages/ea/99/bbe054ebb5a5039457c590e0a4156ed073fb0fe9ce4f7523404dd5b37463/sentencepiece-0.2.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c83b85ab2d6576607f31df77ff86f28182be4a8de6d175d2c33ca609925f5da1", size = 1253670, upload-time = "2025-08-12T07:00:24.69Z" },
|
| 2105 |
+
{ url = "https://files.pythonhosted.org/packages/19/ad/d5c7075f701bd97971d7c2ac2904f227566f51ef0838dfbdfdccb58cd212/sentencepiece-0.2.1-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1855f57db07b51fb51ed6c9c452f570624d2b169b36f0f79ef71a6e6c618cd8b", size = 1316247, upload-time = "2025-08-12T07:00:26.435Z" },
|
| 2106 |
+
{ url = "https://files.pythonhosted.org/packages/fb/03/35fbe5f3d9a7435eebd0b473e09584bd3cc354ce118b960445b060d33781/sentencepiece-0.2.1-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:01e6912125cb45d3792f530a4d38f8e21bf884d6b4d4ade1b2de5cf7a8d2a52b", size = 1387894, upload-time = "2025-08-12T07:00:28.339Z" },
|
| 2107 |
+
{ url = "https://files.pythonhosted.org/packages/dc/aa/956ef729aafb6c8f9c443104c9636489093bb5c61d6b90fc27aa1a865574/sentencepiece-0.2.1-cp314-cp314-win32.whl", hash = "sha256:c415c9de1447e0a74ae3fdb2e52f967cb544113a3a5ce3a194df185cbc1f962f", size = 1096698, upload-time = "2025-08-12T07:00:29.764Z" },
|
| 2108 |
+
{ url = "https://files.pythonhosted.org/packages/b8/cb/fe400d8836952cc535c81a0ce47dc6875160e5fedb71d2d9ff0e9894c2a6/sentencepiece-0.2.1-cp314-cp314-win_amd64.whl", hash = "sha256:881b2e44b14fc19feade3cbed314be37de639fc415375cefaa5bc81a4be137fd", size = 1155115, upload-time = "2025-08-12T07:00:32.865Z" },
|
| 2109 |
+
{ url = "https://files.pythonhosted.org/packages/32/89/047921cf70f36c7b6b6390876b2399b3633ab73b8d0cb857e5a964238941/sentencepiece-0.2.1-cp314-cp314-win_arm64.whl", hash = "sha256:2005242a16d2dc3ac5fe18aa7667549134d37854823df4c4db244752453b78a8", size = 1133890, upload-time = "2025-08-12T07:00:34.763Z" },
|
| 2110 |
+
{ url = "https://files.pythonhosted.org/packages/a1/11/5b414b9fae6255b5fb1e22e2ed3dc3a72d3a694e5703910e640ac78346bb/sentencepiece-0.2.1-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:a19adcec27c524cb7069a1c741060add95f942d1cbf7ad0d104dffa0a7d28a2b", size = 1946081, upload-time = "2025-08-12T07:00:36.97Z" },
|
| 2111 |
+
{ url = "https://files.pythonhosted.org/packages/77/eb/7a5682bb25824db8545f8e5662e7f3e32d72a508fdce086029d89695106b/sentencepiece-0.2.1-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:e37e4b4c4a11662b5db521def4e44d4d30ae69a1743241412a93ae40fdcab4bb", size = 1327406, upload-time = "2025-08-12T07:00:38.669Z" },
|
| 2112 |
+
{ url = "https://files.pythonhosted.org/packages/03/b0/811dae8fb9f2784e138785d481469788f2e0d0c109c5737372454415f55f/sentencepiece-0.2.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:477c81505db072b3ab627e7eab972ea1025331bd3a92bacbf798df2b75ea86ec", size = 1254846, upload-time = "2025-08-12T07:00:40.611Z" },
|
| 2113 |
+
{ url = "https://files.pythonhosted.org/packages/ef/23/195b2e7ec85ebb6a547969f60b723c7aca5a75800ece6cc3f41da872d14e/sentencepiece-0.2.1-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:010f025a544ef770bb395091d57cb94deb9652d8972e0d09f71d85d5a0816c8c", size = 1315721, upload-time = "2025-08-12T07:00:42.914Z" },
|
| 2114 |
+
{ url = "https://files.pythonhosted.org/packages/7e/aa/553dbe4178b5f23eb28e59393dddd64186178b56b81d9b8d5c3ff1c28395/sentencepiece-0.2.1-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:733e59ff1794d26db706cd41fc2d7ca5f6c64a820709cb801dc0ea31780d64ab", size = 1387458, upload-time = "2025-08-12T07:00:44.56Z" },
|
| 2115 |
+
{ url = "https://files.pythonhosted.org/packages/66/7c/08ff0012507297a4dd74a5420fdc0eb9e3e80f4e88cab1538d7f28db303d/sentencepiece-0.2.1-cp314-cp314t-win32.whl", hash = "sha256:d3233770f78e637dc8b1fda2cd7c3b99ec77e7505041934188a4e7fe751de3b0", size = 1099765, upload-time = "2025-08-12T07:00:46.058Z" },
|
| 2116 |
+
{ url = "https://files.pythonhosted.org/packages/91/d5/2a69e1ce15881beb9ddfc7e3f998322f5cedcd5e4d244cb74dade9441663/sentencepiece-0.2.1-cp314-cp314t-win_amd64.whl", hash = "sha256:5e4366c97b68218fd30ea72d70c525e6e78a6c0a88650f57ac4c43c63b234a9d", size = 1157807, upload-time = "2025-08-12T07:00:47.673Z" },
|
| 2117 |
+
{ url = "https://files.pythonhosted.org/packages/f3/16/54f611fcfc2d1c46cbe3ec4169780b2cfa7cf63708ef2b71611136db7513/sentencepiece-0.2.1-cp314-cp314t-win_arm64.whl", hash = "sha256:105e36e75cbac1292642045458e8da677b2342dcd33df503e640f0b457cb6751", size = 1136264, upload-time = "2025-08-12T07:00:49.485Z" },
|
| 2118 |
+
]
|
| 2119 |
+
|
| 2120 |
[[package]]
|
| 2121 |
name = "six"
|
| 2122 |
version = "1.17.0"
|
|
|
|
| 2199 |
{ url = "https://files.pythonhosted.org/packages/32/d5/f9a850d79b0851d1d4ef6456097579a9005b31fea68726a4ae5f2d82ddd9/threadpoolctl-3.6.0-py3-none-any.whl", hash = "sha256:43a0b8fd5a2928500110039e43a5eed8480b918967083ea48dc3ab9f13c4a7fb", size = 18638, upload-time = "2025-03-13T13:49:21.846Z" },
|
| 2200 |
]
|
| 2201 |
|
| 2202 |
+
[[package]]
|
| 2203 |
+
name = "tokenizers"
|
| 2204 |
+
version = "0.22.1"
|
| 2205 |
+
source = { registry = "https://pypi.org/simple" }
|
| 2206 |
+
dependencies = [
|
| 2207 |
+
{ name = "huggingface-hub" },
|
| 2208 |
+
]
|
| 2209 |
+
sdist = { url = "https://files.pythonhosted.org/packages/1c/46/fb6854cec3278fbfa4a75b50232c77622bc517ac886156e6afbfa4d8fc6e/tokenizers-0.22.1.tar.gz", hash = "sha256:61de6522785310a309b3407bac22d99c4db5dba349935e99e4d15ea2226af2d9", size = 363123, upload-time = "2025-09-19T09:49:23.424Z" }
|
| 2210 |
+
wheels = [
|
| 2211 |
+
{ url = "https://files.pythonhosted.org/packages/bf/33/f4b2d94ada7ab297328fc671fed209368ddb82f965ec2224eb1892674c3a/tokenizers-0.22.1-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:59fdb013df17455e5f950b4b834a7b3ee2e0271e6378ccb33aa74d178b513c73", size = 3069318, upload-time = "2025-09-19T09:49:11.848Z" },
|
| 2212 |
+
{ url = "https://files.pythonhosted.org/packages/1c/58/2aa8c874d02b974990e89ff95826a4852a8b2a273c7d1b4411cdd45a4565/tokenizers-0.22.1-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:8d4e484f7b0827021ac5f9f71d4794aaef62b979ab7608593da22b1d2e3c4edc", size = 2926478, upload-time = "2025-09-19T09:49:09.759Z" },
|
| 2213 |
+
{ url = "https://files.pythonhosted.org/packages/1e/3b/55e64befa1e7bfea963cf4b787b2cea1011362c4193f5477047532ce127e/tokenizers-0.22.1-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:19d2962dd28bc67c1f205ab180578a78eef89ac60ca7ef7cbe9635a46a56422a", size = 3256994, upload-time = "2025-09-19T09:48:56.701Z" },
|
| 2214 |
+
{ url = "https://files.pythonhosted.org/packages/71/0b/fbfecf42f67d9b7b80fde4aabb2b3110a97fac6585c9470b5bff103a80cb/tokenizers-0.22.1-cp39-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:38201f15cdb1f8a6843e6563e6e79f4abd053394992b9bbdf5213ea3469b4ae7", size = 3153141, upload-time = "2025-09-19T09:48:59.749Z" },
|
| 2215 |
+
{ url = "https://files.pythonhosted.org/packages/17/a9/b38f4e74e0817af8f8ef925507c63c6ae8171e3c4cb2d5d4624bf58fca69/tokenizers-0.22.1-cp39-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d1cbe5454c9a15df1b3443c726063d930c16f047a3cc724b9e6e1a91140e5a21", size = 3508049, upload-time = "2025-09-19T09:49:05.868Z" },
|
| 2216 |
+
{ url = "https://files.pythonhosted.org/packages/d2/48/dd2b3dac46bb9134a88e35d72e1aa4869579eacc1a27238f1577270773ff/tokenizers-0.22.1-cp39-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e7d094ae6312d69cc2a872b54b91b309f4f6fbce871ef28eb27b52a98e4d0214", size = 3710730, upload-time = "2025-09-19T09:49:01.832Z" },
|
| 2217 |
+
{ url = "https://files.pythonhosted.org/packages/93/0e/ccabc8d16ae4ba84a55d41345207c1e2ea88784651a5a487547d80851398/tokenizers-0.22.1-cp39-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:afd7594a56656ace95cdd6df4cca2e4059d294c5cfb1679c57824b605556cb2f", size = 3412560, upload-time = "2025-09-19T09:49:03.867Z" },
|
| 2218 |
+
{ url = "https://files.pythonhosted.org/packages/d0/c6/dc3a0db5a6766416c32c034286d7c2d406da1f498e4de04ab1b8959edd00/tokenizers-0.22.1-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e2ef6063d7a84994129732b47e7915e8710f27f99f3a3260b8a38fc7ccd083f4", size = 3250221, upload-time = "2025-09-19T09:49:07.664Z" },
|
| 2219 |
+
{ url = "https://files.pythonhosted.org/packages/d7/a6/2c8486eef79671601ff57b093889a345dd3d576713ef047776015dc66de7/tokenizers-0.22.1-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:ba0a64f450b9ef412c98f6bcd2a50c6df6e2443b560024a09fa6a03189726879", size = 9345569, upload-time = "2025-09-19T09:49:14.214Z" },
|
| 2220 |
+
{ url = "https://files.pythonhosted.org/packages/6b/16/32ce667f14c35537f5f605fe9bea3e415ea1b0a646389d2295ec348d5657/tokenizers-0.22.1-cp39-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:331d6d149fa9c7d632cde4490fb8bbb12337fa3a0232e77892be656464f4b446", size = 9271599, upload-time = "2025-09-19T09:49:16.639Z" },
|
| 2221 |
+
{ url = "https://files.pythonhosted.org/packages/51/7c/a5f7898a3f6baa3fc2685c705e04c98c1094c523051c805cdd9306b8f87e/tokenizers-0.22.1-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:607989f2ea68a46cb1dfbaf3e3aabdf3f21d8748312dbeb6263d1b3b66c5010a", size = 9533862, upload-time = "2025-09-19T09:49:19.146Z" },
|
| 2222 |
+
{ url = "https://files.pythonhosted.org/packages/36/65/7e75caea90bc73c1dd8d40438adf1a7bc26af3b8d0a6705ea190462506e1/tokenizers-0.22.1-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a0f307d490295717726598ef6fa4f24af9d484809223bbc253b201c740a06390", size = 9681250, upload-time = "2025-09-19T09:49:21.501Z" },
|
| 2223 |
+
{ url = "https://files.pythonhosted.org/packages/30/2c/959dddef581b46e6209da82df3b78471e96260e2bc463f89d23b1bf0e52a/tokenizers-0.22.1-cp39-abi3-win32.whl", hash = "sha256:b5120eed1442765cd90b903bb6cfef781fd8fe64e34ccaecbae4c619b7b12a82", size = 2472003, upload-time = "2025-09-19T09:49:27.089Z" },
|
| 2224 |
+
{ url = "https://files.pythonhosted.org/packages/b3/46/e33a8c93907b631a99377ef4c5f817ab453d0b34f93529421f42ff559671/tokenizers-0.22.1-cp39-abi3-win_amd64.whl", hash = "sha256:65fd6e3fb11ca1e78a6a93602490f134d1fdeb13bcef99389d5102ea318ed138", size = 2674684, upload-time = "2025-09-19T09:49:24.953Z" },
|
| 2225 |
+
]
|
| 2226 |
+
|
| 2227 |
[[package]]
|
| 2228 |
name = "toml"
|
| 2229 |
version = "0.10.2"
|
|
|
|
| 2301 |
{ url = "https://files.pythonhosted.org/packages/5e/4f/e1f65e8f8c76d73658b33d33b81eed4322fb5085350e4328d5c956f0c8f9/tornado-6.5.2-cp39-abi3-win_arm64.whl", hash = "sha256:d6c33dc3672e3a1f3618eb63b7ef4683a7688e7b9e6e8f0d9aa5726360a004af", size = 444456, upload-time = "2025-08-08T18:26:59.207Z" },
|
| 2302 |
]
|
| 2303 |
|
| 2304 |
+
[[package]]
|
| 2305 |
+
name = "tqdm"
|
| 2306 |
+
version = "4.67.1"
|
| 2307 |
+
source = { registry = "https://pypi.org/simple" }
|
| 2308 |
+
dependencies = [
|
| 2309 |
+
{ name = "colorama", marker = "sys_platform == 'win32'" },
|
| 2310 |
+
]
|
| 2311 |
+
sdist = { url = "https://files.pythonhosted.org/packages/a8/4b/29b4ef32e036bb34e4ab51796dd745cdba7ed47ad142a9f4a1eb8e0c744d/tqdm-4.67.1.tar.gz", hash = "sha256:f8aef9c52c08c13a65f30ea34f4e5aac3fd1a34959879d7e59e63027286627f2", size = 169737, upload-time = "2024-11-24T20:12:22.481Z" }
|
| 2312 |
+
wheels = [
|
| 2313 |
+
{ url = "https://files.pythonhosted.org/packages/d0/30/dc54f88dd4a2b5dc8a0279bdd7270e735851848b762aeb1c1184ed1f6b14/tqdm-4.67.1-py3-none-any.whl", hash = "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2", size = 78540, upload-time = "2024-11-24T20:12:19.698Z" },
|
| 2314 |
+
]
|
| 2315 |
+
|
| 2316 |
+
[[package]]
|
| 2317 |
+
name = "transformers"
|
| 2318 |
+
version = "4.57.3"
|
| 2319 |
+
source = { registry = "https://pypi.org/simple" }
|
| 2320 |
+
dependencies = [
|
| 2321 |
+
{ name = "filelock" },
|
| 2322 |
+
{ name = "huggingface-hub" },
|
| 2323 |
+
{ name = "numpy" },
|
| 2324 |
+
{ name = "packaging" },
|
| 2325 |
+
{ name = "pyyaml" },
|
| 2326 |
+
{ name = "regex" },
|
| 2327 |
+
{ name = "requests" },
|
| 2328 |
+
{ name = "safetensors" },
|
| 2329 |
+
{ name = "tokenizers" },
|
| 2330 |
+
{ name = "tqdm" },
|
| 2331 |
+
]
|
| 2332 |
+
sdist = { url = "https://files.pythonhosted.org/packages/dd/70/d42a739e8dfde3d92bb2fff5819cbf331fe9657323221e79415cd5eb65ee/transformers-4.57.3.tar.gz", hash = "sha256:df4945029aaddd7c09eec5cad851f30662f8bd1746721b34cc031d70c65afebc", size = 10139680, upload-time = "2025-11-25T15:51:30.139Z" }
|
| 2333 |
+
wheels = [
|
| 2334 |
+
{ url = "https://files.pythonhosted.org/packages/6a/6b/2f416568b3c4c91c96e5a365d164f8a4a4a88030aa8ab4644181fdadce97/transformers-4.57.3-py3-none-any.whl", hash = "sha256:c77d353a4851b1880191603d36acb313411d3577f6e2897814f333841f7003f4", size = 11993463, upload-time = "2025-11-25T15:51:26.493Z" },
|
| 2335 |
+
]
|
| 2336 |
+
|
| 2337 |
[[package]]
|
| 2338 |
name = "typing-extensions"
|
| 2339 |
version = "4.15.0"
|