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,4 +1,4 @@
1
- """Real-time stream processing for financial data"""
1
+ """Real-time stream processing for financial data."""
2
2
 
3
3
  import asyncio
4
4
  import json
@@ -7,21 +7,20 @@ import time
7
7
  from abc import ABC, abstractmethod
8
8
  from collections import deque
9
9
  from dataclasses import dataclass
10
- from datetime import datetime, timedelta
10
+ from datetime import datetime
11
11
  from typing import Any, AsyncIterator, Callable, Dict, List, Optional
12
12
 
13
13
  import numpy as np
14
- import pandas as pd
15
14
  import websockets
16
- from kafka import KafkaConsumer, KafkaProducer
17
- from kafka.errors import KafkaError
15
+
16
+ # KafkaConsumer from kafka library not needed - we have our own implementation below
18
17
 
19
18
  logger = logging.getLogger(__name__)
20
19
 
21
20
 
22
21
  @dataclass
23
22
  class StreamConfig:
24
- """Stream processing configuration"""
23
+ """Stream processing configuration."""
25
24
 
26
25
  buffer_size: int = 1000
27
26
  batch_size: int = 100
@@ -33,7 +32,7 @@ class StreamConfig:
33
32
 
34
33
 
35
34
  class DataStream(ABC):
36
- """Base class for data streams"""
35
+ """Base class for data streams."""
37
36
 
38
37
  def __init__(self, config: StreamConfig):
39
38
  self.config = config
@@ -44,20 +43,18 @@ class DataStream(ABC):
44
43
 
45
44
  @abstractmethod
46
45
  async def connect(self):
47
- """Connect to data source"""
48
- pass
46
+ """Connect to data source."""
49
47
 
50
48
  @abstractmethod
51
49
  async def consume(self) -> AsyncIterator[Dict[str, Any]]:
52
- """Consume data from stream"""
53
- pass
50
+ """Consume data from stream."""
54
51
 
55
52
  def add_handler(self, handler: Callable):
56
- """Add data handler"""
53
+ """Add data handler."""
57
54
  self.handlers.append(handler)
58
55
 
59
56
  async def process_message(self, message: Dict[str, Any]):
60
- """Process single message"""
57
+ """Process single message."""
61
58
  # Add to buffer
62
59
  self.buffer.append(message)
63
60
 
@@ -70,7 +67,7 @@ class DataStream(ABC):
70
67
  await self.flush_buffer()
71
68
 
72
69
  async def flush_buffer(self):
73
- """Flush buffer and process batch"""
70
+ """Flush buffer and process batch."""
74
71
  if not self.buffer:
75
72
  return
76
73
 
@@ -89,7 +86,7 @@ class DataStream(ABC):
89
86
  logger.error(f"Handler error: {e}")
90
87
 
91
88
  async def start(self):
92
- """Start consuming stream"""
89
+ """Start consuming stream."""
93
90
  self.is_running = True
94
91
  await self.connect()
95
92
 
@@ -102,13 +99,13 @@ class DataStream(ABC):
102
99
  await self.flush_buffer()
103
100
 
104
101
  async def stop(self):
105
- """Stop consuming stream"""
102
+ """Stop consuming stream."""
106
103
  self.is_running = False
107
104
  await self.flush_buffer()
108
105
 
109
106
 
110
107
  class KafkaStream(DataStream):
111
- """Kafka stream consumer"""
108
+ """Kafka stream consumer."""
112
109
 
113
110
  def __init__(
114
111
  self,
@@ -124,7 +121,7 @@ class KafkaStream(DataStream):
124
121
  self.consumer = None
125
122
 
126
123
  async def connect(self):
127
- """Connect to Kafka"""
124
+ """Connect to Kafka."""
128
125
  self.consumer = KafkaConsumer(
129
126
  self.topic,
130
127
  bootstrap_servers=self.bootstrap_servers,
@@ -136,20 +133,20 @@ class KafkaStream(DataStream):
136
133
  logger.info(f"Connected to Kafka topic: {self.topic}")
137
134
 
138
135
  async def consume(self) -> AsyncIterator[Dict[str, Any]]:
139
- """Consume from Kafka"""
136
+ """Consume from Kafka."""
140
137
  loop = asyncio.get_event_loop()
141
138
 
142
139
  while self.is_running:
143
140
  # Poll messages
144
141
  messages = await loop.run_in_executor(None, self.consumer.poll, 1000) # timeout ms
145
142
 
146
- for topic_partition, records in messages.items():
143
+ for _topic_partition, records in messages.items():
147
144
  for record in records:
148
145
  yield record.value
149
146
 
150
147
 
151
148
  class WebSocketStream(DataStream):
152
- """WebSocket stream consumer"""
149
+ """WebSocket stream consumer."""
153
150
 
154
151
  def __init__(self, config: StreamConfig, url: str):
155
152
  super().__init__(config)
@@ -157,12 +154,12 @@ class WebSocketStream(DataStream):
157
154
  self.websocket = None
158
155
 
159
156
  async def connect(self):
160
- """Connect to WebSocket"""
157
+ """Connect to WebSocket."""
161
158
  self.websocket = await websockets.connect(self.url)
162
159
  logger.info(f"Connected to WebSocket: {self.url}")
163
160
 
164
161
  async def consume(self) -> AsyncIterator[Dict[str, Any]]:
165
- """Consume from WebSocket"""
162
+ """Consume from WebSocket."""
166
163
  async for message in self.websocket:
167
164
  try:
168
165
  data = json.loads(message)
@@ -172,7 +169,7 @@ class WebSocketStream(DataStream):
172
169
 
173
170
 
174
171
  class StreamProcessor:
175
- """Process real-time data streams"""
172
+ """Process real-time data streams."""
176
173
 
177
174
  def __init__(self, config: StreamConfig):
178
175
  self.config = config
@@ -181,7 +178,7 @@ class StreamProcessor:
181
178
  self.metrics = StreamMetrics()
182
179
 
183
180
  def add_stream(self, name: str, stream: DataStream):
184
- """Add data stream"""
181
+ """Add data stream."""
185
182
  self.streams[name] = stream
186
183
 
187
184
  # Add metrics handler
@@ -192,7 +189,7 @@ class StreamProcessor:
192
189
  stream.add_handler(processor)
193
190
 
194
191
  def add_processor(self, processor: Callable):
195
- """Add data processor"""
192
+ """Add data processor."""
196
193
  self.processors.append(processor)
197
194
 
198
195
  # Add to existing streams
@@ -200,7 +197,7 @@ class StreamProcessor:
200
197
  stream.add_handler(processor)
201
198
 
202
199
  async def update_metrics(self, batch: List[Dict[str, Any]]):
203
- """Update stream metrics"""
200
+ """Update stream metrics."""
204
201
  self.metrics.messages_processed += len(batch)
205
202
  self.metrics.last_update = datetime.now()
206
203
 
@@ -214,7 +211,7 @@ class StreamProcessor:
214
211
  self.metrics.throughput = self.metrics.messages_processed / elapsed
215
212
 
216
213
  async def start(self):
217
- """Start all streams"""
214
+ """Start all streams."""
218
215
  tasks = []
219
216
  for name, stream in self.streams.items():
220
217
  logger.info(f"Starting stream: {name}")
@@ -224,13 +221,13 @@ class StreamProcessor:
224
221
  await asyncio.gather(*tasks)
225
222
 
226
223
  async def stop(self):
227
- """Stop all streams"""
224
+ """Stop all streams."""
228
225
  for name, stream in self.streams.items():
229
226
  logger.info(f"Stopping stream: {name}")
230
227
  await stream.stop()
231
228
 
232
229
  def get_metrics(self) -> Dict[str, Any]:
233
- """Get stream metrics"""
230
+ """Get stream metrics."""
234
231
  return {
235
232
  "messages_processed": self.metrics.messages_processed,
236
233
  "throughput": self.metrics.throughput,
@@ -244,7 +241,7 @@ class StreamProcessor:
244
241
 
245
242
  @dataclass
246
243
  class StreamMetrics:
247
- """Stream processing metrics"""
244
+ """Stream processing metrics."""
248
245
 
249
246
  messages_processed: int = 0
250
247
  throughput: float = 0 # messages per second
@@ -254,7 +251,7 @@ class StreamMetrics:
254
251
 
255
252
 
256
253
  class DataAggregator:
257
- """Aggregate data from multiple streams"""
254
+ """Aggregate data from multiple streams."""
258
255
 
259
256
  def __init__(self, window_size: int = 60):
260
257
  self.window_size = window_size
@@ -263,7 +260,7 @@ class DataAggregator:
263
260
  self.last_aggregation = time.time()
264
261
 
265
262
  async def process_batch(self, batch: List[Dict[str, Any]]):
266
- """Process batch of messages"""
263
+ """Process batch of messages."""
267
264
  for message in batch:
268
265
  # Extract key fields
269
266
  symbol = message.get("symbol") or message.get("ticker")
@@ -280,7 +277,7 @@ class DataAggregator:
280
277
  await self.aggregate()
281
278
 
282
279
  async def aggregate(self):
283
- """Aggregate buffered data"""
280
+ """Aggregate buffered data."""
284
281
  self.last_aggregation = time.time()
285
282
 
286
283
  for symbol, data_points in self.data_buffer.items():
@@ -318,21 +315,21 @@ class DataAggregator:
318
315
  logger.info(f"Aggregated data for {len(self.aggregated_data)} symbols")
319
316
 
320
317
  def get_aggregated_data(self, symbol: Optional[str] = None) -> Dict[str, Any]:
321
- """Get aggregated data"""
318
+ """Get aggregated data."""
322
319
  if symbol:
323
320
  return self.aggregated_data.get(symbol, {})
324
321
  return self.aggregated_data
325
322
 
326
323
 
327
324
  class StreamEnricher:
328
- """Enrich streaming data with additional context"""
325
+ """Enrich streaming data with additional context."""
329
326
 
330
327
  def __init__(self):
331
328
  self.enrichment_cache = {}
332
329
  self.cache_ttl = 300 # 5 minutes
333
330
 
334
331
  async def enrich_batch(self, batch: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
335
- """Enrich batch of messages"""
332
+ """Enrich batch of messages."""
336
333
  enriched = []
337
334
 
338
335
  for message in batch:
@@ -342,7 +339,7 @@ class StreamEnricher:
342
339
  return enriched
343
340
 
344
341
  async def enrich_message(self, message: Dict[str, Any]) -> Dict[str, Any]:
345
- """Enrich single message"""
342
+ """Enrich single message."""
346
343
  enriched = message.copy()
347
344
 
348
345
  # Add processing metadata
@@ -359,7 +356,7 @@ class StreamEnricher:
359
356
  return enriched
360
357
 
361
358
  async def enrich_political_data(self, message: Dict[str, Any]) -> Dict[str, Any]:
362
- """Enrich political trading data"""
359
+ """Enrich political trading data."""
363
360
  politician = message.get("politician")
364
361
 
365
362
  if politician:
@@ -388,7 +385,7 @@ class StreamEnricher:
388
385
  return message
389
386
 
390
387
  async def enrich_market_data(self, message: Dict[str, Any]) -> Dict[str, Any]:
391
- """Enrich market data"""
388
+ """Enrich market data."""
392
389
  symbol = message.get("ticker") or message.get("symbol")
393
390
 
394
391
  if symbol:
@@ -418,7 +415,7 @@ class StreamEnricher:
418
415
 
419
416
 
420
417
  class KafkaConsumer:
421
- """Kafka consumer for real-time data"""
418
+ """Kafka consumer for real-time data."""
422
419
 
423
420
  def __init__(self, bootstrap_servers: str, topics: List[str]):
424
421
  self.bootstrap_servers = bootstrap_servers
@@ -426,7 +423,7 @@ class KafkaConsumer:
426
423
  self.consumer = None
427
424
 
428
425
  async def connect(self):
429
- """Connect to Kafka"""
426
+ """Connect to Kafka."""
430
427
  self.consumer = KafkaConsumer(
431
428
  *self.topics,
432
429
  bootstrap_servers=self.bootstrap_servers,
@@ -435,7 +432,7 @@ class KafkaConsumer:
435
432
  )
436
433
 
437
434
  async def consume(self, handler: Callable):
438
- """Consume messages"""
435
+ """Consume messages."""
439
436
  for message in self.consumer:
440
437
  try:
441
438
  await handler(message.value)
@@ -444,18 +441,18 @@ class KafkaConsumer:
444
441
 
445
442
 
446
443
  class WebSocketConsumer:
447
- """WebSocket consumer for real-time data"""
444
+ """WebSocket consumer for real-time data."""
448
445
 
449
446
  def __init__(self, url: str):
450
447
  self.url = url
451
448
  self.websocket = None
452
449
 
453
450
  async def connect(self):
454
- """Connect to WebSocket"""
451
+ """Connect to WebSocket."""
455
452
  self.websocket = await websockets.connect(self.url)
456
453
 
457
454
  async def consume(self, handler: Callable):
458
- """Consume messages"""
455
+ """Consume messages."""
459
456
  async for message in self.websocket:
460
457
  try:
461
458
  data = json.loads(message)
@@ -1,4 +1,4 @@
1
- """Database models and utilities"""
1
+ """Database models and utilities."""
2
2
 
3
3
  from .models import (
4
4
  Alert,
@@ -1,4 +1,4 @@
1
- """Alembic environment configuration"""
1
+ """Alembic environment configuration."""
2
2
 
3
3
  import os
4
4
  import sys
@@ -27,7 +27,7 @@ target_metadata = Base.metadata
27
27
 
28
28
  # Override database URL from settings
29
29
  def get_url():
30
- """Get database URL from settings or environment"""
30
+ """Get database URL from settings or environment."""
31
31
  # First try environment variable
32
32
  url = os.getenv("DATABASE_URL")
33
33
  if url:
@@ -1,8 +1,7 @@
1
- """Database models for ML system"""
1
+ """Database models for ML system."""
2
2
 
3
3
  from datetime import datetime
4
4
  from enum import Enum as PyEnum
5
- from typing import Any, Dict, List, Optional
6
5
  from uuid import uuid4
7
6
 
8
7
  from sqlalchemy import (
@@ -26,7 +25,6 @@ from sqlalchemy import (
26
25
  from sqlalchemy.dialects.postgresql import ARRAY, UUID
27
26
  from sqlalchemy.ext.hybrid import hybrid_property
28
27
  from sqlalchemy.orm import declarative_base, relationship, validates
29
- from sqlalchemy.sql import func
30
28
 
31
29
  Base = declarative_base()
32
30
 
@@ -52,7 +50,7 @@ experiment_models = Table(
52
50
 
53
51
 
54
52
  class UserRole(PyEnum):
55
- """User role enumeration"""
53
+ """User role enumeration."""
56
54
 
57
55
  ADMIN = "admin"
58
56
  USER = "user"
@@ -61,7 +59,7 @@ class UserRole(PyEnum):
61
59
 
62
60
 
63
61
  class ModelStatus(PyEnum):
64
- """Model status enumeration"""
62
+ """Model status enumeration."""
65
63
 
66
64
  TRAINING = "training"
67
65
  TRAINED = "trained"
@@ -71,7 +69,7 @@ class ModelStatus(PyEnum):
71
69
 
72
70
 
73
71
  class TradeType(PyEnum):
74
- """Trade type enumeration"""
72
+ """Trade type enumeration."""
75
73
 
76
74
  BUY = "buy"
77
75
  SELL = "sell"
@@ -79,7 +77,7 @@ class TradeType(PyEnum):
79
77
 
80
78
 
81
79
  class AlertType(PyEnum):
82
- """Alert type enumeration"""
80
+ """Alert type enumeration."""
83
81
 
84
82
  POLITICIAN_TRADE = "politician_trade"
85
83
  PRICE_MOVEMENT = "price_movement"
@@ -89,7 +87,7 @@ class AlertType(PyEnum):
89
87
 
90
88
 
91
89
  class User(Base):
92
- """User model for authentication and authorization"""
90
+ """User model for authentication and authorization."""
93
91
 
94
92
  __tablename__ = "users"
95
93
 
@@ -127,7 +125,7 @@ class User(Base):
127
125
 
128
126
  @validates("email")
129
127
  def validate_email(self, key, email):
130
- """Validate email format"""
128
+ """Validate email format."""
131
129
  import re
132
130
 
133
131
  if not re.match(r"^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}$", email):
@@ -136,7 +134,7 @@ class User(Base):
136
134
 
137
135
 
138
136
  class Politician(Base):
139
- """Politician information"""
137
+ """Politician information."""
140
138
 
141
139
  __tablename__ = "politicians"
142
140
 
@@ -169,7 +167,7 @@ class Politician(Base):
169
167
 
170
168
 
171
169
  class Trade(Base):
172
- """Political trading records"""
170
+ """Political trading records."""
173
171
 
174
172
  __tablename__ = "trades"
175
173
 
@@ -206,14 +204,14 @@ class Trade(Base):
206
204
 
207
205
  @hybrid_property
208
206
  def estimated_amount(self):
209
- """Get estimated trade amount (midpoint)"""
207
+ """Get estimated trade amount (midpoint)."""
210
208
  if self.amount_min and self.amount_max:
211
209
  return (self.amount_min + self.amount_max) / 2
212
210
  return self.amount_min or self.amount_max
213
211
 
214
212
 
215
213
  class StockData(Base):
216
- """Stock market data"""
214
+ """Stock market data."""
217
215
 
218
216
  __tablename__ = "stock_data"
219
217
 
@@ -264,7 +262,7 @@ class StockData(Base):
264
262
 
265
263
 
266
264
  class Prediction(Base):
267
- """Model predictions"""
265
+ """Model predictions."""
268
266
 
269
267
  __tablename__ = "predictions"
270
268
 
@@ -313,7 +311,7 @@ class Prediction(Base):
313
311
 
314
312
 
315
313
  class Portfolio(Base):
316
- """User portfolios"""
314
+ """User portfolios."""
317
315
 
318
316
  __tablename__ = "portfolios"
319
317
 
@@ -358,7 +356,7 @@ class Portfolio(Base):
358
356
 
359
357
 
360
358
  class Alert(Base):
361
- """User alerts and notifications"""
359
+ """User alerts and notifications."""
362
360
 
363
361
  __tablename__ = "alerts"
364
362
 
@@ -396,7 +394,7 @@ class Alert(Base):
396
394
 
397
395
 
398
396
  class BacktestResult(Base):
399
- """Backtesting results"""
397
+ """Backtesting results."""
400
398
 
401
399
  __tablename__ = "backtest_results"
402
400
 
@@ -448,7 +446,7 @@ class BacktestResult(Base):
448
446
 
449
447
 
450
448
  class Experiment(Base):
451
- """ML experiments tracking"""
449
+ """ML experiments tracking."""
452
450
 
453
451
  __tablename__ = "experiments"
454
452
 
@@ -496,7 +494,7 @@ class Experiment(Base):
496
494
 
497
495
 
498
496
  class Model(Base):
499
- """ML models registry"""
497
+ """ML models registry."""
500
498
 
501
499
  __tablename__ = "models"
502
500
 
@@ -578,7 +576,7 @@ class Model(Base):
578
576
 
579
577
 
580
578
  class FeatureSet(Base):
581
- """Feature sets for models"""
579
+ """Feature sets for models."""
582
580
 
583
581
  __tablename__ = "feature_sets"
584
582
 
@@ -617,7 +615,7 @@ class FeatureSet(Base):
617
615
 
618
616
 
619
617
  class DataVersion(Base):
620
- """Data versioning for DVC"""
618
+ """Data versioning for DVC."""
621
619
 
622
620
  __tablename__ = "data_versions"
623
621
 
@@ -658,7 +656,7 @@ class DataVersion(Base):
658
656
  # Database events and triggers
659
657
  @event.listens_for(User, "before_insert")
660
658
  def generate_api_key(mapper, connection, target):
661
- """Generate API key for new users"""
659
+ """Generate API key for new users."""
662
660
  if not target.api_key:
663
661
  import secrets
664
662
 
@@ -668,7 +666,7 @@ def generate_api_key(mapper, connection, target):
668
666
 
669
667
  @event.listens_for(Portfolio, "before_update")
670
668
  def update_portfolio_metrics(mapper, connection, target):
671
- """Update portfolio performance metrics"""
669
+ """Update portfolio performance metrics."""
672
670
  if target.current_value and target.initial_capital:
673
671
  target.total_return = (
674
672
  (target.current_value - target.initial_capital) / target.initial_capital
@@ -677,7 +675,7 @@ def update_portfolio_metrics(mapper, connection, target):
677
675
 
678
676
  # Create indexes for better query performance
679
677
  def create_indexes(engine):
680
- """Create additional database indexes"""
678
+ """Create additional database indexes."""
681
679
  from sqlalchemy import text
682
680
 
683
681
  indexes = [
@@ -1,4 +1,4 @@
1
- """Database session management"""
1
+ """Database session management."""
2
2
 
3
3
  # Synchronous database setup
4
4
  # Prioritize DATABASE_URL environment variable over settings
@@ -9,7 +9,7 @@ from typing import AsyncGenerator, Generator
9
9
  from sqlalchemy import create_engine
10
10
  from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker, create_async_engine
11
11
  from sqlalchemy.orm import Session, sessionmaker
12
- from sqlalchemy.pool import NullPool, StaticPool
12
+ from sqlalchemy.pool import StaticPool
13
13
 
14
14
  from mcli.ml.config import settings
15
15
 
@@ -28,7 +28,7 @@ if not database_url:
28
28
  settings_url = settings.database.url
29
29
  if settings_url and "sqlite" not in settings_url:
30
30
  database_url = settings_url
31
- except (AttributeError, Exception):
31
+ except Exception:
32
32
  pass # Continue with database_url=None
33
33
 
34
34
  # If still no valid DATABASE_URL, try to use Supabase REST API via connection pooler
@@ -103,7 +103,7 @@ import logging
103
103
  logger = logging.getLogger(__name__)
104
104
 
105
105
  if "pooler.supabase.com" in database_url:
106
- logger.info(f"🔗 Using Supabase connection pooler")
106
+ logger.info("🔗 Using Supabase connection pooler")
107
107
  elif "sqlite" in database_url:
108
108
  logger.warning("📁 Using SQLite fallback (database features limited)")
109
109
  else:
@@ -157,7 +157,7 @@ try:
157
157
  pool_pre_ping=True,
158
158
  echo=settings.debug,
159
159
  )
160
- except (AttributeError, Exception):
160
+ except Exception:
161
161
  # Fallback for async engine
162
162
  import os
163
163
 
@@ -284,7 +284,7 @@ async def get_async_session() -> AsyncGenerator[AsyncSession, None]:
284
284
 
285
285
 
286
286
  def init_db() -> None:
287
- """Initialize database tables"""
287
+ """Initialize database tables."""
288
288
  Base.metadata.create_all(bind=engine)
289
289
 
290
290
  # Create additional indexes
@@ -294,25 +294,25 @@ def init_db() -> None:
294
294
 
295
295
 
296
296
  async def init_async_db() -> None:
297
- """Initialize database tables asynchronously"""
297
+ """Initialize database tables asynchronously."""
298
298
  async with async_engine.begin() as conn:
299
299
  await conn.run_sync(Base.metadata.create_all)
300
300
 
301
301
 
302
302
  def drop_db() -> None:
303
- """Drop all database tables (use with caution!)"""
303
+ """Drop all database tables (use with caution!)."""
304
304
  Base.metadata.drop_all(bind=engine)
305
305
 
306
306
 
307
307
  async def drop_async_db() -> None:
308
- """Drop all database tables asynchronously"""
308
+ """Drop all database tables asynchronously."""
309
309
  async with async_engine.begin() as conn:
310
310
  await conn.run_sync(Base.metadata.drop_all)
311
311
 
312
312
 
313
313
  # Test database setup (for unit tests)
314
314
  def get_test_engine():
315
- """Create test database engine with in-memory SQLite"""
315
+ """Create test database engine with in-memory SQLite."""
316
316
  from sqlalchemy import create_engine
317
317
 
318
318
  engine = create_engine(
@@ -326,7 +326,7 @@ def get_test_engine():
326
326
 
327
327
 
328
328
  def get_test_session():
329
- """Create test database session"""
329
+ """Create test database session."""
330
330
  engine = get_test_engine()
331
331
  TestSessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
332
332
  return TestSessionLocal()
@@ -334,7 +334,7 @@ def get_test_session():
334
334
 
335
335
  # Database health check
336
336
  async def check_database_health() -> bool:
337
- """Check if database is accessible and healthy"""
337
+ """Check if database is accessible and healthy."""
338
338
  try:
339
339
  async with get_async_session() as session:
340
340
  await session.execute("SELECT 1")
@@ -346,14 +346,14 @@ async def check_database_health() -> bool:
346
346
 
347
347
  # Utility functions for bulk operations
348
348
  async def bulk_insert(model_class, data: list) -> None:
349
- """Bulk insert data into database"""
349
+ """Bulk insert data into database."""
350
350
  async with get_async_session() as session:
351
351
  session.add_all([model_class(**item) for item in data])
352
352
  await session.commit()
353
353
 
354
354
 
355
355
  async def bulk_update(model_class, data: list, key_field: str = "id") -> None:
356
- """Bulk update data in database"""
356
+ """Bulk update data in database."""
357
357
  async with get_async_session() as session:
358
358
  for item in data:
359
359
  key_value = item.pop(key_field)
@@ -1,4 +1,4 @@
1
- """ML Experimentation and A/B Testing Framework"""
1
+ """ML Experimentation and A/B Testing Framework."""
2
2
 
3
3
  from .ab_testing import (
4
4
  ABTestingFramework,