mcli-framework 7.12.2__py3-none-any.whl → 7.12.4__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.

Files changed (216) hide show
  1. mcli/app/__init__.py +0 -2
  2. mcli/app/commands_cmd.py +30 -26
  3. mcli/app/completion_helpers.py +5 -5
  4. mcli/app/init_cmd.py +10 -10
  5. mcli/app/lock_cmd.py +29 -24
  6. mcli/app/main.py +2 -8
  7. mcli/app/model/model.py +5 -10
  8. mcli/app/store_cmd.py +8 -8
  9. mcli/app/video/__init__.py +0 -2
  10. mcli/app/video/video.py +1 -14
  11. mcli/chat/chat.py +90 -108
  12. mcli/chat/command_rag.py +0 -4
  13. mcli/chat/enhanced_chat.py +32 -41
  14. mcli/chat/system_controller.py +37 -37
  15. mcli/chat/system_integration.py +4 -5
  16. mcli/cli.py +2 -3
  17. mcli/lib/api/api.py +4 -9
  18. mcli/lib/api/daemon_client.py +19 -20
  19. mcli/lib/api/daemon_client_local.py +1 -3
  20. mcli/lib/api/daemon_decorator.py +6 -6
  21. mcli/lib/api/mcli_decorators.py +4 -8
  22. mcli/lib/auth/__init__.py +0 -1
  23. mcli/lib/auth/auth.py +4 -5
  24. mcli/lib/auth/mcli_manager.py +7 -12
  25. mcli/lib/auth/token_util.py +5 -5
  26. mcli/lib/config/__init__.py +29 -1
  27. mcli/lib/config/config.py +0 -1
  28. mcli/lib/custom_commands.py +1 -1
  29. mcli/lib/discovery/command_discovery.py +15 -15
  30. mcli/lib/erd/erd.py +7 -7
  31. mcli/lib/files/files.py +1 -1
  32. mcli/lib/fs/__init__.py +31 -1
  33. mcli/lib/fs/fs.py +12 -13
  34. mcli/lib/lib.py +0 -1
  35. mcli/lib/logger/logger.py +7 -10
  36. mcli/lib/performance/optimizer.py +25 -27
  37. mcli/lib/performance/rust_bridge.py +22 -27
  38. mcli/lib/performance/uvloop_config.py +0 -1
  39. mcli/lib/pickles/__init__.py +0 -1
  40. mcli/lib/pickles/pickles.py +0 -2
  41. mcli/lib/secrets/commands.py +0 -2
  42. mcli/lib/secrets/manager.py +0 -1
  43. mcli/lib/secrets/repl.py +2 -3
  44. mcli/lib/secrets/store.py +1 -2
  45. mcli/lib/services/data_pipeline.py +34 -34
  46. mcli/lib/services/lsh_client.py +38 -40
  47. mcli/lib/shell/shell.py +2 -2
  48. mcli/lib/toml/__init__.py +0 -1
  49. mcli/lib/ui/styling.py +0 -1
  50. mcli/lib/ui/visual_effects.py +33 -41
  51. mcli/lib/watcher/watcher.py +0 -1
  52. mcli/ml/__init__.py +1 -1
  53. mcli/ml/api/__init__.py +1 -1
  54. mcli/ml/api/app.py +8 -9
  55. mcli/ml/api/middleware.py +10 -10
  56. mcli/ml/api/routers/__init__.py +1 -1
  57. mcli/ml/api/routers/admin_router.py +3 -3
  58. mcli/ml/api/routers/auth_router.py +17 -18
  59. mcli/ml/api/routers/backtest_router.py +2 -2
  60. mcli/ml/api/routers/data_router.py +2 -2
  61. mcli/ml/api/routers/model_router.py +14 -15
  62. mcli/ml/api/routers/monitoring_router.py +2 -2
  63. mcli/ml/api/routers/portfolio_router.py +2 -2
  64. mcli/ml/api/routers/prediction_router.py +10 -9
  65. mcli/ml/api/routers/trade_router.py +2 -2
  66. mcli/ml/api/routers/websocket_router.py +6 -7
  67. mcli/ml/api/schemas.py +2 -2
  68. mcli/ml/auth/__init__.py +1 -1
  69. mcli/ml/auth/auth_manager.py +22 -23
  70. mcli/ml/auth/models.py +17 -17
  71. mcli/ml/auth/permissions.py +17 -17
  72. mcli/ml/backtesting/__init__.py +1 -1
  73. mcli/ml/backtesting/backtest_engine.py +31 -35
  74. mcli/ml/backtesting/performance_metrics.py +12 -14
  75. mcli/ml/backtesting/run.py +1 -2
  76. mcli/ml/cache.py +35 -36
  77. mcli/ml/cli/__init__.py +1 -1
  78. mcli/ml/cli/main.py +21 -24
  79. mcli/ml/config/__init__.py +1 -1
  80. mcli/ml/config/settings.py +28 -29
  81. mcli/ml/configs/__init__.py +1 -1
  82. mcli/ml/configs/dvc_config.py +14 -15
  83. mcli/ml/configs/mlflow_config.py +12 -13
  84. mcli/ml/configs/mlops_manager.py +19 -21
  85. mcli/ml/dashboard/__init__.py +4 -4
  86. mcli/ml/dashboard/app.py +20 -30
  87. mcli/ml/dashboard/app_supabase.py +16 -19
  88. mcli/ml/dashboard/app_training.py +11 -14
  89. mcli/ml/dashboard/cli.py +2 -2
  90. mcli/ml/dashboard/common.py +2 -3
  91. mcli/ml/dashboard/components/__init__.py +1 -1
  92. mcli/ml/dashboard/components/charts.py +13 -11
  93. mcli/ml/dashboard/components/metrics.py +7 -7
  94. mcli/ml/dashboard/components/tables.py +12 -9
  95. mcli/ml/dashboard/overview.py +2 -2
  96. mcli/ml/dashboard/pages/__init__.py +1 -1
  97. mcli/ml/dashboard/pages/cicd.py +15 -18
  98. mcli/ml/dashboard/pages/debug_dependencies.py +7 -7
  99. mcli/ml/dashboard/pages/monte_carlo_predictions.py +11 -18
  100. mcli/ml/dashboard/pages/predictions_enhanced.py +24 -32
  101. mcli/ml/dashboard/pages/scrapers_and_logs.py +22 -24
  102. mcli/ml/dashboard/pages/test_portfolio.py +3 -6
  103. mcli/ml/dashboard/pages/trading.py +16 -18
  104. mcli/ml/dashboard/pages/workflows.py +20 -30
  105. mcli/ml/dashboard/utils.py +9 -9
  106. mcli/ml/dashboard/warning_suppression.py +3 -3
  107. mcli/ml/data_ingestion/__init__.py +1 -1
  108. mcli/ml/data_ingestion/api_connectors.py +41 -46
  109. mcli/ml/data_ingestion/data_pipeline.py +36 -46
  110. mcli/ml/data_ingestion/stream_processor.py +43 -46
  111. mcli/ml/database/__init__.py +1 -1
  112. mcli/ml/database/migrations/env.py +2 -2
  113. mcli/ml/database/models.py +22 -24
  114. mcli/ml/database/session.py +14 -14
  115. mcli/ml/experimentation/__init__.py +1 -1
  116. mcli/ml/experimentation/ab_testing.py +45 -46
  117. mcli/ml/features/__init__.py +1 -1
  118. mcli/ml/features/ensemble_features.py +22 -27
  119. mcli/ml/features/recommendation_engine.py +30 -30
  120. mcli/ml/features/stock_features.py +29 -32
  121. mcli/ml/features/test_feature_engineering.py +10 -11
  122. mcli/ml/logging.py +4 -4
  123. mcli/ml/mlops/__init__.py +1 -1
  124. mcli/ml/mlops/data_versioning.py +29 -30
  125. mcli/ml/mlops/experiment_tracker.py +24 -24
  126. mcli/ml/mlops/model_serving.py +31 -34
  127. mcli/ml/mlops/pipeline_orchestrator.py +27 -35
  128. mcli/ml/models/__init__.py +5 -6
  129. mcli/ml/models/base_models.py +23 -23
  130. mcli/ml/models/ensemble_models.py +31 -31
  131. mcli/ml/models/recommendation_models.py +18 -19
  132. mcli/ml/models/test_models.py +14 -16
  133. mcli/ml/monitoring/__init__.py +1 -1
  134. mcli/ml/monitoring/drift_detection.py +32 -36
  135. mcli/ml/monitoring/metrics.py +2 -2
  136. mcli/ml/optimization/__init__.py +1 -1
  137. mcli/ml/optimization/optimize.py +1 -2
  138. mcli/ml/optimization/portfolio_optimizer.py +30 -32
  139. mcli/ml/predictions/__init__.py +1 -1
  140. mcli/ml/preprocessing/__init__.py +1 -1
  141. mcli/ml/preprocessing/data_cleaners.py +22 -23
  142. mcli/ml/preprocessing/feature_extractors.py +23 -26
  143. mcli/ml/preprocessing/ml_pipeline.py +23 -23
  144. mcli/ml/preprocessing/test_preprocessing.py +7 -8
  145. mcli/ml/scripts/populate_sample_data.py +0 -4
  146. mcli/ml/serving/serve.py +1 -2
  147. mcli/ml/tasks.py +17 -17
  148. mcli/ml/tests/test_integration.py +29 -30
  149. mcli/ml/tests/test_training_dashboard.py +21 -21
  150. mcli/ml/trading/__init__.py +1 -1
  151. mcli/ml/trading/migrations.py +5 -5
  152. mcli/ml/trading/models.py +21 -23
  153. mcli/ml/trading/paper_trading.py +16 -13
  154. mcli/ml/trading/risk_management.py +17 -18
  155. mcli/ml/trading/trading_service.py +25 -28
  156. mcli/ml/training/__init__.py +1 -1
  157. mcli/ml/training/train.py +0 -1
  158. mcli/public/oi/oi.py +1 -2
  159. mcli/self/completion_cmd.py +6 -10
  160. mcli/self/logs_cmd.py +19 -24
  161. mcli/self/migrate_cmd.py +22 -20
  162. mcli/self/redis_cmd.py +10 -11
  163. mcli/self/self_cmd.py +10 -18
  164. mcli/self/store_cmd.py +10 -12
  165. mcli/self/visual_cmd.py +9 -14
  166. mcli/self/zsh_cmd.py +2 -4
  167. mcli/workflow/daemon/async_command_database.py +23 -24
  168. mcli/workflow/daemon/async_process_manager.py +27 -29
  169. mcli/workflow/daemon/client.py +27 -33
  170. mcli/workflow/daemon/daemon.py +32 -36
  171. mcli/workflow/daemon/enhanced_daemon.py +24 -33
  172. mcli/workflow/daemon/process_cli.py +11 -12
  173. mcli/workflow/daemon/process_manager.py +23 -26
  174. mcli/workflow/daemon/test_daemon.py +4 -5
  175. mcli/workflow/dashboard/dashboard_cmd.py +0 -1
  176. mcli/workflow/doc_convert.py +15 -17
  177. mcli/workflow/gcloud/__init__.py +0 -1
  178. mcli/workflow/gcloud/gcloud.py +11 -8
  179. mcli/workflow/git_commit/ai_service.py +14 -15
  180. mcli/workflow/lsh_integration.py +9 -11
  181. mcli/workflow/model_service/client.py +26 -31
  182. mcli/workflow/model_service/download_and_run_efficient_models.py +10 -14
  183. mcli/workflow/model_service/lightweight_embedder.py +25 -35
  184. mcli/workflow/model_service/lightweight_model_server.py +26 -32
  185. mcli/workflow/model_service/lightweight_test.py +7 -10
  186. mcli/workflow/model_service/model_service.py +80 -91
  187. mcli/workflow/model_service/ollama_efficient_runner.py +14 -18
  188. mcli/workflow/model_service/openai_adapter.py +23 -23
  189. mcli/workflow/model_service/pdf_processor.py +21 -26
  190. mcli/workflow/model_service/test_efficient_runner.py +12 -16
  191. mcli/workflow/model_service/test_example.py +11 -13
  192. mcli/workflow/model_service/test_integration.py +3 -5
  193. mcli/workflow/model_service/test_new_features.py +7 -8
  194. mcli/workflow/notebook/converter.py +1 -1
  195. mcli/workflow/notebook/notebook_cmd.py +5 -6
  196. mcli/workflow/notebook/schema.py +0 -1
  197. mcli/workflow/notebook/validator.py +7 -3
  198. mcli/workflow/openai/openai.py +1 -2
  199. mcli/workflow/registry/registry.py +4 -1
  200. mcli/workflow/repo/repo.py +6 -7
  201. mcli/workflow/scheduler/cron_parser.py +16 -19
  202. mcli/workflow/scheduler/job.py +10 -10
  203. mcli/workflow/scheduler/monitor.py +15 -15
  204. mcli/workflow/scheduler/persistence.py +17 -18
  205. mcli/workflow/scheduler/scheduler.py +37 -38
  206. mcli/workflow/secrets/__init__.py +1 -1
  207. mcli/workflow/sync/test_cmd.py +0 -1
  208. mcli/workflow/wakatime/__init__.py +5 -9
  209. mcli/workflow/wakatime/wakatime.py +1 -2
  210. {mcli_framework-7.12.2.dist-info → mcli_framework-7.12.4.dist-info}/METADATA +1 -1
  211. mcli_framework-7.12.4.dist-info/RECORD +279 -0
  212. mcli_framework-7.12.2.dist-info/RECORD +0 -279
  213. {mcli_framework-7.12.2.dist-info → mcli_framework-7.12.4.dist-info}/WHEEL +0 -0
  214. {mcli_framework-7.12.2.dist-info → mcli_framework-7.12.4.dist-info}/entry_points.txt +0 -0
  215. {mcli_framework-7.12.2.dist-info → mcli_framework-7.12.4.dist-info}/licenses/LICENSE +0 -0
  216. {mcli_framework-7.12.2.dist-info → mcli_framework-7.12.4.dist-info}/top_level.txt +0 -0
@@ -1,18 +1,17 @@
1
- """Model management API routes"""
1
+ """Model management API routes."""
2
2
 
3
3
  from datetime import datetime
4
4
  from typing import List, Optional
5
5
  from uuid import UUID
6
6
 
7
7
  from fastapi import APIRouter, Depends, File, HTTPException, Query, UploadFile, status
8
- from sqlalchemy import select
9
8
  from sqlalchemy.orm import Session
10
9
 
11
10
  from mcli.ml.api.schemas import ModelCreate, ModelMetrics, ModelResponse, ModelUpdate
12
- from mcli.ml.auth import Permission, get_current_active_user, require_role
11
+ from mcli.ml.auth import get_current_active_user, require_role
13
12
  from mcli.ml.cache import cached
14
13
  from mcli.ml.database.models import Model, ModelStatus, User, UserRole
15
- from mcli.ml.database.session import get_async_db, get_db
14
+ from mcli.ml.database.session import get_db
16
15
  from mcli.ml.tasks import train_model_task
17
16
 
18
17
  router = APIRouter()
@@ -27,7 +26,7 @@ async def list_models(
27
26
  current_user: User = Depends(get_current_active_user),
28
27
  db: Session = Depends(get_db),
29
28
  ):
30
- """List all available models"""
29
+ """List all available models."""
31
30
  query = db.query(Model)
32
31
 
33
32
  if status:
@@ -44,7 +43,7 @@ async def get_model(
44
43
  current_user: User = Depends(get_current_active_user),
45
44
  db: Session = Depends(get_db),
46
45
  ):
47
- """Get specific model details"""
46
+ """Get specific model details."""
48
47
  model = db.query(Model).filter(Model.id == model_id).first()
49
48
 
50
49
  if not model:
@@ -59,7 +58,7 @@ async def create_model(
59
58
  current_user: User = Depends(require_role(UserRole.ANALYST, UserRole.ADMIN)),
60
59
  db: Session = Depends(get_db),
61
60
  ):
62
- """Create a new model"""
61
+ """Create a new model."""
63
62
  model = Model(
64
63
  **model_data.dict(), created_by=current_user.username, status=ModelStatus.TRAINING
65
64
  )
@@ -81,7 +80,7 @@ async def update_model(
81
80
  current_user: User = Depends(require_role(UserRole.ANALYST, UserRole.ADMIN)),
82
81
  db: Session = Depends(get_db),
83
82
  ):
84
- """Update model metadata"""
83
+ """Update model metadata."""
85
84
  model = db.query(Model).filter(Model.id == model_id).first()
86
85
 
87
86
  if not model:
@@ -104,7 +103,7 @@ async def deploy_model(
104
103
  current_user: User = Depends(require_role(UserRole.ANALYST, UserRole.ADMIN)),
105
104
  db: Session = Depends(get_db),
106
105
  ):
107
- """Deploy model to production"""
106
+ """Deploy model to production."""
108
107
  model = db.query(Model).filter(Model.id == model_id).first()
109
108
 
110
109
  if not model:
@@ -132,7 +131,7 @@ async def archive_model(
132
131
  current_user: User = Depends(require_role(UserRole.ADMIN)),
133
132
  db: Session = Depends(get_db),
134
133
  ):
135
- """Archive a model"""
134
+ """Archive a model."""
136
135
  model = db.query(Model).filter(Model.id == model_id).first()
137
136
 
138
137
  if not model:
@@ -151,7 +150,7 @@ async def get_model_metrics(
151
150
  current_user: User = Depends(get_current_active_user),
152
151
  db: Session = Depends(get_db),
153
152
  ):
154
- """Get model performance metrics"""
153
+ """Get model performance metrics."""
155
154
  model = db.query(Model).filter(Model.id == model_id).first()
156
155
 
157
156
  if not model:
@@ -176,7 +175,7 @@ async def retrain_model(
176
175
  current_user: User = Depends(require_role(UserRole.ANALYST, UserRole.ADMIN)),
177
176
  db: Session = Depends(get_db),
178
177
  ):
179
- """Retrain an existing model"""
178
+ """Retrain an existing model."""
180
179
  model = db.query(Model).filter(Model.id == model_id).first()
181
180
 
182
181
  if not model:
@@ -202,7 +201,7 @@ async def upload_model_artifact(
202
201
  current_user: User = Depends(require_role(UserRole.ANALYST, UserRole.ADMIN)),
203
202
  db: Session = Depends(get_db),
204
203
  ):
205
- """Upload model artifact file"""
204
+ """Upload model artifact file."""
206
205
  model = db.query(Model).filter(Model.id == model_id).first()
207
206
 
208
207
  if not model:
@@ -224,7 +223,7 @@ async def delete_model(
224
223
  current_user: User = Depends(require_role(UserRole.ADMIN)),
225
224
  db: Session = Depends(get_db),
226
225
  ):
227
- """Delete a model"""
226
+ """Delete a model."""
228
227
  model = db.query(Model).filter(Model.id == model_id).first()
229
228
 
230
229
  if not model:
@@ -248,7 +247,7 @@ async def download_model(
248
247
  current_user: User = Depends(get_current_active_user),
249
248
  db: Session = Depends(get_db),
250
249
  ):
251
- """Download model artifact"""
250
+ """Download model artifact."""
252
251
  model = db.query(Model).filter(Model.id == model_id).first()
253
252
 
254
253
  if not model:
@@ -1,4 +1,4 @@
1
- """Monitoring API routes"""
1
+ """Monitoring API routes."""
2
2
 
3
3
  from fastapi import APIRouter, Depends
4
4
 
@@ -10,5 +10,5 @@ router = APIRouter()
10
10
 
11
11
  @router.get("/drift")
12
12
  async def get_drift_status(current_user: User = Depends(get_current_active_user)):
13
- """Get drift monitoring status"""
13
+ """Get drift monitoring status."""
14
14
  return {"drift_detected": False}
@@ -1,4 +1,4 @@
1
- """Portfolio management API routes"""
1
+ """Portfolio management API routes."""
2
2
 
3
3
  from fastapi import APIRouter, Depends
4
4
 
@@ -10,5 +10,5 @@ router = APIRouter()
10
10
 
11
11
  @router.get("/")
12
12
  async def list_portfolios(current_user: User = Depends(get_current_active_user)):
13
- """List user portfolios"""
13
+ """List user portfolios."""
14
14
  return {"portfolios": []}
@@ -1,14 +1,15 @@
1
- """Prediction API routes"""
1
+ """Prediction API routes."""
2
2
 
3
3
  from datetime import datetime, timedelta
4
4
  from typing import List, Optional
5
5
  from uuid import UUID
6
6
 
7
+ import numpy as np
7
8
  from fastapi import APIRouter, BackgroundTasks, Depends, HTTPException, Query, status
8
9
  from pydantic import BaseModel
9
10
  from sqlalchemy.orm import Session
10
11
 
11
- from mcli.ml.api.schemas import BatchPredictionRequest, PredictionRequest, PredictionResponse
12
+ from mcli.ml.api.schemas import BatchPredictionRequest, PredictionResponse
12
13
  from mcli.ml.auth import get_current_active_user
13
14
  from mcli.ml.cache import cache_set, cached
14
15
  from mcli.ml.database.models import Model, Prediction, StockData, User
@@ -32,7 +33,7 @@ async def create_prediction(
32
33
  current_user: User = Depends(get_current_active_user),
33
34
  db: Session = Depends(get_db),
34
35
  ):
35
- """Create a new prediction"""
36
+ """Create a new prediction."""
36
37
 
37
38
  # Get model (use default if not specified)
38
39
  if request.model_id:
@@ -107,7 +108,7 @@ async def create_batch_predictions(
107
108
  current_user: User = Depends(get_current_active_user),
108
109
  db: Session = Depends(get_db),
109
110
  ):
110
- """Create predictions for multiple tickers"""
111
+ """Create predictions for multiple tickers."""
111
112
  predictions = []
112
113
 
113
114
  for ticker_data in request.tickers:
@@ -135,7 +136,7 @@ async def list_predictions(
135
136
  current_user: User = Depends(get_current_active_user),
136
137
  db: Session = Depends(get_db),
137
138
  ):
138
- """List user's predictions"""
139
+ """List user's predictions."""
139
140
  query = db.query(Prediction).filter(Prediction.user_id == current_user.id)
140
141
 
141
142
  if ticker:
@@ -159,7 +160,7 @@ async def get_prediction(
159
160
  current_user: User = Depends(get_current_active_user),
160
161
  db: Session = Depends(get_db),
161
162
  ):
162
- """Get specific prediction details"""
163
+ """Get specific prediction details."""
163
164
  prediction = (
164
165
  db.query(Prediction)
165
166
  .filter(Prediction.id == prediction_id, Prediction.user_id == current_user.id)
@@ -178,7 +179,7 @@ async def get_prediction_outcome(
178
179
  current_user: User = Depends(get_current_active_user),
179
180
  db: Session = Depends(get_db),
180
181
  ):
181
- """Get actual outcome of a prediction"""
182
+ """Get actual outcome of a prediction."""
182
183
  prediction = (
183
184
  db.query(Prediction)
184
185
  .filter(Prediction.id == prediction_id, Prediction.user_id == current_user.id)
@@ -221,7 +222,7 @@ async def get_latest_recommendations(
221
222
  current_user: User = Depends(get_current_active_user),
222
223
  db: Session = Depends(get_db),
223
224
  ):
224
- """Get latest stock recommendations"""
225
+ """Get latest stock recommendations."""
225
226
  # Get recent predictions with high confidence
226
227
  predictions = (
227
228
  db.query(Prediction)
@@ -254,7 +255,7 @@ async def get_latest_recommendations(
254
255
 
255
256
 
256
257
  async def update_stock_data(ticker: str, db: Session):
257
- """Background task to update stock data"""
258
+ """Background task to update stock data."""
258
259
  # In real implementation, fetch latest stock data
259
260
  stock = db.query(StockData).filter(StockData.ticker == ticker).first()
260
261
  if stock:
@@ -1,4 +1,4 @@
1
- """Trading API routes"""
1
+ """Trading API routes."""
2
2
 
3
3
  from fastapi import APIRouter, Depends
4
4
 
@@ -12,5 +12,5 @@ router = APIRouter()
12
12
  async def get_politician_trades(
13
13
  politician_id: str, current_user: User = Depends(get_current_active_user)
14
14
  ):
15
- """Get politician trades"""
15
+ """Get politician trades."""
16
16
  return {"politician_id": politician_id, "trades": []}
@@ -1,10 +1,9 @@
1
- """WebSocket API routes for real-time updates"""
1
+ """WebSocket API routes for real-time updates."""
2
2
 
3
3
  import asyncio
4
- import json
5
4
  from typing import Dict, Set
6
5
 
7
- from fastapi import APIRouter, Depends, WebSocket, WebSocketDisconnect
6
+ from fastapi import APIRouter, WebSocket, WebSocketDisconnect
8
7
 
9
8
  from mcli.ml.logging import get_logger
10
9
 
@@ -13,7 +12,7 @@ logger = get_logger(__name__)
13
12
 
14
13
 
15
14
  class ConnectionManager:
16
- """Manage WebSocket connections"""
15
+ """Manage WebSocket connections."""
17
16
 
18
17
  def __init__(self):
19
18
  self.active_connections: Dict[str, Set[WebSocket]] = {}
@@ -36,7 +35,7 @@ class ConnectionManager:
36
35
  for connection in self.active_connections[channel]:
37
36
  try:
38
37
  await connection.send_json(message)
39
- except:
38
+ except Exception:
40
39
  disconnected.add(connection)
41
40
 
42
41
  # Remove disconnected clients
@@ -49,7 +48,7 @@ manager = ConnectionManager()
49
48
 
50
49
  @router.websocket("/predictions")
51
50
  async def websocket_predictions(websocket: WebSocket):
52
- """Real-time prediction updates"""
51
+ """Real-time prediction updates."""
53
52
  await manager.connect(websocket, "predictions")
54
53
  try:
55
54
  while True:
@@ -62,7 +61,7 @@ async def websocket_predictions(websocket: WebSocket):
62
61
 
63
62
  @router.websocket("/prices")
64
63
  async def websocket_prices(websocket: WebSocket):
65
- """Real-time price updates"""
64
+ """Real-time price updates."""
66
65
  await manager.connect(websocket, "prices")
67
66
  try:
68
67
  while True:
mcli/ml/api/schemas.py CHANGED
@@ -1,10 +1,10 @@
1
- """API request/response schemas"""
1
+ """API request/response schemas."""
2
2
 
3
3
  from datetime import datetime
4
4
  from typing import Any, Dict, List, Optional
5
5
  from uuid import UUID
6
6
 
7
- from pydantic import BaseModel, Field
7
+ from pydantic import BaseModel
8
8
 
9
9
 
10
10
  # Model schemas
mcli/ml/auth/__init__.py CHANGED
@@ -1,4 +1,4 @@
1
- """Authentication and authorization system"""
1
+ """Authentication and authorization system."""
2
2
 
3
3
  from .auth_manager import (
4
4
  AuthManager,
@@ -1,15 +1,13 @@
1
- """Authentication manager with JWT support"""
1
+ """Authentication manager with JWT support."""
2
2
 
3
3
  import secrets
4
4
  from datetime import datetime, timedelta
5
- from typing import Any, Dict, Optional, Union
6
- from uuid import UUID
5
+ from typing import Optional
7
6
 
8
7
  import bcrypt
9
8
  import jwt
10
9
  from fastapi import Depends, HTTPException, Request, status
11
10
  from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
12
- from sqlalchemy import select
13
11
  from sqlalchemy.orm import Session
14
12
 
15
13
  from mcli.ml.config import settings
@@ -23,7 +21,7 @@ security = HTTPBearer()
23
21
 
24
22
 
25
23
  class AuthManager:
26
- """Authentication and authorization manager"""
24
+ """Authentication and authorization manager."""
27
25
 
28
26
  def __init__(self):
29
27
  self.secret_key = settings.api.secret_key
@@ -32,19 +30,19 @@ class AuthManager:
32
30
  self.refresh_token_expire_days = 7
33
31
 
34
32
  def hash_password(self, password: str) -> str:
35
- """Hash a password using bcrypt"""
33
+ """Hash a password using bcrypt."""
36
34
  salt = bcrypt.gensalt()
37
35
  hashed = bcrypt.hashpw(password.encode("utf-8"), salt)
38
36
  return hashed.decode("utf-8")
39
37
 
40
38
  def verify_password(self, plain_password: str, hashed_password: str) -> bool:
41
- """Verify a password against a hash"""
39
+ """Verify a password against a hash."""
42
40
  return bcrypt.checkpw(plain_password.encode("utf-8"), hashed_password.encode("utf-8"))
43
41
 
44
42
  def create_access_token(
45
43
  self, user_id: str, username: str, role: str, expires_delta: Optional[timedelta] = None
46
44
  ) -> str:
47
- """Create a JWT access token"""
45
+ """Create a JWT access token."""
48
46
  if expires_delta:
49
47
  expire = datetime.utcnow() + expires_delta
50
48
  else:
@@ -63,7 +61,7 @@ class AuthManager:
63
61
  return encoded_jwt
64
62
 
65
63
  def create_refresh_token(self, user_id: str, expires_delta: Optional[timedelta] = None) -> str:
66
- """Create a refresh token"""
64
+ """Create a refresh token."""
67
65
  if expires_delta:
68
66
  expire = datetime.utcnow() + expires_delta
69
67
  else:
@@ -81,7 +79,7 @@ class AuthManager:
81
79
  return encoded_jwt
82
80
 
83
81
  def verify_token(self, token: str) -> Optional[TokenData]:
84
- """Verify and decode a JWT token"""
82
+ """Verify and decode a JWT token."""
85
83
  try:
86
84
  payload = jwt.decode(token, self.secret_key, algorithms=[self.algorithm])
87
85
 
@@ -110,7 +108,7 @@ class AuthManager:
110
108
  )
111
109
 
112
110
  async def register_user(self, user_data: UserCreate, db: Session) -> User:
113
- """Register a new user"""
111
+ """Register a new user."""
114
112
  # Check if user already exists
115
113
  existing_user = (
116
114
  db.query(User)
@@ -149,7 +147,7 @@ class AuthManager:
149
147
  return new_user
150
148
 
151
149
  async def authenticate_user(self, login_data: UserLogin, db: Session) -> Optional[User]:
152
- """Authenticate a user"""
150
+ """Authenticate a user."""
153
151
  user = db.query(User).filter(User.username == login_data.username).first()
154
152
 
155
153
  if not user:
@@ -165,7 +163,7 @@ class AuthManager:
165
163
  return user
166
164
 
167
165
  async def login(self, login_data: UserLogin, db: Session) -> TokenResponse:
168
- """Login user and return tokens"""
166
+ """Login user and return tokens."""
169
167
  user = await self.authenticate_user(login_data, db)
170
168
 
171
169
  if not user:
@@ -195,7 +193,7 @@ class AuthManager:
195
193
  )
196
194
 
197
195
  async def refresh_access_token(self, refresh_token: str, db: Session) -> TokenResponse:
198
- """Refresh access token using refresh token"""
196
+ """Refresh access token using refresh token."""
199
197
  try:
200
198
  payload = jwt.decode(refresh_token, self.secret_key, algorithms=[self.algorithm])
201
199
 
@@ -238,7 +236,7 @@ class AuthManager:
238
236
  credentials: HTTPAuthorizationCredentials = Depends(security),
239
237
  db: Session = Depends(get_db),
240
238
  ) -> User:
241
- """Get current authenticated user from JWT token"""
239
+ """Get current authenticated user from JWT token."""
242
240
  token = credentials.credentials
243
241
 
244
242
  token_data = self.verify_token(token)
@@ -256,7 +254,7 @@ class AuthManager:
256
254
  return user
257
255
 
258
256
  def require_role(self, *allowed_roles: UserRole):
259
- """Decorator/dependency to require specific roles"""
257
+ """Decorator/dependency to require specific roles."""
260
258
 
261
259
  async def role_checker(current_user: User = Depends(self.get_current_user)) -> User:
262
260
  if current_user.role not in allowed_roles:
@@ -281,14 +279,14 @@ require_role = auth_manager.require_role
281
279
 
282
280
 
283
281
  async def get_current_active_user(current_user: User = Depends(get_current_user)) -> User:
284
- """Get current active user"""
282
+ """Get current active user."""
285
283
  if not current_user.is_active:
286
284
  raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Inactive user")
287
285
  return current_user
288
286
 
289
287
 
290
288
  async def get_admin_user(current_user: User = Depends(get_current_user)) -> User:
291
- """Get current admin user"""
289
+ """Get current admin user."""
292
290
  if current_user.role != UserRole.ADMIN:
293
291
  raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Admin access required")
294
292
  return current_user
@@ -298,11 +296,12 @@ import asyncio
298
296
 
299
297
  # Rate limiting
300
298
  from collections import defaultdict
301
- from datetime import datetime, timedelta
299
+
300
+ # datetime and timedelta already imported at top
302
301
 
303
302
 
304
303
  class RateLimiter:
305
- """Simple rate limiter"""
304
+ """Simple rate limiter."""
306
305
 
307
306
  def __init__(self, requests: int = 100, window: int = 60):
308
307
  self.requests = requests
@@ -311,7 +310,7 @@ class RateLimiter:
311
310
  self._cleanup_task = None
312
311
 
313
312
  async def check_rate_limit(self, client_id: str) -> bool:
314
- """Check if client has exceeded rate limit"""
313
+ """Check if client has exceeded rate limit."""
315
314
  now = datetime.utcnow()
316
315
  minute_ago = now - timedelta(seconds=self.window)
317
316
 
@@ -329,7 +328,7 @@ class RateLimiter:
329
328
  return True
330
329
 
331
330
  async def cleanup(self):
332
- """Periodic cleanup of old entries"""
331
+ """Periodic cleanup of old entries."""
333
332
  while True:
334
333
  await asyncio.sleep(300) # Clean every 5 minutes
335
334
  now = datetime.utcnow()
@@ -349,7 +348,7 @@ rate_limiter = RateLimiter(requests=settings.api.rate_limit, window=60)
349
348
 
350
349
 
351
350
  async def check_rate_limit(request: Request):
352
- """FastAPI dependency to check rate limit"""
351
+ """FastAPI dependency to check rate limit."""
353
352
  client_ip = request.client.host
354
353
 
355
354
  if not await rate_limiter.check_rate_limit(client_ip):
mcli/ml/auth/models.py CHANGED
@@ -1,4 +1,4 @@
1
- """Authentication data models"""
1
+ """Authentication data models."""
2
2
 
3
3
  from datetime import datetime
4
4
  from typing import List, Optional
@@ -8,7 +8,7 @@ from pydantic import BaseModel, EmailStr, Field, validator
8
8
 
9
9
 
10
10
  class UserCreate(BaseModel):
11
- """User registration model"""
11
+ """User registration model."""
12
12
 
13
13
  username: str = Field(..., min_length=3, max_length=50, pattern="^[a-zA-Z0-9_-]+$")
14
14
  email: EmailStr
@@ -18,7 +18,7 @@ class UserCreate(BaseModel):
18
18
 
19
19
  @validator("password")
20
20
  def validate_password(cls, v):
21
- """Ensure password meets security requirements"""
21
+ """Ensure password meets security requirements."""
22
22
  if len(v) < 8:
23
23
  raise ValueError("Password must be at least 8 characters long")
24
24
  if not any(char.isdigit() for char in v):
@@ -31,14 +31,14 @@ class UserCreate(BaseModel):
31
31
 
32
32
 
33
33
  class UserLogin(BaseModel):
34
- """User login model"""
34
+ """User login model."""
35
35
 
36
36
  username: str
37
37
  password: str
38
38
 
39
39
 
40
40
  class UserResponse(BaseModel):
41
- """User response model"""
41
+ """User response model."""
42
42
 
43
43
  id: UUID
44
44
  username: str
@@ -56,7 +56,7 @@ class UserResponse(BaseModel):
56
56
 
57
57
 
58
58
  class TokenResponse(BaseModel):
59
- """JWT token response"""
59
+ """JWT token response."""
60
60
 
61
61
  access_token: str
62
62
  token_type: str = "Bearer"
@@ -66,7 +66,7 @@ class TokenResponse(BaseModel):
66
66
 
67
67
 
68
68
  class TokenData(BaseModel):
69
- """JWT token payload"""
69
+ """JWT token payload."""
70
70
 
71
71
  sub: str # User ID
72
72
  username: str
@@ -77,27 +77,27 @@ class TokenData(BaseModel):
77
77
 
78
78
 
79
79
  class PasswordReset(BaseModel):
80
- """Password reset request"""
80
+ """Password reset request."""
81
81
 
82
82
  email: EmailStr
83
83
 
84
84
 
85
85
  class PasswordResetConfirm(BaseModel):
86
- """Password reset confirmation"""
86
+ """Password reset confirmation."""
87
87
 
88
88
  token: str
89
89
  new_password: str = Field(..., min_length=8, max_length=100)
90
90
 
91
91
 
92
92
  class PasswordChange(BaseModel):
93
- """Password change request"""
93
+ """Password change request."""
94
94
 
95
95
  current_password: str
96
96
  new_password: str = Field(..., min_length=8, max_length=100)
97
97
 
98
98
  @validator("new_password")
99
99
  def validate_password(cls, v, values):
100
- """Ensure new password is different and meets requirements"""
100
+ """Ensure new password is different and meets requirements."""
101
101
  if "current_password" in values and v == values["current_password"]:
102
102
  raise ValueError("New password must be different from current password")
103
103
 
@@ -113,14 +113,14 @@ class PasswordChange(BaseModel):
113
113
 
114
114
 
115
115
  class APIKeyCreate(BaseModel):
116
- """API key creation model"""
116
+ """API key creation model."""
117
117
 
118
118
  name: str = Field(..., min_length=1, max_length=100)
119
119
  expires_at: Optional[datetime] = None
120
120
 
121
121
 
122
122
  class APIKeyResponse(BaseModel):
123
- """API key response"""
123
+ """API key response."""
124
124
 
125
125
  key: str
126
126
  name: str
@@ -130,7 +130,7 @@ class APIKeyResponse(BaseModel):
130
130
 
131
131
 
132
132
  class UserUpdate(BaseModel):
133
- """User update model"""
133
+ """User update model."""
134
134
 
135
135
  email: Optional[EmailStr] = None
136
136
  first_name: Optional[str] = Field(None, max_length=50)
@@ -140,7 +140,7 @@ class UserUpdate(BaseModel):
140
140
 
141
141
 
142
142
  class UserPermissions(BaseModel):
143
- """User permissions model"""
143
+ """User permissions model."""
144
144
 
145
145
  user_id: UUID
146
146
  permissions: List[str]
@@ -148,7 +148,7 @@ class UserPermissions(BaseModel):
148
148
 
149
149
 
150
150
  class SessionInfo(BaseModel):
151
- """Session information"""
151
+ """Session information."""
152
152
 
153
153
  session_id: str
154
154
  user_id: UUID
@@ -160,7 +160,7 @@ class SessionInfo(BaseModel):
160
160
 
161
161
 
162
162
  class LoginAttempt(BaseModel):
163
- """Login attempt tracking"""
163
+ """Login attempt tracking."""
164
164
 
165
165
  username: str
166
166
  ip_address: str