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.

Files changed (42) hide show
  1. mcli/lib/custom_commands.py +10 -0
  2. mcli/lib/optional_deps.py +240 -0
  3. mcli/ml/backtesting/run.py +5 -3
  4. mcli/ml/models/ensemble_models.py +1 -0
  5. mcli/ml/models/recommendation_models.py +1 -0
  6. mcli/ml/optimization/optimize.py +6 -4
  7. mcli/ml/serving/serve.py +2 -2
  8. mcli/ml/training/train.py +14 -7
  9. mcli/self/completion_cmd.py +2 -2
  10. mcli/workflow/doc_convert.py +82 -112
  11. mcli/workflow/git_commit/ai_service.py +13 -2
  12. mcli/workflow/notebook/converter.py +375 -0
  13. mcli/workflow/notebook/notebook_cmd.py +441 -0
  14. mcli/workflow/notebook/schema.py +402 -0
  15. mcli/workflow/notebook/validator.py +313 -0
  16. mcli/workflow/workflow.py +14 -0
  17. {mcli_framework-7.10.0.dist-info → mcli_framework-7.10.2.dist-info}/METADATA +37 -3
  18. {mcli_framework-7.10.0.dist-info → mcli_framework-7.10.2.dist-info}/RECORD +22 -37
  19. mcli/ml/features/political_features.py +0 -677
  20. mcli/ml/preprocessing/politician_trading_preprocessor.py +0 -570
  21. mcli/workflow/politician_trading/config.py +0 -134
  22. mcli/workflow/politician_trading/connectivity.py +0 -492
  23. mcli/workflow/politician_trading/data_sources.py +0 -654
  24. mcli/workflow/politician_trading/database.py +0 -412
  25. mcli/workflow/politician_trading/demo.py +0 -249
  26. mcli/workflow/politician_trading/models.py +0 -327
  27. mcli/workflow/politician_trading/monitoring.py +0 -413
  28. mcli/workflow/politician_trading/scrapers.py +0 -1074
  29. mcli/workflow/politician_trading/scrapers_california.py +0 -434
  30. mcli/workflow/politician_trading/scrapers_corporate_registry.py +0 -797
  31. mcli/workflow/politician_trading/scrapers_eu.py +0 -376
  32. mcli/workflow/politician_trading/scrapers_free_sources.py +0 -509
  33. mcli/workflow/politician_trading/scrapers_third_party.py +0 -373
  34. mcli/workflow/politician_trading/scrapers_uk.py +0 -378
  35. mcli/workflow/politician_trading/scrapers_us_states.py +0 -471
  36. mcli/workflow/politician_trading/seed_database.py +0 -520
  37. mcli/workflow/politician_trading/supabase_functions.py +0 -354
  38. mcli/workflow/politician_trading/workflow.py +0 -879
  39. {mcli_framework-7.10.0.dist-info → mcli_framework-7.10.2.dist-info}/WHEEL +0 -0
  40. {mcli_framework-7.10.0.dist-info → mcli_framework-7.10.2.dist-info}/entry_points.txt +0 -0
  41. {mcli_framework-7.10.0.dist-info → mcli_framework-7.10.2.dist-info}/licenses/LICENSE +0 -0
  42. {mcli_framework-7.10.0.dist-info → mcli_framework-7.10.2.dist-info}/top_level.txt +0 -0
@@ -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)
@@ -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(strategy: str, start_date: str, end_date: str, initial_capital: float, output: str):
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()
@@ -9,6 +9,7 @@ import pandas as pd
9
9
  import torch
10
10
  import torch.nn as nn
11
11
  import torch.nn.functional as F
12
+
12
13
  from mcli.ml.models.base_models import BaseStockModel, ModelMetrics, ValidationResult
13
14
 
14
15
  logger = logging.getLogger(__name__)
@@ -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
 
@@ -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(symbols: str, start_date: str, end_date: str, risk_free_rate: float, output: str):
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(model_type: str, dataset: str, epochs: int, batch_size: int, learning_rate: float, output_dir: str):
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()
@@ -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