mcli-framework 7.1.1__py3-none-any.whl → 7.1.2__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 (94) hide show
  1. mcli/app/completion_cmd.py +59 -49
  2. mcli/app/completion_helpers.py +60 -138
  3. mcli/app/logs_cmd.py +6 -2
  4. mcli/app/main.py +17 -14
  5. mcli/app/model_cmd.py +19 -4
  6. mcli/chat/chat.py +3 -2
  7. mcli/lib/search/cached_vectorizer.py +1 -0
  8. mcli/lib/services/data_pipeline.py +12 -5
  9. mcli/lib/services/lsh_client.py +68 -57
  10. mcli/ml/api/app.py +28 -36
  11. mcli/ml/api/middleware.py +8 -16
  12. mcli/ml/api/routers/admin_router.py +3 -1
  13. mcli/ml/api/routers/auth_router.py +32 -56
  14. mcli/ml/api/routers/backtest_router.py +3 -1
  15. mcli/ml/api/routers/data_router.py +3 -1
  16. mcli/ml/api/routers/model_router.py +35 -74
  17. mcli/ml/api/routers/monitoring_router.py +3 -1
  18. mcli/ml/api/routers/portfolio_router.py +3 -1
  19. mcli/ml/api/routers/prediction_router.py +60 -65
  20. mcli/ml/api/routers/trade_router.py +6 -2
  21. mcli/ml/api/routers/websocket_router.py +12 -9
  22. mcli/ml/api/schemas.py +10 -2
  23. mcli/ml/auth/auth_manager.py +49 -114
  24. mcli/ml/auth/models.py +30 -15
  25. mcli/ml/auth/permissions.py +12 -19
  26. mcli/ml/backtesting/backtest_engine.py +134 -108
  27. mcli/ml/backtesting/performance_metrics.py +142 -108
  28. mcli/ml/cache.py +12 -18
  29. mcli/ml/cli/main.py +37 -23
  30. mcli/ml/config/settings.py +29 -12
  31. mcli/ml/dashboard/app.py +122 -130
  32. mcli/ml/dashboard/app_integrated.py +216 -150
  33. mcli/ml/dashboard/app_supabase.py +176 -108
  34. mcli/ml/dashboard/app_training.py +212 -206
  35. mcli/ml/dashboard/cli.py +14 -5
  36. mcli/ml/data_ingestion/api_connectors.py +51 -81
  37. mcli/ml/data_ingestion/data_pipeline.py +127 -125
  38. mcli/ml/data_ingestion/stream_processor.py +72 -80
  39. mcli/ml/database/migrations/env.py +3 -2
  40. mcli/ml/database/models.py +112 -79
  41. mcli/ml/database/session.py +6 -5
  42. mcli/ml/experimentation/ab_testing.py +149 -99
  43. mcli/ml/features/ensemble_features.py +9 -8
  44. mcli/ml/features/political_features.py +6 -5
  45. mcli/ml/features/recommendation_engine.py +15 -14
  46. mcli/ml/features/stock_features.py +7 -6
  47. mcli/ml/features/test_feature_engineering.py +8 -7
  48. mcli/ml/logging.py +10 -15
  49. mcli/ml/mlops/data_versioning.py +57 -64
  50. mcli/ml/mlops/experiment_tracker.py +49 -41
  51. mcli/ml/mlops/model_serving.py +59 -62
  52. mcli/ml/mlops/pipeline_orchestrator.py +203 -149
  53. mcli/ml/models/base_models.py +8 -7
  54. mcli/ml/models/ensemble_models.py +6 -5
  55. mcli/ml/models/recommendation_models.py +7 -6
  56. mcli/ml/models/test_models.py +18 -14
  57. mcli/ml/monitoring/drift_detection.py +95 -74
  58. mcli/ml/monitoring/metrics.py +10 -22
  59. mcli/ml/optimization/portfolio_optimizer.py +172 -132
  60. mcli/ml/predictions/prediction_engine.py +62 -50
  61. mcli/ml/preprocessing/data_cleaners.py +6 -5
  62. mcli/ml/preprocessing/feature_extractors.py +7 -6
  63. mcli/ml/preprocessing/ml_pipeline.py +3 -2
  64. mcli/ml/preprocessing/politician_trading_preprocessor.py +11 -10
  65. mcli/ml/preprocessing/test_preprocessing.py +4 -4
  66. mcli/ml/scripts/populate_sample_data.py +36 -16
  67. mcli/ml/tasks.py +82 -83
  68. mcli/ml/tests/test_integration.py +86 -76
  69. mcli/ml/tests/test_training_dashboard.py +169 -142
  70. mcli/mygroup/test_cmd.py +2 -1
  71. mcli/self/self_cmd.py +31 -16
  72. mcli/self/test_cmd.py +2 -1
  73. mcli/workflow/dashboard/dashboard_cmd.py +13 -6
  74. mcli/workflow/lsh_integration.py +46 -58
  75. mcli/workflow/politician_trading/commands.py +576 -427
  76. mcli/workflow/politician_trading/config.py +7 -7
  77. mcli/workflow/politician_trading/connectivity.py +35 -33
  78. mcli/workflow/politician_trading/data_sources.py +72 -71
  79. mcli/workflow/politician_trading/database.py +18 -16
  80. mcli/workflow/politician_trading/demo.py +4 -3
  81. mcli/workflow/politician_trading/models.py +5 -5
  82. mcli/workflow/politician_trading/monitoring.py +13 -13
  83. mcli/workflow/politician_trading/scrapers.py +332 -224
  84. mcli/workflow/politician_trading/scrapers_california.py +116 -94
  85. mcli/workflow/politician_trading/scrapers_eu.py +70 -71
  86. mcli/workflow/politician_trading/scrapers_uk.py +118 -90
  87. mcli/workflow/politician_trading/scrapers_us_states.py +125 -92
  88. mcli/workflow/politician_trading/workflow.py +98 -71
  89. {mcli_framework-7.1.1.dist-info → mcli_framework-7.1.2.dist-info}/METADATA +1 -1
  90. {mcli_framework-7.1.1.dist-info → mcli_framework-7.1.2.dist-info}/RECORD +94 -94
  91. {mcli_framework-7.1.1.dist-info → mcli_framework-7.1.2.dist-info}/WHEEL +0 -0
  92. {mcli_framework-7.1.1.dist-info → mcli_framework-7.1.2.dist-info}/entry_points.txt +0 -0
  93. {mcli_framework-7.1.1.dist-info → mcli_framework-7.1.2.dist-info}/licenses/LICENSE +0 -0
  94. {mcli_framework-7.1.1.dist-info → mcli_framework-7.1.2.dist-info}/top_level.txt +0 -0
@@ -1,12 +1,14 @@
1
1
  """Admin API routes"""
2
2
 
3
3
  from fastapi import APIRouter, Depends
4
+
4
5
  from mcli.ml.auth import get_current_active_user, require_role
5
6
  from mcli.ml.database.models import User, UserRole
6
7
 
7
8
  router = APIRouter()
8
9
 
10
+
9
11
  @router.get("/users")
10
12
  async def list_users(current_user: User = Depends(require_role(UserRole.ADMIN))):
11
13
  """List all users"""
12
- return {"users": []}
14
+ return {"users": []}
@@ -3,24 +3,24 @@
3
3
  from datetime import datetime
4
4
  from typing import Optional
5
5
 
6
- from fastapi import APIRouter, Depends, HTTPException, status, Request
7
- from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
6
+ from fastapi import APIRouter, Depends, HTTPException, Request, status
7
+ from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
8
8
  from sqlalchemy.orm import Session
9
9
 
10
10
  from mcli.ml.auth import (
11
11
  AuthManager,
12
+ PasswordChange,
13
+ PasswordReset,
14
+ TokenResponse,
12
15
  UserCreate,
13
16
  UserLogin,
14
- TokenResponse,
15
17
  UserResponse,
16
- PasswordChange,
17
- PasswordReset,
18
- get_current_user,
19
- get_current_active_user,
20
18
  check_rate_limit,
19
+ get_current_active_user,
20
+ get_current_user,
21
21
  )
22
- from mcli.ml.database.session import get_db
23
22
  from mcli.ml.database.models import User
23
+ from mcli.ml.database.session import get_db
24
24
 
25
25
  router = APIRouter()
26
26
  auth_manager = AuthManager()
@@ -29,9 +29,7 @@ security = HTTPBearer()
29
29
 
30
30
  @router.post("/register", response_model=UserResponse)
31
31
  async def register(
32
- user_data: UserCreate,
33
- db: Session = Depends(get_db),
34
- _: bool = Depends(check_rate_limit)
32
+ user_data: UserCreate, db: Session = Depends(get_db), _: bool = Depends(check_rate_limit)
35
33
  ):
36
34
  """Register a new user"""
37
35
  try:
@@ -40,44 +38,32 @@ async def register(
40
38
  except HTTPException:
41
39
  raise
42
40
  except Exception as e:
43
- raise HTTPException(
44
- status_code=status.HTTP_400_BAD_REQUEST,
45
- detail=str(e)
46
- )
41
+ raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))
47
42
 
48
43
 
49
44
  @router.post("/login", response_model=TokenResponse)
50
45
  async def login(
51
- login_data: UserLogin,
52
- db: Session = Depends(get_db),
53
- _: bool = Depends(check_rate_limit)
46
+ login_data: UserLogin, db: Session = Depends(get_db), _: bool = Depends(check_rate_limit)
54
47
  ):
55
48
  """Login and receive access token"""
56
49
  return await auth_manager.login(login_data, db)
57
50
 
58
51
 
59
52
  @router.post("/refresh", response_model=TokenResponse)
60
- async def refresh_token(
61
- refresh_token: str,
62
- db: Session = Depends(get_db)
63
- ):
53
+ async def refresh_token(refresh_token: str, db: Session = Depends(get_db)):
64
54
  """Refresh access token using refresh token"""
65
55
  return await auth_manager.refresh_access_token(refresh_token, db)
66
56
 
67
57
 
68
58
  @router.post("/logout")
69
- async def logout(
70
- current_user: User = Depends(get_current_active_user)
71
- ):
59
+ async def logout(current_user: User = Depends(get_current_active_user)):
72
60
  """Logout current user"""
73
61
  # In a real implementation, you might want to blacklist the token
74
62
  return {"message": "Successfully logged out"}
75
63
 
76
64
 
77
65
  @router.get("/me", response_model=UserResponse)
78
- async def get_current_user_info(
79
- current_user: User = Depends(get_current_active_user)
80
- ):
66
+ async def get_current_user_info(current_user: User = Depends(get_current_active_user)):
81
67
  """Get current user information"""
82
68
  return UserResponse.from_orm(current_user)
83
69
 
@@ -86,7 +72,7 @@ async def get_current_user_info(
86
72
  async def update_current_user(
87
73
  updates: dict,
88
74
  current_user: User = Depends(get_current_active_user),
89
- db: Session = Depends(get_db)
75
+ db: Session = Depends(get_db),
90
76
  ):
91
77
  """Update current user information"""
92
78
  # Update allowed fields
@@ -104,17 +90,13 @@ async def update_current_user(
104
90
  async def change_password(
105
91
  password_data: PasswordChange,
106
92
  current_user: User = Depends(get_current_active_user),
107
- db: Session = Depends(get_db)
93
+ db: Session = Depends(get_db),
108
94
  ):
109
95
  """Change current user's password"""
110
96
  # Verify current password
111
- if not auth_manager.verify_password(
112
- password_data.current_password,
113
- current_user.password_hash
114
- ):
97
+ if not auth_manager.verify_password(password_data.current_password, current_user.password_hash):
115
98
  raise HTTPException(
116
- status_code=status.HTTP_400_BAD_REQUEST,
117
- detail="Current password is incorrect"
99
+ status_code=status.HTTP_400_BAD_REQUEST, detail="Current password is incorrect"
118
100
  )
119
101
 
120
102
  # Update password
@@ -126,9 +108,7 @@ async def change_password(
126
108
 
127
109
  @router.post("/reset-password")
128
110
  async def reset_password_request(
129
- reset_data: PasswordReset,
130
- db: Session = Depends(get_db),
131
- _: bool = Depends(check_rate_limit)
111
+ reset_data: PasswordReset, db: Session = Depends(get_db), _: bool = Depends(check_rate_limit)
132
112
  ):
133
113
  """Request password reset"""
134
114
  # Find user by email
@@ -144,10 +124,7 @@ async def reset_password_request(
144
124
 
145
125
 
146
126
  @router.post("/verify-email/{token}")
147
- async def verify_email(
148
- token: str,
149
- db: Session = Depends(get_db)
150
- ):
127
+ async def verify_email(token: str, db: Session = Depends(get_db)):
151
128
  """Verify email address"""
152
129
  # In a real implementation, verify the token and update user
153
130
  return {"message": "Email verified successfully"}
@@ -155,8 +132,7 @@ async def verify_email(
155
132
 
156
133
  @router.get("/sessions")
157
134
  async def get_user_sessions(
158
- current_user: User = Depends(get_current_active_user),
159
- db: Session = Depends(get_db)
135
+ current_user: User = Depends(get_current_active_user), db: Session = Depends(get_db)
160
136
  ):
161
137
  """Get all active sessions for current user"""
162
138
  # In a real implementation, return active sessions from database
@@ -167,7 +143,7 @@ async def get_user_sessions(
167
143
  "ip_address": "127.0.0.1",
168
144
  "user_agent": "Mozilla/5.0",
169
145
  "created_at": datetime.utcnow().isoformat(),
170
- "last_active": datetime.utcnow().isoformat()
146
+ "last_active": datetime.utcnow().isoformat(),
171
147
  }
172
148
  ]
173
149
  }
@@ -177,7 +153,7 @@ async def get_user_sessions(
177
153
  async def revoke_session(
178
154
  session_id: str,
179
155
  current_user: User = Depends(get_current_active_user),
180
- db: Session = Depends(get_db)
156
+ db: Session = Depends(get_db),
181
157
  ):
182
158
  """Revoke a specific session"""
183
159
  # In a real implementation, revoke the session
@@ -189,7 +165,7 @@ async def create_api_key(
189
165
  name: str,
190
166
  expires_in_days: Optional[int] = None,
191
167
  current_user: User = Depends(get_current_active_user),
192
- db: Session = Depends(get_db)
168
+ db: Session = Depends(get_db),
193
169
  ):
194
170
  """Create a new API key"""
195
171
  import secrets
@@ -209,15 +185,15 @@ async def create_api_key(
209
185
  "name": name,
210
186
  "created_at": datetime.utcnow().isoformat(),
211
187
  "expires_at": (
212
- datetime.utcnow() + timedelta(days=expires_in_days)
213
- ).isoformat() if expires_in_days else None
188
+ (datetime.utcnow() + timedelta(days=expires_in_days)).isoformat()
189
+ if expires_in_days
190
+ else None
191
+ ),
214
192
  }
215
193
 
216
194
 
217
195
  @router.get("/api-keys")
218
- async def list_api_keys(
219
- current_user: User = Depends(get_current_active_user)
220
- ):
196
+ async def list_api_keys(current_user: User = Depends(get_current_active_user)):
221
197
  """List all API keys for current user"""
222
198
  # In a real implementation, return API keys from database
223
199
  return {
@@ -227,7 +203,7 @@ async def list_api_keys(
227
203
  "name": "Production API",
228
204
  "created_at": datetime.utcnow().isoformat(),
229
205
  "last_used": datetime.utcnow().isoformat(),
230
- "expires_at": None
206
+ "expires_at": None,
231
207
  }
232
208
  ]
233
209
  }
@@ -237,8 +213,8 @@ async def list_api_keys(
237
213
  async def revoke_api_key(
238
214
  key_id: str,
239
215
  current_user: User = Depends(get_current_active_user),
240
- db: Session = Depends(get_db)
216
+ db: Session = Depends(get_db),
241
217
  ):
242
218
  """Revoke an API key"""
243
219
  # In a real implementation, revoke the API key
244
- return {"message": f"API key {key_id} revoked successfully"}
220
+ return {"message": f"API key {key_id} revoked successfully"}
@@ -1,12 +1,14 @@
1
1
  """Backtesting API routes"""
2
2
 
3
3
  from fastapi import APIRouter, Depends
4
+
4
5
  from mcli.ml.auth import get_current_active_user
5
6
  from mcli.ml.database.models import User
6
7
 
7
8
  router = APIRouter()
8
9
 
10
+
9
11
  @router.post("/run")
10
12
  async def run_backtest(current_user: User = Depends(get_current_active_user)):
11
13
  """Run backtest"""
12
- return {"status": "started"}
14
+ return {"status": "started"}
@@ -1,12 +1,14 @@
1
1
  """Data API routes"""
2
2
 
3
3
  from fastapi import APIRouter, Depends
4
+
4
5
  from mcli.ml.auth import get_current_active_user
5
6
  from mcli.ml.database.models import User
6
7
 
7
8
  router = APIRouter()
8
9
 
10
+
9
11
  @router.get("/stocks/{ticker}")
10
12
  async def get_stock_data(ticker: str, current_user: User = Depends(get_current_active_user)):
11
13
  """Get stock data"""
12
- return {"ticker": ticker, "data": {}}
14
+ return {"ticker": ticker, "data": {}}
@@ -1,18 +1,18 @@
1
1
  """Model management API routes"""
2
2
 
3
- from typing import List, Optional
4
3
  from datetime import datetime
4
+ from typing import List, Optional
5
5
  from uuid import UUID
6
6
 
7
- from fastapi import APIRouter, Depends, HTTPException, status, UploadFile, File, Query
8
- from sqlalchemy.orm import Session
7
+ from fastapi import APIRouter, Depends, File, HTTPException, Query, UploadFile, status
9
8
  from sqlalchemy import select
9
+ from sqlalchemy.orm import Session
10
10
 
11
- from mcli.ml.auth import get_current_active_user, require_role, Permission
12
- from mcli.ml.database.session import get_db, get_async_db
13
- from mcli.ml.database.models import User, Model, ModelStatus, UserRole
14
- from mcli.ml.api.schemas import ModelCreate, ModelResponse, ModelUpdate, ModelMetrics
11
+ from mcli.ml.api.schemas import ModelCreate, ModelMetrics, ModelResponse, ModelUpdate
12
+ from mcli.ml.auth import Permission, get_current_active_user, require_role
15
13
  from mcli.ml.cache import cached
14
+ from mcli.ml.database.models import Model, ModelStatus, User, UserRole
15
+ from mcli.ml.database.session import get_async_db, get_db
16
16
  from mcli.ml.tasks import train_model_task
17
17
 
18
18
  router = APIRouter()
@@ -25,7 +25,7 @@ async def list_models(
25
25
  limit: int = Query(100, le=1000),
26
26
  status: Optional[ModelStatus] = None,
27
27
  current_user: User = Depends(get_current_active_user),
28
- db: Session = Depends(get_db)
28
+ db: Session = Depends(get_db),
29
29
  ):
30
30
  """List all available models"""
31
31
  query = db.query(Model)
@@ -42,16 +42,13 @@ async def list_models(
42
42
  async def get_model(
43
43
  model_id: UUID,
44
44
  current_user: User = Depends(get_current_active_user),
45
- db: Session = Depends(get_db)
45
+ db: Session = Depends(get_db),
46
46
  ):
47
47
  """Get specific model details"""
48
48
  model = db.query(Model).filter(Model.id == model_id).first()
49
49
 
50
50
  if not model:
51
- raise HTTPException(
52
- status_code=status.HTTP_404_NOT_FOUND,
53
- detail="Model not found"
54
- )
51
+ raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Model not found")
55
52
 
56
53
  return ModelResponse.from_orm(model)
57
54
 
@@ -60,13 +57,11 @@ async def get_model(
60
57
  async def create_model(
61
58
  model_data: ModelCreate,
62
59
  current_user: User = Depends(require_role(UserRole.ANALYST, UserRole.ADMIN)),
63
- db: Session = Depends(get_db)
60
+ db: Session = Depends(get_db),
64
61
  ):
65
62
  """Create a new model"""
66
63
  model = Model(
67
- **model_data.dict(),
68
- created_by=current_user.username,
69
- status=ModelStatus.TRAINING
64
+ **model_data.dict(), created_by=current_user.username, status=ModelStatus.TRAINING
70
65
  )
71
66
 
72
67
  db.add(model)
@@ -84,16 +79,13 @@ async def update_model(
84
79
  model_id: UUID,
85
80
  updates: ModelUpdate,
86
81
  current_user: User = Depends(require_role(UserRole.ANALYST, UserRole.ADMIN)),
87
- db: Session = Depends(get_db)
82
+ db: Session = Depends(get_db),
88
83
  ):
89
84
  """Update model metadata"""
90
85
  model = db.query(Model).filter(Model.id == model_id).first()
91
86
 
92
87
  if not model:
93
- raise HTTPException(
94
- status_code=status.HTTP_404_NOT_FOUND,
95
- detail="Model not found"
96
- )
88
+ raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Model not found")
97
89
 
98
90
  for field, value in updates.dict(exclude_unset=True).items():
99
91
  setattr(model, field, value)
@@ -110,21 +102,18 @@ async def deploy_model(
110
102
  model_id: UUID,
111
103
  endpoint: Optional[str] = None,
112
104
  current_user: User = Depends(require_role(UserRole.ANALYST, UserRole.ADMIN)),
113
- db: Session = Depends(get_db)
105
+ db: Session = Depends(get_db),
114
106
  ):
115
107
  """Deploy model to production"""
116
108
  model = db.query(Model).filter(Model.id == model_id).first()
117
109
 
118
110
  if not model:
119
- raise HTTPException(
120
- status_code=status.HTTP_404_NOT_FOUND,
121
- detail="Model not found"
122
- )
111
+ raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Model not found")
123
112
 
124
113
  if model.status != ModelStatus.TRAINED:
125
114
  raise HTTPException(
126
115
  status_code=status.HTTP_400_BAD_REQUEST,
127
- detail="Model must be trained before deployment"
116
+ detail="Model must be trained before deployment",
128
117
  )
129
118
 
130
119
  # Deploy model (in real implementation, this would deploy to serving infrastructure)
@@ -134,26 +123,20 @@ async def deploy_model(
134
123
 
135
124
  db.commit()
136
125
 
137
- return {
138
- "message": "Model deployed successfully",
139
- "endpoint": model.deployment_endpoint
140
- }
126
+ return {"message": "Model deployed successfully", "endpoint": model.deployment_endpoint}
141
127
 
142
128
 
143
129
  @router.post("/{model_id}/archive")
144
130
  async def archive_model(
145
131
  model_id: UUID,
146
132
  current_user: User = Depends(require_role(UserRole.ADMIN)),
147
- db: Session = Depends(get_db)
133
+ db: Session = Depends(get_db),
148
134
  ):
149
135
  """Archive a model"""
150
136
  model = db.query(Model).filter(Model.id == model_id).first()
151
137
 
152
138
  if not model:
153
- raise HTTPException(
154
- status_code=status.HTTP_404_NOT_FOUND,
155
- detail="Model not found"
156
- )
139
+ raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Model not found")
157
140
 
158
141
  model.status = ModelStatus.ARCHIVED
159
142
  db.commit()
@@ -166,16 +149,13 @@ async def archive_model(
166
149
  async def get_model_metrics(
167
150
  model_id: UUID,
168
151
  current_user: User = Depends(get_current_active_user),
169
- db: Session = Depends(get_db)
152
+ db: Session = Depends(get_db),
170
153
  ):
171
154
  """Get model performance metrics"""
172
155
  model = db.query(Model).filter(Model.id == model_id).first()
173
156
 
174
157
  if not model:
175
- raise HTTPException(
176
- status_code=status.HTTP_404_NOT_FOUND,
177
- detail="Model not found"
178
- )
158
+ raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Model not found")
179
159
 
180
160
  return ModelMetrics(
181
161
  model_id=model.id,
@@ -185,7 +165,7 @@ async def get_model_metrics(
185
165
  train_loss=model.train_loss,
186
166
  val_loss=model.val_loss,
187
167
  test_loss=model.test_loss,
188
- additional_metrics=model.metrics or {}
168
+ additional_metrics=model.metrics or {},
189
169
  )
190
170
 
191
171
 
@@ -194,16 +174,13 @@ async def retrain_model(
194
174
  model_id: UUID,
195
175
  hyperparameters: Optional[dict] = None,
196
176
  current_user: User = Depends(require_role(UserRole.ANALYST, UserRole.ADMIN)),
197
- db: Session = Depends(get_db)
177
+ db: Session = Depends(get_db),
198
178
  ):
199
179
  """Retrain an existing model"""
200
180
  model = db.query(Model).filter(Model.id == model_id).first()
201
181
 
202
182
  if not model:
203
- raise HTTPException(
204
- status_code=status.HTTP_404_NOT_FOUND,
205
- detail="Model not found"
206
- )
183
+ raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Model not found")
207
184
 
208
185
  # Update hyperparameters if provided
209
186
  if hyperparameters:
@@ -223,16 +200,13 @@ async def upload_model_artifact(
223
200
  model_id: UUID,
224
201
  file: UploadFile = File(...),
225
202
  current_user: User = Depends(require_role(UserRole.ANALYST, UserRole.ADMIN)),
226
- db: Session = Depends(get_db)
203
+ db: Session = Depends(get_db),
227
204
  ):
228
205
  """Upload model artifact file"""
229
206
  model = db.query(Model).filter(Model.id == model_id).first()
230
207
 
231
208
  if not model:
232
- raise HTTPException(
233
- status_code=status.HTTP_404_NOT_FOUND,
234
- detail="Model not found"
235
- )
209
+ raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Model not found")
236
210
 
237
211
  # Save file (in real implementation, save to S3 or similar)
238
212
  file_path = f"/models/{model_id}/{file.filename}"
@@ -241,31 +215,25 @@ async def upload_model_artifact(
241
215
  model.model_path = file_path
242
216
  db.commit()
243
217
 
244
- return {
245
- "message": "Model artifact uploaded successfully",
246
- "path": file_path
247
- }
218
+ return {"message": "Model artifact uploaded successfully", "path": file_path}
248
219
 
249
220
 
250
221
  @router.delete("/{model_id}")
251
222
  async def delete_model(
252
223
  model_id: UUID,
253
224
  current_user: User = Depends(require_role(UserRole.ADMIN)),
254
- db: Session = Depends(get_db)
225
+ db: Session = Depends(get_db),
255
226
  ):
256
227
  """Delete a model"""
257
228
  model = db.query(Model).filter(Model.id == model_id).first()
258
229
 
259
230
  if not model:
260
- raise HTTPException(
261
- status_code=status.HTTP_404_NOT_FOUND,
262
- detail="Model not found"
263
- )
231
+ raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Model not found")
264
232
 
265
233
  if model.status == ModelStatus.DEPLOYED:
266
234
  raise HTTPException(
267
235
  status_code=status.HTTP_400_BAD_REQUEST,
268
- detail="Cannot delete deployed model. Archive it first."
236
+ detail="Cannot delete deployed model. Archive it first.",
269
237
  )
270
238
 
271
239
  db.delete(model)
@@ -278,25 +246,18 @@ async def delete_model(
278
246
  async def download_model(
279
247
  model_id: UUID,
280
248
  current_user: User = Depends(get_current_active_user),
281
- db: Session = Depends(get_db)
249
+ db: Session = Depends(get_db),
282
250
  ):
283
251
  """Download model artifact"""
284
252
  model = db.query(Model).filter(Model.id == model_id).first()
285
253
 
286
254
  if not model:
287
- raise HTTPException(
288
- status_code=status.HTTP_404_NOT_FOUND,
289
- detail="Model not found"
290
- )
255
+ raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Model not found")
291
256
 
292
257
  if not model.model_path:
293
258
  raise HTTPException(
294
- status_code=status.HTTP_404_NOT_FOUND,
295
- detail="Model artifact not available"
259
+ status_code=status.HTTP_404_NOT_FOUND, detail="Model artifact not available"
296
260
  )
297
261
 
298
262
  # In real implementation, return file from storage
299
- return {
300
- "download_url": f"https://storage.mcli-ml.com{model.model_path}",
301
- "expires_in": 3600
302
- }
263
+ return {"download_url": f"https://storage.mcli-ml.com{model.model_path}", "expires_in": 3600}
@@ -1,12 +1,14 @@
1
1
  """Monitoring API routes"""
2
2
 
3
3
  from fastapi import APIRouter, Depends
4
+
4
5
  from mcli.ml.auth import get_current_active_user
5
6
  from mcli.ml.database.models import User
6
7
 
7
8
  router = APIRouter()
8
9
 
10
+
9
11
  @router.get("/drift")
10
12
  async def get_drift_status(current_user: User = Depends(get_current_active_user)):
11
13
  """Get drift monitoring status"""
12
- return {"drift_detected": False}
14
+ return {"drift_detected": False}
@@ -1,12 +1,14 @@
1
1
  """Portfolio management API routes"""
2
2
 
3
3
  from fastapi import APIRouter, Depends
4
+
4
5
  from mcli.ml.auth import get_current_active_user
5
6
  from mcli.ml.database.models import User
6
7
 
7
8
  router = APIRouter()
8
9
 
10
+
9
11
  @router.get("/")
10
12
  async def list_portfolios(current_user: User = Depends(get_current_active_user)):
11
13
  """List user portfolios"""
12
- return {"portfolios": []}
14
+ return {"portfolios": []}