mcli-framework 7.10.0__py3-none-any.whl → 7.10.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.
- mcli/lib/custom_commands.py +10 -0
- mcli/lib/optional_deps.py +240 -0
- mcli/ml/backtesting/run.py +5 -3
- mcli/ml/models/ensemble_models.py +1 -0
- mcli/ml/models/recommendation_models.py +1 -0
- mcli/ml/optimization/optimize.py +6 -4
- mcli/ml/serving/serve.py +2 -2
- mcli/ml/training/train.py +14 -7
- mcli/self/completion_cmd.py +2 -2
- mcli/workflow/doc_convert.py +82 -112
- mcli/workflow/git_commit/ai_service.py +13 -2
- mcli/workflow/notebook/converter.py +375 -0
- mcli/workflow/notebook/notebook_cmd.py +441 -0
- mcli/workflow/notebook/schema.py +402 -0
- mcli/workflow/notebook/validator.py +313 -0
- mcli/workflow/workflow.py +14 -0
- {mcli_framework-7.10.0.dist-info → mcli_framework-7.10.2.dist-info}/METADATA +37 -3
- {mcli_framework-7.10.0.dist-info → mcli_framework-7.10.2.dist-info}/RECORD +22 -37
- mcli/ml/features/political_features.py +0 -677
- mcli/ml/preprocessing/politician_trading_preprocessor.py +0 -570
- mcli/workflow/politician_trading/config.py +0 -134
- mcli/workflow/politician_trading/connectivity.py +0 -492
- mcli/workflow/politician_trading/data_sources.py +0 -654
- mcli/workflow/politician_trading/database.py +0 -412
- mcli/workflow/politician_trading/demo.py +0 -249
- mcli/workflow/politician_trading/models.py +0 -327
- mcli/workflow/politician_trading/monitoring.py +0 -413
- mcli/workflow/politician_trading/scrapers.py +0 -1074
- mcli/workflow/politician_trading/scrapers_california.py +0 -434
- mcli/workflow/politician_trading/scrapers_corporate_registry.py +0 -797
- mcli/workflow/politician_trading/scrapers_eu.py +0 -376
- mcli/workflow/politician_trading/scrapers_free_sources.py +0 -509
- mcli/workflow/politician_trading/scrapers_third_party.py +0 -373
- mcli/workflow/politician_trading/scrapers_uk.py +0 -378
- mcli/workflow/politician_trading/scrapers_us_states.py +0 -471
- mcli/workflow/politician_trading/seed_database.py +0 -520
- mcli/workflow/politician_trading/supabase_functions.py +0 -354
- mcli/workflow/politician_trading/workflow.py +0 -879
- {mcli_framework-7.10.0.dist-info → mcli_framework-7.10.2.dist-info}/WHEEL +0 -0
- {mcli_framework-7.10.0.dist-info → mcli_framework-7.10.2.dist-info}/entry_points.txt +0 -0
- {mcli_framework-7.10.0.dist-info → mcli_framework-7.10.2.dist-info}/licenses/LICENSE +0 -0
- {mcli_framework-7.10.0.dist-info → mcli_framework-7.10.2.dist-info}/top_level.txt +0 -0
mcli/lib/custom_commands.py
CHANGED
|
@@ -107,15 +107,25 @@ class CustomCommandManager:
|
|
|
107
107
|
"""
|
|
108
108
|
Load all custom commands from the commands directory.
|
|
109
109
|
|
|
110
|
+
Automatically filters out test commands (starting with 'test_' or 'test-')
|
|
111
|
+
unless MCLI_INCLUDE_TEST_COMMANDS=true is set.
|
|
112
|
+
|
|
110
113
|
Returns:
|
|
111
114
|
List of command data dictionaries
|
|
112
115
|
"""
|
|
113
116
|
commands = []
|
|
117
|
+
include_test = os.environ.get('MCLI_INCLUDE_TEST_COMMANDS', 'false').lower() == 'true'
|
|
118
|
+
|
|
114
119
|
for command_file in self.commands_dir.glob("*.json"):
|
|
115
120
|
# Skip the lockfile
|
|
116
121
|
if command_file.name == "commands.lock.json":
|
|
117
122
|
continue
|
|
118
123
|
|
|
124
|
+
# Skip test commands unless explicitly included
|
|
125
|
+
if not include_test and command_file.stem.startswith(('test_', 'test-')):
|
|
126
|
+
logger.debug(f"Skipping test command: {command_file.name}")
|
|
127
|
+
continue
|
|
128
|
+
|
|
119
129
|
command_data = self.load_command(command_file)
|
|
120
130
|
if command_data:
|
|
121
131
|
commands.append(command_data)
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Utilities for graceful handling of optional dependencies.
|
|
3
|
+
|
|
4
|
+
This module provides helper functions and decorators to handle optional
|
|
5
|
+
dependencies gracefully, with clear error messages when features are unavailable.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import functools
|
|
9
|
+
from typing import Any, Callable, Dict, Optional, Tuple
|
|
10
|
+
|
|
11
|
+
from mcli.lib.logger.logger import get_logger
|
|
12
|
+
|
|
13
|
+
logger = get_logger(__name__)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class OptionalDependency:
|
|
17
|
+
"""
|
|
18
|
+
Container for an optional dependency with availability tracking.
|
|
19
|
+
|
|
20
|
+
Example:
|
|
21
|
+
>>> ollama = OptionalDependency("ollama")
|
|
22
|
+
>>> if ollama.available:
|
|
23
|
+
... client = ollama.module.Client()
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
def __init__(
|
|
27
|
+
self,
|
|
28
|
+
module_name: str,
|
|
29
|
+
import_name: Optional[str] = None,
|
|
30
|
+
install_hint: Optional[str] = None,
|
|
31
|
+
):
|
|
32
|
+
"""
|
|
33
|
+
Initialize optional dependency handler.
|
|
34
|
+
|
|
35
|
+
Args:
|
|
36
|
+
module_name: Name of the module to import (e.g., "ollama")
|
|
37
|
+
import_name: Alternative import name if different from module_name
|
|
38
|
+
install_hint: Custom installation instruction
|
|
39
|
+
"""
|
|
40
|
+
self.module_name = module_name
|
|
41
|
+
self.import_name = import_name or module_name
|
|
42
|
+
self.install_hint = install_hint or f"pip install {module_name}"
|
|
43
|
+
self.module: Optional[Any] = None
|
|
44
|
+
self.available = False
|
|
45
|
+
self.error: Optional[Exception] = None
|
|
46
|
+
|
|
47
|
+
self._try_import()
|
|
48
|
+
|
|
49
|
+
def _try_import(self):
|
|
50
|
+
"""Attempt to import the module."""
|
|
51
|
+
try:
|
|
52
|
+
self.module = __import__(self.import_name)
|
|
53
|
+
self.available = True
|
|
54
|
+
logger.debug(f"Optional dependency '{self.module_name}' is available")
|
|
55
|
+
except ImportError as e:
|
|
56
|
+
self.available = False
|
|
57
|
+
self.error = e
|
|
58
|
+
logger.debug(f"Optional dependency '{self.module_name}' is not available: {e}")
|
|
59
|
+
|
|
60
|
+
def require(self, feature_name: Optional[str] = None) -> Any:
|
|
61
|
+
"""
|
|
62
|
+
Require the dependency to be available, raising an error if not.
|
|
63
|
+
|
|
64
|
+
Args:
|
|
65
|
+
feature_name: Name of the feature requiring this dependency
|
|
66
|
+
|
|
67
|
+
Returns:
|
|
68
|
+
The imported module
|
|
69
|
+
|
|
70
|
+
Raises:
|
|
71
|
+
ImportError: If the dependency is not available
|
|
72
|
+
"""
|
|
73
|
+
if not self.available:
|
|
74
|
+
feature_msg = f" for {feature_name}" if feature_name else ""
|
|
75
|
+
raise ImportError(
|
|
76
|
+
f"'{self.module_name}' is required{feature_msg} but not installed.\n"
|
|
77
|
+
f"Install it with: {self.install_hint}"
|
|
78
|
+
)
|
|
79
|
+
return self.module
|
|
80
|
+
|
|
81
|
+
def __getattr__(self, name: str) -> Any:
|
|
82
|
+
"""Allow direct attribute access to the module."""
|
|
83
|
+
if not self.available:
|
|
84
|
+
raise ImportError(
|
|
85
|
+
f"Cannot access '{name}' from '{self.module_name}' - module not installed.\n"
|
|
86
|
+
f"Install it with: {self.install_hint}"
|
|
87
|
+
)
|
|
88
|
+
return getattr(self.module, name)
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def optional_import(
|
|
92
|
+
module_name: str, import_name: Optional[str] = None, install_hint: Optional[str] = None
|
|
93
|
+
) -> Tuple[Optional[Any], bool]:
|
|
94
|
+
"""
|
|
95
|
+
Try to import an optional dependency.
|
|
96
|
+
|
|
97
|
+
Args:
|
|
98
|
+
module_name: Name of the module to import
|
|
99
|
+
import_name: Alternative import name if different from module_name
|
|
100
|
+
install_hint: Custom installation instruction
|
|
101
|
+
|
|
102
|
+
Returns:
|
|
103
|
+
Tuple of (module, available) where module is None if unavailable
|
|
104
|
+
|
|
105
|
+
Example:
|
|
106
|
+
>>> ollama, OLLAMA_AVAILABLE = optional_import("ollama")
|
|
107
|
+
>>> if OLLAMA_AVAILABLE:
|
|
108
|
+
... client = ollama.Client()
|
|
109
|
+
"""
|
|
110
|
+
dep = OptionalDependency(module_name, import_name, install_hint)
|
|
111
|
+
return (dep.module, dep.available)
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def require_dependency(
|
|
115
|
+
module_name: str, feature_name: str, install_hint: Optional[str] = None
|
|
116
|
+
) -> Any:
|
|
117
|
+
"""
|
|
118
|
+
Require a dependency, raising clear error if not available.
|
|
119
|
+
|
|
120
|
+
Args:
|
|
121
|
+
module_name: Name of the module to import
|
|
122
|
+
feature_name: Name of the feature requiring this dependency
|
|
123
|
+
install_hint: Custom installation instruction
|
|
124
|
+
|
|
125
|
+
Returns:
|
|
126
|
+
The imported module
|
|
127
|
+
|
|
128
|
+
Raises:
|
|
129
|
+
ImportError: If the dependency is not available
|
|
130
|
+
|
|
131
|
+
Example:
|
|
132
|
+
>>> streamlit = require_dependency("streamlit", "dashboard")
|
|
133
|
+
"""
|
|
134
|
+
dep = OptionalDependency(module_name, install_hint=install_hint)
|
|
135
|
+
return dep.require(feature_name)
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def requires(*dependencies: str, install_all_hint: Optional[str] = None):
|
|
139
|
+
"""
|
|
140
|
+
Decorator to mark a function as requiring specific dependencies.
|
|
141
|
+
|
|
142
|
+
Args:
|
|
143
|
+
*dependencies: Module names required by the function
|
|
144
|
+
install_all_hint: Custom installation instruction for all dependencies
|
|
145
|
+
|
|
146
|
+
Raises:
|
|
147
|
+
ImportError: If any required dependency is not available
|
|
148
|
+
|
|
149
|
+
Example:
|
|
150
|
+
>>> @requires("torch", "transformers")
|
|
151
|
+
... def train_model():
|
|
152
|
+
... import torch
|
|
153
|
+
... import transformers
|
|
154
|
+
... # training code
|
|
155
|
+
"""
|
|
156
|
+
|
|
157
|
+
def decorator(func: Callable) -> Callable:
|
|
158
|
+
@functools.wraps(func)
|
|
159
|
+
def wrapper(*args, **kwargs):
|
|
160
|
+
missing = []
|
|
161
|
+
for dep_name in dependencies:
|
|
162
|
+
dep = OptionalDependency(dep_name)
|
|
163
|
+
if not dep.available:
|
|
164
|
+
missing.append(dep_name)
|
|
165
|
+
|
|
166
|
+
if missing:
|
|
167
|
+
if install_all_hint:
|
|
168
|
+
hint = install_all_hint
|
|
169
|
+
else:
|
|
170
|
+
hint = f"pip install {' '.join(missing)}"
|
|
171
|
+
|
|
172
|
+
raise ImportError(
|
|
173
|
+
f"Function '{func.__name__}' requires missing dependencies: {', '.join(missing)}\n"
|
|
174
|
+
f"Install them with: {hint}"
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
return func(*args, **kwargs)
|
|
178
|
+
|
|
179
|
+
return wrapper
|
|
180
|
+
|
|
181
|
+
return decorator
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
# Common optional dependencies registry
|
|
185
|
+
OPTIONAL_DEPS: Dict[str, OptionalDependency] = {}
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
def register_optional_dependency(
|
|
189
|
+
module_name: str, import_name: Optional[str] = None, install_hint: Optional[str] = None
|
|
190
|
+
) -> OptionalDependency:
|
|
191
|
+
"""
|
|
192
|
+
Register and cache an optional dependency.
|
|
193
|
+
|
|
194
|
+
Args:
|
|
195
|
+
module_name: Name of the module to import
|
|
196
|
+
import_name: Alternative import name if different from module_name
|
|
197
|
+
install_hint: Custom installation instruction
|
|
198
|
+
|
|
199
|
+
Returns:
|
|
200
|
+
OptionalDependency instance
|
|
201
|
+
"""
|
|
202
|
+
if module_name not in OPTIONAL_DEPS:
|
|
203
|
+
OPTIONAL_DEPS[module_name] = OptionalDependency(module_name, import_name, install_hint)
|
|
204
|
+
return OPTIONAL_DEPS[module_name]
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
def check_dependencies(*module_names: str) -> Dict[str, bool]:
|
|
208
|
+
"""
|
|
209
|
+
Check availability of multiple dependencies.
|
|
210
|
+
|
|
211
|
+
Args:
|
|
212
|
+
*module_names: Module names to check
|
|
213
|
+
|
|
214
|
+
Returns:
|
|
215
|
+
Dictionary mapping module names to availability status
|
|
216
|
+
|
|
217
|
+
Example:
|
|
218
|
+
>>> status = check_dependencies("torch", "transformers", "streamlit")
|
|
219
|
+
>>> print(status)
|
|
220
|
+
{'torch': True, 'transformers': False, 'streamlit': True}
|
|
221
|
+
"""
|
|
222
|
+
return {
|
|
223
|
+
name: OptionalDependency(name).available for name in module_names
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
# Pre-register common optional dependencies
|
|
228
|
+
_COMMON_DEPS = {
|
|
229
|
+
"ollama": ("ollama", "pip install ollama"),
|
|
230
|
+
"streamlit": ("streamlit", "pip install streamlit"),
|
|
231
|
+
"torch": ("torch", "pip install torch"),
|
|
232
|
+
"transformers": ("transformers", "pip install transformers"),
|
|
233
|
+
"mlflow": ("mlflow", "pip install mlflow"),
|
|
234
|
+
"plotly": ("plotly", "pip install plotly"),
|
|
235
|
+
"pandas": ("pandas", "pip install pandas"),
|
|
236
|
+
"numpy": ("numpy", "pip install numpy"),
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
for module_name, (import_name, hint) in _COMMON_DEPS.items():
|
|
240
|
+
register_optional_dependency(module_name, import_name, hint)
|
mcli/ml/backtesting/run.py
CHANGED
|
@@ -18,12 +18,14 @@ def cli():
|
|
|
18
18
|
@click.option("--end-date", required=True, help="End date (YYYY-MM-DD)")
|
|
19
19
|
@click.option("--initial-capital", default=100000, help="Initial capital")
|
|
20
20
|
@click.option("--output", help="Output file for results")
|
|
21
|
-
def run_backtest(
|
|
21
|
+
def run_backtest(
|
|
22
|
+
strategy: str, start_date: str, end_date: str, initial_capital: float, output: str
|
|
23
|
+
):
|
|
22
24
|
"""Run a backtest with the specified parameters."""
|
|
23
25
|
info(f"Running backtest for strategy: {strategy}")
|
|
24
26
|
info(f"Period: {start_date} to {end_date}")
|
|
25
27
|
info(f"Initial capital: ${initial_capital:,.2f}")
|
|
26
|
-
|
|
28
|
+
|
|
27
29
|
# TODO: Implement actual backtesting logic
|
|
28
30
|
error("Backtesting functionality not yet implemented")
|
|
29
31
|
|
|
@@ -51,4 +53,4 @@ def main():
|
|
|
51
53
|
|
|
52
54
|
|
|
53
55
|
if __name__ == "__main__":
|
|
54
|
-
main()
|
|
56
|
+
main()
|
|
@@ -10,6 +10,7 @@ import pandas as pd
|
|
|
10
10
|
import torch
|
|
11
11
|
import torch.nn as nn
|
|
12
12
|
import torch.nn.functional as F
|
|
13
|
+
|
|
13
14
|
from mcli.ml.models.base_models import BaseStockModel, ModelMetrics, ValidationResult
|
|
14
15
|
from mcli.ml.models.ensemble_models import DeepEnsembleModel, EnsembleConfig, ModelConfig
|
|
15
16
|
|
mcli/ml/optimization/optimize.py
CHANGED
|
@@ -18,13 +18,15 @@ def cli():
|
|
|
18
18
|
@click.option("--end-date", required=True, help="End date (YYYY-MM-DD)")
|
|
19
19
|
@click.option("--risk-free-rate", default=0.02, help="Risk-free rate")
|
|
20
20
|
@click.option("--output", help="Output file for results")
|
|
21
|
-
def optimize_portfolio(
|
|
21
|
+
def optimize_portfolio(
|
|
22
|
+
symbols: str, start_date: str, end_date: str, risk_free_rate: float, output: str
|
|
23
|
+
):
|
|
22
24
|
"""Optimize portfolio allocation for given symbols."""
|
|
23
25
|
symbol_list = [s.strip() for s in symbols.split(",")]
|
|
24
26
|
info(f"Optimizing portfolio for: {', '.join(symbol_list)}")
|
|
25
27
|
info(f"Period: {start_date} to {end_date}")
|
|
26
28
|
info(f"Risk-free rate: {risk_free_rate:.2%}")
|
|
27
|
-
|
|
29
|
+
|
|
28
30
|
# TODO: Implement actual optimization
|
|
29
31
|
error("Portfolio optimization not yet implemented")
|
|
30
32
|
|
|
@@ -37,7 +39,7 @@ def efficient_frontier(symbols: str, points: int):
|
|
|
37
39
|
symbol_list = [s.strip() for s in symbols.split(",")]
|
|
38
40
|
info(f"Generating efficient frontier for: {', '.join(symbol_list)}")
|
|
39
41
|
info(f"Points: {points}")
|
|
40
|
-
|
|
42
|
+
|
|
41
43
|
# TODO: Implement efficient frontier generation
|
|
42
44
|
error("Efficient frontier generation not yet implemented")
|
|
43
45
|
|
|
@@ -48,4 +50,4 @@ def main():
|
|
|
48
50
|
|
|
49
51
|
|
|
50
52
|
if __name__ == "__main__":
|
|
51
|
-
main()
|
|
53
|
+
main()
|
mcli/ml/serving/serve.py
CHANGED
|
@@ -20,7 +20,7 @@ def start_server(model: str, port: int, host: str):
|
|
|
20
20
|
"""Start the model serving server."""
|
|
21
21
|
info(f"Starting model server for: {model}")
|
|
22
22
|
info(f"Serving on {host}:{port}")
|
|
23
|
-
|
|
23
|
+
|
|
24
24
|
# TODO: Implement actual model serving
|
|
25
25
|
error("Model serving functionality not yet implemented")
|
|
26
26
|
|
|
@@ -47,4 +47,4 @@ def main():
|
|
|
47
47
|
|
|
48
48
|
|
|
49
49
|
if __name__ == "__main__":
|
|
50
|
-
main()
|
|
50
|
+
main()
|
mcli/ml/training/train.py
CHANGED
|
@@ -19,12 +19,19 @@ def cli():
|
|
|
19
19
|
@click.option("--batch-size", default=32, help="Batch size for training")
|
|
20
20
|
@click.option("--learning-rate", default=0.001, help="Learning rate")
|
|
21
21
|
@click.option("--output-dir", help="Directory to save trained model")
|
|
22
|
-
def train_model(
|
|
22
|
+
def train_model(
|
|
23
|
+
model_type: str,
|
|
24
|
+
dataset: str,
|
|
25
|
+
epochs: int,
|
|
26
|
+
batch_size: int,
|
|
27
|
+
learning_rate: float,
|
|
28
|
+
output_dir: str,
|
|
29
|
+
):
|
|
23
30
|
"""Train a model with the specified parameters."""
|
|
24
31
|
info(f"Training {model_type} model")
|
|
25
32
|
info(f"Dataset: {dataset}")
|
|
26
33
|
info(f"Epochs: {epochs}, Batch size: {batch_size}, Learning rate: {learning_rate}")
|
|
27
|
-
|
|
34
|
+
|
|
28
35
|
# TODO: Implement actual training logic
|
|
29
36
|
error("Model training functionality not yet implemented")
|
|
30
37
|
|
|
@@ -36,7 +43,7 @@ def resume_training(checkpoint: str, epochs: int):
|
|
|
36
43
|
"""Resume training from a checkpoint."""
|
|
37
44
|
info(f"Resuming training from: {checkpoint}")
|
|
38
45
|
info(f"Additional epochs: {epochs}")
|
|
39
|
-
|
|
46
|
+
|
|
40
47
|
# TODO: Implement resume functionality
|
|
41
48
|
error("Resume training not yet implemented")
|
|
42
49
|
|
|
@@ -46,14 +53,14 @@ def resume_training(checkpoint: str, epochs: int):
|
|
|
46
53
|
def train_politician_trading(output_dir: str):
|
|
47
54
|
"""Train the politician trading prediction model."""
|
|
48
55
|
info("Training politician trading prediction model...")
|
|
49
|
-
|
|
56
|
+
|
|
50
57
|
try:
|
|
51
58
|
# Import the actual training function
|
|
52
59
|
from mcli.ml.training.train_model import train_politician_trading_model
|
|
53
|
-
|
|
60
|
+
|
|
54
61
|
# Run the training
|
|
55
62
|
metrics = train_politician_trading_model(output_dir)
|
|
56
|
-
|
|
63
|
+
|
|
57
64
|
if metrics:
|
|
58
65
|
success(f"Training completed! Final loss: {metrics.get('final_loss', 'N/A')}")
|
|
59
66
|
else:
|
|
@@ -70,4 +77,4 @@ def main():
|
|
|
70
77
|
|
|
71
78
|
|
|
72
79
|
if __name__ == "__main__":
|
|
73
|
-
main()
|
|
80
|
+
main()
|
mcli/self/completion_cmd.py
CHANGED
|
@@ -216,10 +216,10 @@ def completion_status():
|
|
|
216
216
|
|
|
217
217
|
click.echo()
|
|
218
218
|
click.echo("💡 To install completion for your shell:")
|
|
219
|
-
click.echo(" mcli completion install")
|
|
219
|
+
click.echo(" mcli self completion install")
|
|
220
220
|
click.echo()
|
|
221
221
|
click.echo("💡 To generate completion script manually:")
|
|
222
|
-
click.echo(f" mcli completion {shell_name}")
|
|
222
|
+
click.echo(f" mcli self completion {shell_name}")
|
|
223
223
|
|
|
224
224
|
|
|
225
225
|
# Export the CLI group for registration
|