mcli-framework 7.12.1__py3-none-any.whl → 7.12.3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of mcli-framework might be problematic. Click here for more details.
- mcli/app/__init__.py +0 -2
- mcli/app/commands_cmd.py +19 -23
- mcli/app/completion_helpers.py +5 -5
- mcli/app/init_cmd.py +10 -10
- mcli/app/lock_cmd.py +82 -27
- mcli/app/main.py +2 -8
- mcli/app/model/model.py +5 -10
- mcli/app/store_cmd.py +8 -8
- mcli/app/video/__init__.py +0 -2
- mcli/app/video/video.py +1 -14
- mcli/chat/chat.py +90 -108
- mcli/chat/command_rag.py +0 -4
- mcli/chat/enhanced_chat.py +32 -41
- mcli/chat/system_controller.py +37 -37
- mcli/chat/system_integration.py +4 -5
- mcli/cli.py +2 -3
- mcli/lib/api/api.py +4 -9
- mcli/lib/api/daemon_client.py +19 -20
- mcli/lib/api/daemon_client_local.py +1 -3
- mcli/lib/api/daemon_decorator.py +6 -6
- mcli/lib/api/mcli_decorators.py +4 -8
- mcli/lib/auth/__init__.py +0 -1
- mcli/lib/auth/auth.py +4 -5
- mcli/lib/auth/mcli_manager.py +7 -12
- mcli/lib/auth/token_util.py +5 -5
- mcli/lib/config/__init__.py +29 -1
- mcli/lib/config/config.py +0 -1
- mcli/lib/custom_commands.py +1 -1
- mcli/lib/discovery/command_discovery.py +15 -15
- mcli/lib/erd/erd.py +7 -7
- mcli/lib/files/files.py +1 -1
- mcli/lib/fs/__init__.py +31 -1
- mcli/lib/fs/fs.py +12 -13
- mcli/lib/lib.py +0 -1
- mcli/lib/logger/logger.py +7 -10
- mcli/lib/performance/optimizer.py +25 -27
- mcli/lib/performance/rust_bridge.py +22 -27
- mcli/lib/performance/uvloop_config.py +0 -1
- mcli/lib/pickles/__init__.py +0 -1
- mcli/lib/pickles/pickles.py +0 -2
- mcli/lib/secrets/commands.py +0 -2
- mcli/lib/secrets/manager.py +0 -1
- mcli/lib/secrets/repl.py +2 -3
- mcli/lib/secrets/store.py +1 -2
- mcli/lib/services/data_pipeline.py +34 -34
- mcli/lib/services/lsh_client.py +38 -40
- mcli/lib/shell/shell.py +2 -2
- mcli/lib/toml/__init__.py +0 -1
- mcli/lib/ui/styling.py +0 -1
- mcli/lib/ui/visual_effects.py +33 -41
- mcli/lib/watcher/watcher.py +0 -1
- mcli/ml/__init__.py +1 -1
- mcli/ml/api/__init__.py +1 -1
- mcli/ml/api/app.py +8 -9
- mcli/ml/api/middleware.py +10 -10
- mcli/ml/api/routers/__init__.py +1 -1
- mcli/ml/api/routers/admin_router.py +3 -3
- mcli/ml/api/routers/auth_router.py +17 -18
- mcli/ml/api/routers/backtest_router.py +2 -2
- mcli/ml/api/routers/data_router.py +2 -2
- mcli/ml/api/routers/model_router.py +14 -15
- mcli/ml/api/routers/monitoring_router.py +2 -2
- mcli/ml/api/routers/portfolio_router.py +2 -2
- mcli/ml/api/routers/prediction_router.py +10 -9
- mcli/ml/api/routers/trade_router.py +2 -2
- mcli/ml/api/routers/websocket_router.py +6 -7
- mcli/ml/api/schemas.py +2 -2
- mcli/ml/auth/__init__.py +1 -1
- mcli/ml/auth/auth_manager.py +22 -23
- mcli/ml/auth/models.py +17 -17
- mcli/ml/auth/permissions.py +17 -17
- mcli/ml/backtesting/__init__.py +1 -1
- mcli/ml/backtesting/backtest_engine.py +31 -35
- mcli/ml/backtesting/performance_metrics.py +12 -14
- mcli/ml/backtesting/run.py +1 -2
- mcli/ml/cache.py +35 -36
- mcli/ml/cli/__init__.py +1 -1
- mcli/ml/cli/main.py +21 -24
- mcli/ml/config/__init__.py +1 -1
- mcli/ml/config/settings.py +28 -29
- mcli/ml/configs/__init__.py +1 -1
- mcli/ml/configs/dvc_config.py +14 -15
- mcli/ml/configs/mlflow_config.py +12 -13
- mcli/ml/configs/mlops_manager.py +19 -21
- mcli/ml/dashboard/__init__.py +4 -4
- mcli/ml/dashboard/app.py +20 -30
- mcli/ml/dashboard/app_supabase.py +16 -19
- mcli/ml/dashboard/app_training.py +11 -14
- mcli/ml/dashboard/cli.py +2 -2
- mcli/ml/dashboard/common.py +2 -3
- mcli/ml/dashboard/components/__init__.py +1 -1
- mcli/ml/dashboard/components/charts.py +13 -11
- mcli/ml/dashboard/components/metrics.py +7 -7
- mcli/ml/dashboard/components/tables.py +12 -9
- mcli/ml/dashboard/overview.py +2 -2
- mcli/ml/dashboard/pages/__init__.py +1 -1
- mcli/ml/dashboard/pages/cicd.py +15 -18
- mcli/ml/dashboard/pages/debug_dependencies.py +7 -7
- mcli/ml/dashboard/pages/monte_carlo_predictions.py +11 -18
- mcli/ml/dashboard/pages/predictions_enhanced.py +24 -32
- mcli/ml/dashboard/pages/scrapers_and_logs.py +22 -24
- mcli/ml/dashboard/pages/test_portfolio.py +3 -6
- mcli/ml/dashboard/pages/trading.py +16 -18
- mcli/ml/dashboard/pages/workflows.py +20 -30
- mcli/ml/dashboard/utils.py +9 -9
- mcli/ml/dashboard/warning_suppression.py +3 -3
- mcli/ml/data_ingestion/__init__.py +1 -1
- mcli/ml/data_ingestion/api_connectors.py +41 -46
- mcli/ml/data_ingestion/data_pipeline.py +36 -46
- mcli/ml/data_ingestion/stream_processor.py +43 -46
- mcli/ml/database/__init__.py +1 -1
- mcli/ml/database/migrations/env.py +2 -2
- mcli/ml/database/models.py +22 -24
- mcli/ml/database/session.py +14 -14
- mcli/ml/experimentation/__init__.py +1 -1
- mcli/ml/experimentation/ab_testing.py +45 -46
- mcli/ml/features/__init__.py +1 -1
- mcli/ml/features/ensemble_features.py +22 -27
- mcli/ml/features/recommendation_engine.py +30 -30
- mcli/ml/features/stock_features.py +29 -32
- mcli/ml/features/test_feature_engineering.py +10 -11
- mcli/ml/logging.py +4 -4
- mcli/ml/mlops/__init__.py +1 -1
- mcli/ml/mlops/data_versioning.py +29 -30
- mcli/ml/mlops/experiment_tracker.py +24 -24
- mcli/ml/mlops/model_serving.py +31 -34
- mcli/ml/mlops/pipeline_orchestrator.py +27 -35
- mcli/ml/models/__init__.py +5 -6
- mcli/ml/models/base_models.py +23 -23
- mcli/ml/models/ensemble_models.py +31 -31
- mcli/ml/models/recommendation_models.py +18 -19
- mcli/ml/models/test_models.py +14 -16
- mcli/ml/monitoring/__init__.py +1 -1
- mcli/ml/monitoring/drift_detection.py +32 -36
- mcli/ml/monitoring/metrics.py +2 -2
- mcli/ml/optimization/__init__.py +1 -1
- mcli/ml/optimization/optimize.py +1 -2
- mcli/ml/optimization/portfolio_optimizer.py +30 -32
- mcli/ml/predictions/__init__.py +1 -1
- mcli/ml/preprocessing/__init__.py +1 -1
- mcli/ml/preprocessing/data_cleaners.py +22 -23
- mcli/ml/preprocessing/feature_extractors.py +23 -26
- mcli/ml/preprocessing/ml_pipeline.py +23 -23
- mcli/ml/preprocessing/test_preprocessing.py +7 -8
- mcli/ml/scripts/populate_sample_data.py +0 -4
- mcli/ml/serving/serve.py +1 -2
- mcli/ml/tasks.py +17 -17
- mcli/ml/tests/test_integration.py +29 -30
- mcli/ml/tests/test_training_dashboard.py +21 -21
- mcli/ml/trading/__init__.py +1 -1
- mcli/ml/trading/migrations.py +5 -5
- mcli/ml/trading/models.py +21 -23
- mcli/ml/trading/paper_trading.py +16 -13
- mcli/ml/trading/risk_management.py +17 -18
- mcli/ml/trading/trading_service.py +25 -28
- mcli/ml/training/__init__.py +1 -1
- mcli/ml/training/train.py +0 -1
- mcli/public/oi/oi.py +1 -2
- mcli/self/completion_cmd.py +6 -10
- mcli/self/logs_cmd.py +19 -24
- mcli/self/migrate_cmd.py +22 -20
- mcli/self/redis_cmd.py +10 -11
- mcli/self/self_cmd.py +10 -18
- mcli/self/store_cmd.py +10 -12
- mcli/self/visual_cmd.py +9 -14
- mcli/self/zsh_cmd.py +2 -4
- mcli/workflow/daemon/async_command_database.py +23 -24
- mcli/workflow/daemon/async_process_manager.py +27 -29
- mcli/workflow/daemon/client.py +27 -33
- mcli/workflow/daemon/daemon.py +32 -36
- mcli/workflow/daemon/enhanced_daemon.py +24 -33
- mcli/workflow/daemon/process_cli.py +11 -12
- mcli/workflow/daemon/process_manager.py +23 -26
- mcli/workflow/daemon/test_daemon.py +4 -5
- mcli/workflow/dashboard/dashboard_cmd.py +0 -1
- mcli/workflow/doc_convert.py +15 -17
- mcli/workflow/gcloud/__init__.py +0 -1
- mcli/workflow/gcloud/gcloud.py +11 -8
- mcli/workflow/git_commit/ai_service.py +14 -15
- mcli/workflow/lsh_integration.py +9 -11
- mcli/workflow/model_service/client.py +26 -31
- mcli/workflow/model_service/download_and_run_efficient_models.py +10 -14
- mcli/workflow/model_service/lightweight_embedder.py +25 -35
- mcli/workflow/model_service/lightweight_model_server.py +26 -32
- mcli/workflow/model_service/lightweight_test.py +7 -10
- mcli/workflow/model_service/model_service.py +80 -91
- mcli/workflow/model_service/ollama_efficient_runner.py +14 -18
- mcli/workflow/model_service/openai_adapter.py +23 -23
- mcli/workflow/model_service/pdf_processor.py +21 -26
- mcli/workflow/model_service/test_efficient_runner.py +12 -16
- mcli/workflow/model_service/test_example.py +11 -13
- mcli/workflow/model_service/test_integration.py +3 -5
- mcli/workflow/model_service/test_new_features.py +7 -8
- mcli/workflow/notebook/converter.py +1 -1
- mcli/workflow/notebook/notebook_cmd.py +5 -6
- mcli/workflow/notebook/schema.py +0 -1
- mcli/workflow/notebook/validator.py +7 -3
- mcli/workflow/openai/openai.py +1 -2
- mcli/workflow/registry/registry.py +4 -1
- mcli/workflow/repo/repo.py +6 -7
- mcli/workflow/scheduler/cron_parser.py +16 -19
- mcli/workflow/scheduler/job.py +10 -10
- mcli/workflow/scheduler/monitor.py +15 -15
- mcli/workflow/scheduler/persistence.py +17 -18
- mcli/workflow/scheduler/scheduler.py +37 -38
- mcli/workflow/secrets/__init__.py +1 -1
- mcli/workflow/sync/test_cmd.py +0 -1
- mcli/workflow/wakatime/__init__.py +5 -9
- mcli/workflow/wakatime/wakatime.py +1 -2
- {mcli_framework-7.12.1.dist-info → mcli_framework-7.12.3.dist-info}/METADATA +1 -1
- mcli_framework-7.12.3.dist-info/RECORD +279 -0
- mcli_framework-7.12.1.dist-info/RECORD +0 -279
- {mcli_framework-7.12.1.dist-info → mcli_framework-7.12.3.dist-info}/WHEEL +0 -0
- {mcli_framework-7.12.1.dist-info → mcli_framework-7.12.3.dist-info}/entry_points.txt +0 -0
- {mcli_framework-7.12.1.dist-info → mcli_framework-7.12.3.dist-info}/licenses/LICENSE +0 -0
- {mcli_framework-7.12.1.dist-info → mcli_framework-7.12.3.dist-info}/top_level.txt +0 -0
mcli/ml/mlops/model_serving.py
CHANGED
|
@@ -1,21 +1,18 @@
|
|
|
1
|
-
"""REST API for model serving"""
|
|
1
|
+
"""REST API for model serving."""
|
|
2
2
|
|
|
3
|
-
import asyncio
|
|
4
3
|
import json
|
|
5
4
|
import logging
|
|
6
5
|
import os
|
|
7
6
|
import sys
|
|
8
7
|
from contextlib import asynccontextmanager
|
|
9
8
|
from datetime import datetime
|
|
10
|
-
from
|
|
11
|
-
from typing import Any, Dict, List, Optional, Union
|
|
9
|
+
from typing import Any, Dict, List, Optional
|
|
12
10
|
|
|
13
11
|
import numpy as np
|
|
14
12
|
import pandas as pd
|
|
15
13
|
import torch
|
|
16
14
|
import uvicorn
|
|
17
15
|
from fastapi import BackgroundTasks, FastAPI, File, HTTPException, UploadFile
|
|
18
|
-
from fastapi.responses import JSONResponse
|
|
19
16
|
from pydantic import BaseModel, Field
|
|
20
17
|
|
|
21
18
|
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "../.."))
|
|
@@ -30,7 +27,7 @@ logger = logging.getLogger(__name__)
|
|
|
30
27
|
|
|
31
28
|
# Pydantic models for API
|
|
32
29
|
class PredictionRequest(BaseModel):
|
|
33
|
-
"""Request model for predictions"""
|
|
30
|
+
"""Request model for predictions."""
|
|
34
31
|
|
|
35
32
|
trading_data: Dict[str, Any] = Field(..., description="Politician trading data")
|
|
36
33
|
stock_data: Optional[Dict[str, Any]] = Field(None, description="Stock price data")
|
|
@@ -39,7 +36,7 @@ class PredictionRequest(BaseModel):
|
|
|
39
36
|
|
|
40
37
|
|
|
41
38
|
class PredictionResponse(BaseModel):
|
|
42
|
-
"""Response model for predictions"""
|
|
39
|
+
"""Response model for predictions."""
|
|
43
40
|
|
|
44
41
|
recommendations: List[Dict[str, Any]]
|
|
45
42
|
timestamp: str
|
|
@@ -48,7 +45,7 @@ class PredictionResponse(BaseModel):
|
|
|
48
45
|
|
|
49
46
|
|
|
50
47
|
class HealthResponse(BaseModel):
|
|
51
|
-
"""Health check response"""
|
|
48
|
+
"""Health check response."""
|
|
52
49
|
|
|
53
50
|
status: str
|
|
54
51
|
model_loaded: bool
|
|
@@ -57,7 +54,7 @@ class HealthResponse(BaseModel):
|
|
|
57
54
|
|
|
58
55
|
|
|
59
56
|
class ModelMetricsResponse(BaseModel):
|
|
60
|
-
"""Model metrics response"""
|
|
57
|
+
"""Model metrics response."""
|
|
61
58
|
|
|
62
59
|
accuracy: float
|
|
63
60
|
precision: float
|
|
@@ -67,14 +64,14 @@ class ModelMetricsResponse(BaseModel):
|
|
|
67
64
|
|
|
68
65
|
|
|
69
66
|
class BatchPredictionRequest(BaseModel):
|
|
70
|
-
"""Batch prediction request"""
|
|
67
|
+
"""Batch prediction request."""
|
|
71
68
|
|
|
72
69
|
batch_id: str
|
|
73
70
|
data: List[PredictionRequest]
|
|
74
71
|
|
|
75
72
|
|
|
76
73
|
class BatchPredictionResponse(BaseModel):
|
|
77
|
-
"""Batch prediction response"""
|
|
74
|
+
"""Batch prediction response."""
|
|
78
75
|
|
|
79
76
|
batch_id: str
|
|
80
77
|
status: str
|
|
@@ -83,7 +80,7 @@ class BatchPredictionResponse(BaseModel):
|
|
|
83
80
|
|
|
84
81
|
|
|
85
82
|
class ModelEndpoint:
|
|
86
|
-
"""Model endpoint manager"""
|
|
83
|
+
"""Model endpoint manager."""
|
|
87
84
|
|
|
88
85
|
def __init__(self, model_path: Optional[str] = None):
|
|
89
86
|
self.model = None
|
|
@@ -100,7 +97,7 @@ class ModelEndpoint:
|
|
|
100
97
|
self._setup_feature_extractors()
|
|
101
98
|
|
|
102
99
|
def _setup_feature_extractors(self):
|
|
103
|
-
"""Initialize feature extractors"""
|
|
100
|
+
"""Initialize feature extractors."""
|
|
104
101
|
self.feature_extractors = {
|
|
105
102
|
"stock": StockRecommendationFeatures(),
|
|
106
103
|
"political": PoliticalInfluenceFeatures(),
|
|
@@ -109,7 +106,7 @@ class ModelEndpoint:
|
|
|
109
106
|
logger.info("Feature extractors initialized")
|
|
110
107
|
|
|
111
108
|
def load_model(self, model_path: str):
|
|
112
|
-
"""Load model from file"""
|
|
109
|
+
"""Load model from file."""
|
|
113
110
|
try:
|
|
114
111
|
checkpoint = torch.load(model_path, map_location="cpu")
|
|
115
112
|
|
|
@@ -151,7 +148,7 @@ class ModelEndpoint:
|
|
|
151
148
|
def extract_features(
|
|
152
149
|
self, trading_data: pd.DataFrame, stock_data: Optional[pd.DataFrame] = None
|
|
153
150
|
) -> np.ndarray:
|
|
154
|
-
"""Extract features from raw data"""
|
|
151
|
+
"""Extract features from raw data."""
|
|
155
152
|
features = pd.DataFrame()
|
|
156
153
|
|
|
157
154
|
# Extract political features
|
|
@@ -176,7 +173,7 @@ class ModelEndpoint:
|
|
|
176
173
|
return features.values if not features.empty else np.array([[]])
|
|
177
174
|
|
|
178
175
|
async def predict(self, request: PredictionRequest) -> PredictionResponse:
|
|
179
|
-
"""Generate predictions"""
|
|
176
|
+
"""Generate predictions."""
|
|
180
177
|
start_time = datetime.now()
|
|
181
178
|
|
|
182
179
|
try:
|
|
@@ -259,7 +256,7 @@ class ModelEndpoint:
|
|
|
259
256
|
raise HTTPException(status_code=500, detail=str(e))
|
|
260
257
|
|
|
261
258
|
def _generate_mock_recommendations(self, tickers: List[str]) -> List[PortfolioRecommendation]:
|
|
262
|
-
"""Generate mock recommendations for testing"""
|
|
259
|
+
"""Generate mock recommendations for testing."""
|
|
263
260
|
recommendations = []
|
|
264
261
|
for ticker in tickers:
|
|
265
262
|
rec = PortfolioRecommendation(
|
|
@@ -276,7 +273,7 @@ class ModelEndpoint:
|
|
|
276
273
|
return recommendations
|
|
277
274
|
|
|
278
275
|
def get_health(self) -> HealthResponse:
|
|
279
|
-
"""Get endpoint health status"""
|
|
276
|
+
"""Get endpoint health status."""
|
|
280
277
|
uptime = (datetime.now() - self.start_time).total_seconds()
|
|
281
278
|
|
|
282
279
|
return HealthResponse(
|
|
@@ -287,7 +284,7 @@ class ModelEndpoint:
|
|
|
287
284
|
)
|
|
288
285
|
|
|
289
286
|
def get_metrics(self) -> Dict[str, Any]:
|
|
290
|
-
"""Get endpoint metrics"""
|
|
287
|
+
"""Get endpoint metrics."""
|
|
291
288
|
return {
|
|
292
289
|
**self.metrics,
|
|
293
290
|
"model_version": self.model_version,
|
|
@@ -297,7 +294,7 @@ class ModelEndpoint:
|
|
|
297
294
|
|
|
298
295
|
|
|
299
296
|
class PredictionService:
|
|
300
|
-
"""Async prediction service for batch processing"""
|
|
297
|
+
"""Async prediction service for batch processing."""
|
|
301
298
|
|
|
302
299
|
def __init__(self, model_endpoint: ModelEndpoint):
|
|
303
300
|
self.model_endpoint = model_endpoint
|
|
@@ -305,7 +302,7 @@ class PredictionService:
|
|
|
305
302
|
self.executor = None
|
|
306
303
|
|
|
307
304
|
async def process_batch(self, batch_request: BatchPredictionRequest) -> BatchPredictionResponse:
|
|
308
|
-
"""Process batch predictions asynchronously"""
|
|
305
|
+
"""Process batch predictions asynchronously."""
|
|
309
306
|
batch_id = batch_request.batch_id
|
|
310
307
|
|
|
311
308
|
# Initialize batch job
|
|
@@ -341,7 +338,7 @@ class PredictionService:
|
|
|
341
338
|
)
|
|
342
339
|
|
|
343
340
|
def get_batch_status(self, batch_id: str) -> BatchPredictionResponse:
|
|
344
|
-
"""Get batch job status"""
|
|
341
|
+
"""Get batch job status."""
|
|
345
342
|
if batch_id not in self.batch_jobs:
|
|
346
343
|
raise HTTPException(status_code=404, detail="Batch job not found")
|
|
347
344
|
|
|
@@ -356,7 +353,7 @@ class PredictionService:
|
|
|
356
353
|
|
|
357
354
|
|
|
358
355
|
class ModelServer:
|
|
359
|
-
"""FastAPI model server"""
|
|
356
|
+
"""FastAPI model server."""
|
|
360
357
|
|
|
361
358
|
def __init__(self, model_path: Optional[str] = None):
|
|
362
359
|
self.model_endpoint = ModelEndpoint(model_path)
|
|
@@ -365,7 +362,7 @@ class ModelServer:
|
|
|
365
362
|
|
|
366
363
|
@asynccontextmanager
|
|
367
364
|
async def lifespan(self, app: FastAPI):
|
|
368
|
-
"""Manage application lifecycle"""
|
|
365
|
+
"""Manage application lifecycle."""
|
|
369
366
|
# Startup
|
|
370
367
|
logger.info("Starting model server...")
|
|
371
368
|
yield
|
|
@@ -373,7 +370,7 @@ class ModelServer:
|
|
|
373
370
|
logger.info("Shutting down model server...")
|
|
374
371
|
|
|
375
372
|
def _create_app(self) -> FastAPI:
|
|
376
|
-
"""Create FastAPI application"""
|
|
373
|
+
"""Create FastAPI application."""
|
|
377
374
|
app = FastAPI(
|
|
378
375
|
title="Stock Recommendation API",
|
|
379
376
|
description="ML-powered stock recommendation system based on politician trading data",
|
|
@@ -384,19 +381,19 @@ class ModelServer:
|
|
|
384
381
|
# Health check
|
|
385
382
|
@app.get("/health", response_model=HealthResponse)
|
|
386
383
|
async def health():
|
|
387
|
-
"""Health check endpoint"""
|
|
384
|
+
"""Health check endpoint."""
|
|
388
385
|
return self.model_endpoint.get_health()
|
|
389
386
|
|
|
390
387
|
# Metrics
|
|
391
388
|
@app.get("/metrics")
|
|
392
389
|
async def metrics():
|
|
393
|
-
"""Get service metrics"""
|
|
390
|
+
"""Get service metrics."""
|
|
394
391
|
return self.model_endpoint.get_metrics()
|
|
395
392
|
|
|
396
393
|
# Single prediction
|
|
397
394
|
@app.post("/predict", response_model=PredictionResponse)
|
|
398
395
|
async def predict(request: PredictionRequest):
|
|
399
|
-
"""Generate stock recommendations"""
|
|
396
|
+
"""Generate stock recommendations."""
|
|
400
397
|
return await self.model_endpoint.predict(request)
|
|
401
398
|
|
|
402
399
|
# Batch prediction
|
|
@@ -404,7 +401,7 @@ class ModelServer:
|
|
|
404
401
|
async def batch_predict(
|
|
405
402
|
batch_request: BatchPredictionRequest, background_tasks: BackgroundTasks
|
|
406
403
|
):
|
|
407
|
-
"""Submit batch prediction job"""
|
|
404
|
+
"""Submit batch prediction job."""
|
|
408
405
|
background_tasks.add_task(self.prediction_service.process_batch, batch_request)
|
|
409
406
|
|
|
410
407
|
return BatchPredictionResponse(
|
|
@@ -414,13 +411,13 @@ class ModelServer:
|
|
|
414
411
|
# Batch status
|
|
415
412
|
@app.get("/batch/{batch_id}", response_model=BatchPredictionResponse)
|
|
416
413
|
async def batch_status(batch_id: str):
|
|
417
|
-
"""Get batch job status"""
|
|
414
|
+
"""Get batch job status."""
|
|
418
415
|
return self.prediction_service.get_batch_status(batch_id)
|
|
419
416
|
|
|
420
417
|
# Model reload
|
|
421
418
|
@app.post("/model/reload")
|
|
422
419
|
async def reload_model(model_path: str):
|
|
423
|
-
"""Reload model from new path"""
|
|
420
|
+
"""Reload model from new path."""
|
|
424
421
|
try:
|
|
425
422
|
self.model_endpoint.load_model(model_path)
|
|
426
423
|
return {"status": "success", "model_version": self.model_endpoint.model_version}
|
|
@@ -430,7 +427,7 @@ class ModelServer:
|
|
|
430
427
|
# Upload and predict
|
|
431
428
|
@app.post("/upload/predict")
|
|
432
429
|
async def upload_predict(file: UploadFile = File(...)):
|
|
433
|
-
"""Upload CSV and get predictions"""
|
|
430
|
+
"""Upload CSV and get predictions."""
|
|
434
431
|
try:
|
|
435
432
|
# Read uploaded file
|
|
436
433
|
content = await file.read()
|
|
@@ -455,12 +452,12 @@ class ModelServer:
|
|
|
455
452
|
return app
|
|
456
453
|
|
|
457
454
|
def run(self, host: str = "0.0.0.0", port: int = 8000):
|
|
458
|
-
"""Run the server"""
|
|
455
|
+
"""Run the server."""
|
|
459
456
|
uvicorn.run(self.app, host=host, port=port)
|
|
460
457
|
|
|
461
458
|
|
|
462
459
|
def main():
|
|
463
|
-
"""Run the model server"""
|
|
460
|
+
"""Run the model server."""
|
|
464
461
|
import argparse
|
|
465
462
|
|
|
466
463
|
parser = argparse.ArgumentParser(description="Stock Recommendation Model Server")
|
|
@@ -1,33 +1,25 @@
|
|
|
1
|
-
"""End-to-end ML pipeline orchestrator"""
|
|
1
|
+
"""End-to-end ML pipeline orchestrator."""
|
|
2
2
|
|
|
3
3
|
import os
|
|
4
4
|
import sys
|
|
5
5
|
|
|
6
6
|
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "../.."))
|
|
7
7
|
|
|
8
|
-
import json
|
|
9
8
|
import logging
|
|
10
9
|
import pickle
|
|
11
10
|
from dataclasses import dataclass, field
|
|
12
11
|
from datetime import datetime
|
|
13
12
|
from enum import Enum
|
|
14
13
|
from pathlib import Path
|
|
15
|
-
from typing import Any, Callable, Dict, List, Optional
|
|
14
|
+
from typing import Any, Callable, Dict, List, Optional
|
|
16
15
|
|
|
17
16
|
import numpy as np
|
|
18
17
|
import pandas as pd
|
|
19
18
|
import torch
|
|
20
19
|
from ml.features.ensemble_features import EnsembleFeatureBuilder
|
|
21
20
|
from ml.features.political_features import PoliticalInfluenceFeatures
|
|
22
|
-
from ml.features.recommendation_engine import RecommendationConfig as FeatureRecommendationConfig
|
|
23
|
-
from ml.features.recommendation_engine import StockRecommendationEngine
|
|
24
21
|
from ml.features.stock_features import StockRecommendationFeatures
|
|
25
|
-
from ml.models.ensemble_models import
|
|
26
|
-
DeepEnsembleModel,
|
|
27
|
-
EnsembleConfig,
|
|
28
|
-
EnsembleTrainer,
|
|
29
|
-
ModelConfig,
|
|
30
|
-
)
|
|
22
|
+
from ml.models.ensemble_models import EnsembleConfig, ModelConfig
|
|
31
23
|
from ml.models.recommendation_models import (
|
|
32
24
|
RecommendationConfig,
|
|
33
25
|
RecommendationTrainer,
|
|
@@ -41,7 +33,7 @@ logger = logging.getLogger(__name__)
|
|
|
41
33
|
|
|
42
34
|
|
|
43
35
|
class PipelineStage(Enum):
|
|
44
|
-
"""Pipeline execution stages"""
|
|
36
|
+
"""Pipeline execution stages."""
|
|
45
37
|
|
|
46
38
|
DATA_INGESTION = "data_ingestion"
|
|
47
39
|
DATA_PREPROCESSING = "data_preprocessing"
|
|
@@ -53,7 +45,7 @@ class PipelineStage(Enum):
|
|
|
53
45
|
|
|
54
46
|
@dataclass
|
|
55
47
|
class PipelineStep:
|
|
56
|
-
"""Individual pipeline step configuration"""
|
|
48
|
+
"""Individual pipeline step configuration."""
|
|
57
49
|
|
|
58
50
|
name: str
|
|
59
51
|
stage: PipelineStage
|
|
@@ -68,7 +60,7 @@ class PipelineStep:
|
|
|
68
60
|
|
|
69
61
|
@dataclass
|
|
70
62
|
class PipelineConfig:
|
|
71
|
-
"""Complete pipeline configuration"""
|
|
63
|
+
"""Complete pipeline configuration."""
|
|
72
64
|
|
|
73
65
|
name: str = "politician-trading-ml-pipeline"
|
|
74
66
|
version: str = "1.0.0"
|
|
@@ -93,7 +85,7 @@ class PipelineConfig:
|
|
|
93
85
|
|
|
94
86
|
|
|
95
87
|
class MLPipeline:
|
|
96
|
-
"""End-to-end ML pipeline orchestrator"""
|
|
88
|
+
"""End-to-end ML pipeline orchestrator."""
|
|
97
89
|
|
|
98
90
|
def __init__(self, config: PipelineConfig):
|
|
99
91
|
self.config = config
|
|
@@ -115,7 +107,7 @@ class MLPipeline:
|
|
|
115
107
|
self._setup_default_pipeline()
|
|
116
108
|
|
|
117
109
|
def _setup_default_pipeline(self):
|
|
118
|
-
"""Setup default pipeline steps"""
|
|
110
|
+
"""Setup default pipeline steps."""
|
|
119
111
|
# Data ingestion
|
|
120
112
|
self.add_step(
|
|
121
113
|
PipelineStep(
|
|
@@ -189,12 +181,12 @@ class MLPipeline:
|
|
|
189
181
|
)
|
|
190
182
|
|
|
191
183
|
def add_step(self, step: PipelineStep):
|
|
192
|
-
"""Add step to pipeline"""
|
|
184
|
+
"""Add step to pipeline."""
|
|
193
185
|
self.steps.append(step)
|
|
194
186
|
logger.debug(f"Added pipeline step: {step.name}")
|
|
195
187
|
|
|
196
188
|
def _load_raw_data(self) -> Dict[str, pd.DataFrame]:
|
|
197
|
-
"""Load raw data from sources"""
|
|
189
|
+
"""Load raw data from sources."""
|
|
198
190
|
logger.info("Loading raw data...")
|
|
199
191
|
|
|
200
192
|
# Load politician trading data
|
|
@@ -222,7 +214,7 @@ class MLPipeline:
|
|
|
222
214
|
def _preprocess_data(
|
|
223
215
|
self, trading_data: pd.DataFrame, stock_data: pd.DataFrame
|
|
224
216
|
) -> Dict[str, pd.DataFrame]:
|
|
225
|
-
"""Preprocess raw data"""
|
|
217
|
+
"""Preprocess raw data."""
|
|
226
218
|
logger.info("Preprocessing data...")
|
|
227
219
|
|
|
228
220
|
# Initialize data processor
|
|
@@ -251,7 +243,7 @@ class MLPipeline:
|
|
|
251
243
|
def _extract_features(
|
|
252
244
|
self, trading_data: pd.DataFrame, stock_data: pd.DataFrame
|
|
253
245
|
) -> Dict[str, Any]:
|
|
254
|
-
"""Extract features from preprocessed data"""
|
|
246
|
+
"""Extract features from preprocessed data."""
|
|
255
247
|
logger.info("Extracting features...")
|
|
256
248
|
|
|
257
249
|
# Initialize feature extractors
|
|
@@ -294,7 +286,7 @@ class MLPipeline:
|
|
|
294
286
|
}
|
|
295
287
|
|
|
296
288
|
def _train_model(self, X: np.ndarray, y: np.ndarray) -> Dict[str, Any]:
|
|
297
|
-
"""Train ensemble model"""
|
|
289
|
+
"""Train ensemble model."""
|
|
298
290
|
logger.info("Training model...")
|
|
299
291
|
|
|
300
292
|
# Split data
|
|
@@ -380,7 +372,7 @@ class MLPipeline:
|
|
|
380
372
|
def _evaluate_model(
|
|
381
373
|
self, model: StockRecommendationModel, X_test: np.ndarray, y_test: np.ndarray
|
|
382
374
|
) -> Dict[str, Any]:
|
|
383
|
-
"""Evaluate trained model"""
|
|
375
|
+
"""Evaluate trained model."""
|
|
384
376
|
logger.info("Evaluating model...")
|
|
385
377
|
|
|
386
378
|
# Generate predictions
|
|
@@ -407,9 +399,9 @@ class MLPipeline:
|
|
|
407
399
|
|
|
408
400
|
# Calculate AUC if binary classification
|
|
409
401
|
if probabilities.shape[1] == 2:
|
|
410
|
-
try:
|
|
402
|
+
try: # noqa: SIM105
|
|
411
403
|
evaluation_metrics["test_auc"] = roc_auc_score(y_test, probabilities[:, 1])
|
|
412
|
-
except:
|
|
404
|
+
except Exception:
|
|
413
405
|
pass
|
|
414
406
|
|
|
415
407
|
logger.info(f"Model evaluation - Test accuracy: {evaluation_metrics['test_accuracy']:.3f}")
|
|
@@ -419,7 +411,7 @@ class MLPipeline:
|
|
|
419
411
|
def _deploy_model(
|
|
420
412
|
self, model: StockRecommendationModel, metrics: Dict[str, float]
|
|
421
413
|
) -> Dict[str, Any]:
|
|
422
|
-
"""Deploy model (save to disk)"""
|
|
414
|
+
"""Deploy model (save to disk)."""
|
|
423
415
|
logger.info("Deploying model...")
|
|
424
416
|
|
|
425
417
|
# Save model
|
|
@@ -445,7 +437,7 @@ class MLPipeline:
|
|
|
445
437
|
def run(
|
|
446
438
|
self, start_step: Optional[str] = None, end_step: Optional[str] = None
|
|
447
439
|
) -> Dict[str, Any]:
|
|
448
|
-
"""Execute pipeline"""
|
|
440
|
+
"""Execute pipeline."""
|
|
449
441
|
logger.info(f"Starting pipeline: {self.config.name} v{self.config.version}")
|
|
450
442
|
|
|
451
443
|
# Start MLflow run if enabled
|
|
@@ -502,7 +494,7 @@ class MLPipeline:
|
|
|
502
494
|
self.artifacts[output_key] = value
|
|
503
495
|
|
|
504
496
|
# Log to MLflow
|
|
505
|
-
if self.experiment_tracker and "metrics" in str(result):
|
|
497
|
+
if self.experiment_tracker and "metrics" in str(result): # noqa: SIM102
|
|
506
498
|
if isinstance(result, dict) and any("metric" in k for k in result.keys()):
|
|
507
499
|
metrics_dict = result.get(
|
|
508
500
|
"training_metrics", result.get("evaluation_metrics", {})
|
|
@@ -540,7 +532,7 @@ class MLPipeline:
|
|
|
540
532
|
return {"artifacts": self.artifacts, "metrics": self.metrics, "model": self.model}
|
|
541
533
|
|
|
542
534
|
def _save_checkpoint(self, step_number: int):
|
|
543
|
-
"""Save pipeline checkpoint"""
|
|
535
|
+
"""Save pipeline checkpoint."""
|
|
544
536
|
checkpoint_path = self.config.cache_dir / f"checkpoint_step_{step_number}.pkl"
|
|
545
537
|
|
|
546
538
|
checkpoint = {
|
|
@@ -560,7 +552,7 @@ class MLPipeline:
|
|
|
560
552
|
logger.debug(f"Saved checkpoint at step {step_number}")
|
|
561
553
|
|
|
562
554
|
def load_checkpoint(self, checkpoint_path: Path):
|
|
563
|
-
"""Load pipeline checkpoint"""
|
|
555
|
+
"""Load pipeline checkpoint."""
|
|
564
556
|
with open(checkpoint_path, "rb") as f:
|
|
565
557
|
checkpoint = pickle.load(f)
|
|
566
558
|
|
|
@@ -570,7 +562,7 @@ class MLPipeline:
|
|
|
570
562
|
logger.info(f"Loaded checkpoint from step {checkpoint['step_number']}")
|
|
571
563
|
|
|
572
564
|
def _generate_mock_trading_data(self) -> pd.DataFrame:
|
|
573
|
-
"""Generate mock politician trading data for testing"""
|
|
565
|
+
"""Generate mock politician trading data for testing."""
|
|
574
566
|
np.random.seed(42)
|
|
575
567
|
n_records = 500
|
|
576
568
|
|
|
@@ -593,7 +585,7 @@ class MLPipeline:
|
|
|
593
585
|
return pd.DataFrame(data)
|
|
594
586
|
|
|
595
587
|
def _generate_mock_stock_data(self) -> pd.DataFrame:
|
|
596
|
-
"""Generate mock stock price data for testing"""
|
|
588
|
+
"""Generate mock stock price data for testing."""
|
|
597
589
|
np.random.seed(42)
|
|
598
590
|
tickers = ["AAPL", "MSFT", "GOOGL", "AMZN", "TSLA"]
|
|
599
591
|
dates = pd.date_range(end=pd.Timestamp.now(), periods=100)
|
|
@@ -619,20 +611,20 @@ class MLPipeline:
|
|
|
619
611
|
|
|
620
612
|
|
|
621
613
|
class PipelineExecutor:
|
|
622
|
-
"""Execute and manage multiple pipeline runs"""
|
|
614
|
+
"""Execute and manage multiple pipeline runs."""
|
|
623
615
|
|
|
624
616
|
def __init__(self, config: PipelineConfig):
|
|
625
617
|
self.config = config
|
|
626
618
|
self.pipelines: Dict[str, MLPipeline] = {}
|
|
627
619
|
|
|
628
620
|
def create_pipeline(self, name: str) -> MLPipeline:
|
|
629
|
-
"""Create new pipeline instance"""
|
|
621
|
+
"""Create new pipeline instance."""
|
|
630
622
|
pipeline = MLPipeline(self.config)
|
|
631
623
|
self.pipelines[name] = pipeline
|
|
632
624
|
return pipeline
|
|
633
625
|
|
|
634
626
|
def run_pipeline(self, name: str, **kwargs) -> Dict[str, Any]:
|
|
635
|
-
"""Run specific pipeline"""
|
|
627
|
+
"""Run specific pipeline."""
|
|
636
628
|
if name not in self.pipelines:
|
|
637
629
|
self.pipelines[name] = MLPipeline(self.config)
|
|
638
630
|
|
|
@@ -641,7 +633,7 @@ class PipelineExecutor:
|
|
|
641
633
|
def run_experiment(
|
|
642
634
|
self, n_runs: int = 5, param_grid: Optional[Dict[str, List]] = None
|
|
643
635
|
) -> pd.DataFrame:
|
|
644
|
-
"""Run multiple experiments with different parameters"""
|
|
636
|
+
"""Run multiple experiments with different parameters."""
|
|
645
637
|
results = []
|
|
646
638
|
|
|
647
639
|
for i in range(n_runs):
|
mcli/ml/models/__init__.py
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
"""ML Models for Stock Recommendation System"""
|
|
1
|
+
"""ML Models for Stock Recommendation System."""
|
|
2
2
|
|
|
3
|
-
from
|
|
4
|
-
from typing import Any, Dict, Optional
|
|
3
|
+
from typing import Any, Dict
|
|
5
4
|
|
|
6
5
|
import torch
|
|
7
6
|
|
|
@@ -27,7 +26,7 @@ _loaded_models: Dict[str, Any] = {}
|
|
|
27
26
|
|
|
28
27
|
|
|
29
28
|
async def load_production_models():
|
|
30
|
-
"""Load production models into memory"""
|
|
29
|
+
"""Load production models into memory."""
|
|
31
30
|
from mcli.ml.config import settings
|
|
32
31
|
from mcli.ml.logging import get_logger
|
|
33
32
|
|
|
@@ -49,7 +48,7 @@ async def load_production_models():
|
|
|
49
48
|
|
|
50
49
|
|
|
51
50
|
async def get_model_by_id(model_id: str):
|
|
52
|
-
"""Get loaded model by ID"""
|
|
51
|
+
"""Get loaded model by ID."""
|
|
53
52
|
from mcli.ml.config import settings
|
|
54
53
|
|
|
55
54
|
if model_id not in _loaded_models:
|
|
@@ -62,7 +61,7 @@ async def get_model_by_id(model_id: str):
|
|
|
62
61
|
|
|
63
62
|
|
|
64
63
|
def initialize_models():
|
|
65
|
-
"""Initialize models on startup"""
|
|
64
|
+
"""Initialize models on startup."""
|
|
66
65
|
from mcli.ml.logging import get_logger
|
|
67
66
|
|
|
68
67
|
logger = get_logger(__name__)
|