mcli-framework 7.12.1__py3-none-any.whl → 7.12.3__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/app/__init__.py +0 -2
- mcli/app/commands_cmd.py +19 -23
- mcli/app/completion_helpers.py +5 -5
- mcli/app/init_cmd.py +10 -10
- mcli/app/lock_cmd.py +82 -27
- mcli/app/main.py +2 -8
- mcli/app/model/model.py +5 -10
- mcli/app/store_cmd.py +8 -8
- mcli/app/video/__init__.py +0 -2
- mcli/app/video/video.py +1 -14
- mcli/chat/chat.py +90 -108
- mcli/chat/command_rag.py +0 -4
- mcli/chat/enhanced_chat.py +32 -41
- mcli/chat/system_controller.py +37 -37
- mcli/chat/system_integration.py +4 -5
- mcli/cli.py +2 -3
- mcli/lib/api/api.py +4 -9
- mcli/lib/api/daemon_client.py +19 -20
- mcli/lib/api/daemon_client_local.py +1 -3
- mcli/lib/api/daemon_decorator.py +6 -6
- mcli/lib/api/mcli_decorators.py +4 -8
- mcli/lib/auth/__init__.py +0 -1
- mcli/lib/auth/auth.py +4 -5
- mcli/lib/auth/mcli_manager.py +7 -12
- mcli/lib/auth/token_util.py +5 -5
- mcli/lib/config/__init__.py +29 -1
- mcli/lib/config/config.py +0 -1
- mcli/lib/custom_commands.py +1 -1
- mcli/lib/discovery/command_discovery.py +15 -15
- mcli/lib/erd/erd.py +7 -7
- mcli/lib/files/files.py +1 -1
- mcli/lib/fs/__init__.py +31 -1
- mcli/lib/fs/fs.py +12 -13
- mcli/lib/lib.py +0 -1
- mcli/lib/logger/logger.py +7 -10
- mcli/lib/performance/optimizer.py +25 -27
- mcli/lib/performance/rust_bridge.py +22 -27
- mcli/lib/performance/uvloop_config.py +0 -1
- mcli/lib/pickles/__init__.py +0 -1
- mcli/lib/pickles/pickles.py +0 -2
- mcli/lib/secrets/commands.py +0 -2
- mcli/lib/secrets/manager.py +0 -1
- mcli/lib/secrets/repl.py +2 -3
- mcli/lib/secrets/store.py +1 -2
- mcli/lib/services/data_pipeline.py +34 -34
- mcli/lib/services/lsh_client.py +38 -40
- mcli/lib/shell/shell.py +2 -2
- mcli/lib/toml/__init__.py +0 -1
- mcli/lib/ui/styling.py +0 -1
- mcli/lib/ui/visual_effects.py +33 -41
- mcli/lib/watcher/watcher.py +0 -1
- mcli/ml/__init__.py +1 -1
- mcli/ml/api/__init__.py +1 -1
- mcli/ml/api/app.py +8 -9
- mcli/ml/api/middleware.py +10 -10
- mcli/ml/api/routers/__init__.py +1 -1
- mcli/ml/api/routers/admin_router.py +3 -3
- mcli/ml/api/routers/auth_router.py +17 -18
- mcli/ml/api/routers/backtest_router.py +2 -2
- mcli/ml/api/routers/data_router.py +2 -2
- mcli/ml/api/routers/model_router.py +14 -15
- mcli/ml/api/routers/monitoring_router.py +2 -2
- mcli/ml/api/routers/portfolio_router.py +2 -2
- mcli/ml/api/routers/prediction_router.py +10 -9
- mcli/ml/api/routers/trade_router.py +2 -2
- mcli/ml/api/routers/websocket_router.py +6 -7
- mcli/ml/api/schemas.py +2 -2
- mcli/ml/auth/__init__.py +1 -1
- mcli/ml/auth/auth_manager.py +22 -23
- mcli/ml/auth/models.py +17 -17
- mcli/ml/auth/permissions.py +17 -17
- mcli/ml/backtesting/__init__.py +1 -1
- mcli/ml/backtesting/backtest_engine.py +31 -35
- mcli/ml/backtesting/performance_metrics.py +12 -14
- mcli/ml/backtesting/run.py +1 -2
- mcli/ml/cache.py +35 -36
- mcli/ml/cli/__init__.py +1 -1
- mcli/ml/cli/main.py +21 -24
- mcli/ml/config/__init__.py +1 -1
- mcli/ml/config/settings.py +28 -29
- mcli/ml/configs/__init__.py +1 -1
- mcli/ml/configs/dvc_config.py +14 -15
- mcli/ml/configs/mlflow_config.py +12 -13
- mcli/ml/configs/mlops_manager.py +19 -21
- mcli/ml/dashboard/__init__.py +4 -4
- mcli/ml/dashboard/app.py +20 -30
- mcli/ml/dashboard/app_supabase.py +16 -19
- mcli/ml/dashboard/app_training.py +11 -14
- mcli/ml/dashboard/cli.py +2 -2
- mcli/ml/dashboard/common.py +2 -3
- mcli/ml/dashboard/components/__init__.py +1 -1
- mcli/ml/dashboard/components/charts.py +13 -11
- mcli/ml/dashboard/components/metrics.py +7 -7
- mcli/ml/dashboard/components/tables.py +12 -9
- mcli/ml/dashboard/overview.py +2 -2
- mcli/ml/dashboard/pages/__init__.py +1 -1
- mcli/ml/dashboard/pages/cicd.py +15 -18
- mcli/ml/dashboard/pages/debug_dependencies.py +7 -7
- mcli/ml/dashboard/pages/monte_carlo_predictions.py +11 -18
- mcli/ml/dashboard/pages/predictions_enhanced.py +24 -32
- mcli/ml/dashboard/pages/scrapers_and_logs.py +22 -24
- mcli/ml/dashboard/pages/test_portfolio.py +3 -6
- mcli/ml/dashboard/pages/trading.py +16 -18
- mcli/ml/dashboard/pages/workflows.py +20 -30
- mcli/ml/dashboard/utils.py +9 -9
- mcli/ml/dashboard/warning_suppression.py +3 -3
- mcli/ml/data_ingestion/__init__.py +1 -1
- mcli/ml/data_ingestion/api_connectors.py +41 -46
- mcli/ml/data_ingestion/data_pipeline.py +36 -46
- mcli/ml/data_ingestion/stream_processor.py +43 -46
- mcli/ml/database/__init__.py +1 -1
- mcli/ml/database/migrations/env.py +2 -2
- mcli/ml/database/models.py +22 -24
- mcli/ml/database/session.py +14 -14
- mcli/ml/experimentation/__init__.py +1 -1
- mcli/ml/experimentation/ab_testing.py +45 -46
- mcli/ml/features/__init__.py +1 -1
- mcli/ml/features/ensemble_features.py +22 -27
- mcli/ml/features/recommendation_engine.py +30 -30
- mcli/ml/features/stock_features.py +29 -32
- mcli/ml/features/test_feature_engineering.py +10 -11
- mcli/ml/logging.py +4 -4
- mcli/ml/mlops/__init__.py +1 -1
- mcli/ml/mlops/data_versioning.py +29 -30
- mcli/ml/mlops/experiment_tracker.py +24 -24
- mcli/ml/mlops/model_serving.py +31 -34
- mcli/ml/mlops/pipeline_orchestrator.py +27 -35
- mcli/ml/models/__init__.py +5 -6
- mcli/ml/models/base_models.py +23 -23
- mcli/ml/models/ensemble_models.py +31 -31
- mcli/ml/models/recommendation_models.py +18 -19
- mcli/ml/models/test_models.py +14 -16
- mcli/ml/monitoring/__init__.py +1 -1
- mcli/ml/monitoring/drift_detection.py +32 -36
- mcli/ml/monitoring/metrics.py +2 -2
- mcli/ml/optimization/__init__.py +1 -1
- mcli/ml/optimization/optimize.py +1 -2
- mcli/ml/optimization/portfolio_optimizer.py +30 -32
- mcli/ml/predictions/__init__.py +1 -1
- mcli/ml/preprocessing/__init__.py +1 -1
- mcli/ml/preprocessing/data_cleaners.py +22 -23
- mcli/ml/preprocessing/feature_extractors.py +23 -26
- mcli/ml/preprocessing/ml_pipeline.py +23 -23
- mcli/ml/preprocessing/test_preprocessing.py +7 -8
- mcli/ml/scripts/populate_sample_data.py +0 -4
- mcli/ml/serving/serve.py +1 -2
- mcli/ml/tasks.py +17 -17
- mcli/ml/tests/test_integration.py +29 -30
- mcli/ml/tests/test_training_dashboard.py +21 -21
- mcli/ml/trading/__init__.py +1 -1
- mcli/ml/trading/migrations.py +5 -5
- mcli/ml/trading/models.py +21 -23
- mcli/ml/trading/paper_trading.py +16 -13
- mcli/ml/trading/risk_management.py +17 -18
- mcli/ml/trading/trading_service.py +25 -28
- mcli/ml/training/__init__.py +1 -1
- mcli/ml/training/train.py +0 -1
- mcli/public/oi/oi.py +1 -2
- mcli/self/completion_cmd.py +6 -10
- mcli/self/logs_cmd.py +19 -24
- mcli/self/migrate_cmd.py +22 -20
- mcli/self/redis_cmd.py +10 -11
- mcli/self/self_cmd.py +10 -18
- mcli/self/store_cmd.py +10 -12
- mcli/self/visual_cmd.py +9 -14
- mcli/self/zsh_cmd.py +2 -4
- mcli/workflow/daemon/async_command_database.py +23 -24
- mcli/workflow/daemon/async_process_manager.py +27 -29
- mcli/workflow/daemon/client.py +27 -33
- mcli/workflow/daemon/daemon.py +32 -36
- mcli/workflow/daemon/enhanced_daemon.py +24 -33
- mcli/workflow/daemon/process_cli.py +11 -12
- mcli/workflow/daemon/process_manager.py +23 -26
- mcli/workflow/daemon/test_daemon.py +4 -5
- mcli/workflow/dashboard/dashboard_cmd.py +0 -1
- mcli/workflow/doc_convert.py +15 -17
- mcli/workflow/gcloud/__init__.py +0 -1
- mcli/workflow/gcloud/gcloud.py +11 -8
- mcli/workflow/git_commit/ai_service.py +14 -15
- mcli/workflow/lsh_integration.py +9 -11
- mcli/workflow/model_service/client.py +26 -31
- mcli/workflow/model_service/download_and_run_efficient_models.py +10 -14
- mcli/workflow/model_service/lightweight_embedder.py +25 -35
- mcli/workflow/model_service/lightweight_model_server.py +26 -32
- mcli/workflow/model_service/lightweight_test.py +7 -10
- mcli/workflow/model_service/model_service.py +80 -91
- mcli/workflow/model_service/ollama_efficient_runner.py +14 -18
- mcli/workflow/model_service/openai_adapter.py +23 -23
- mcli/workflow/model_service/pdf_processor.py +21 -26
- mcli/workflow/model_service/test_efficient_runner.py +12 -16
- mcli/workflow/model_service/test_example.py +11 -13
- mcli/workflow/model_service/test_integration.py +3 -5
- mcli/workflow/model_service/test_new_features.py +7 -8
- mcli/workflow/notebook/converter.py +1 -1
- mcli/workflow/notebook/notebook_cmd.py +5 -6
- mcli/workflow/notebook/schema.py +0 -1
- mcli/workflow/notebook/validator.py +7 -3
- mcli/workflow/openai/openai.py +1 -2
- mcli/workflow/registry/registry.py +4 -1
- mcli/workflow/repo/repo.py +6 -7
- mcli/workflow/scheduler/cron_parser.py +16 -19
- mcli/workflow/scheduler/job.py +10 -10
- mcli/workflow/scheduler/monitor.py +15 -15
- mcli/workflow/scheduler/persistence.py +17 -18
- mcli/workflow/scheduler/scheduler.py +37 -38
- mcli/workflow/secrets/__init__.py +1 -1
- mcli/workflow/sync/test_cmd.py +0 -1
- mcli/workflow/wakatime/__init__.py +5 -9
- mcli/workflow/wakatime/wakatime.py +1 -2
- {mcli_framework-7.12.1.dist-info → mcli_framework-7.12.3.dist-info}/METADATA +1 -1
- mcli_framework-7.12.3.dist-info/RECORD +279 -0
- mcli_framework-7.12.1.dist-info/RECORD +0 -279
- {mcli_framework-7.12.1.dist-info → mcli_framework-7.12.3.dist-info}/WHEEL +0 -0
- {mcli_framework-7.12.1.dist-info → mcli_framework-7.12.3.dist-info}/entry_points.txt +0 -0
- {mcli_framework-7.12.1.dist-info → mcli_framework-7.12.3.dist-info}/licenses/LICENSE +0 -0
- {mcli_framework-7.12.1.dist-info → mcli_framework-7.12.3.dist-info}/top_level.txt +0 -0
mcli/app/__init__.py
CHANGED
mcli/app/commands_cmd.py
CHANGED
|
@@ -4,7 +4,6 @@ import inspect
|
|
|
4
4
|
import json
|
|
5
5
|
import os
|
|
6
6
|
import re
|
|
7
|
-
import shutil
|
|
8
7
|
import subprocess
|
|
9
8
|
import tempfile
|
|
10
9
|
from datetime import datetime
|
|
@@ -12,16 +11,13 @@ from pathlib import Path
|
|
|
12
11
|
from typing import Any, Dict, List, Optional
|
|
13
12
|
|
|
14
13
|
import click
|
|
15
|
-
import tomli
|
|
16
|
-
from rich.console import Console
|
|
17
14
|
from rich.prompt import Prompt
|
|
18
|
-
from rich.table import Table
|
|
19
15
|
|
|
20
16
|
from mcli.lib.api.daemon_client import get_daemon_client
|
|
21
17
|
from mcli.lib.custom_commands import get_command_manager
|
|
22
18
|
from mcli.lib.discovery.command_discovery import get_command_discovery
|
|
23
19
|
from mcli.lib.logger.logger import get_logger
|
|
24
|
-
from mcli.lib.ui.styling import console
|
|
20
|
+
from mcli.lib.ui.styling import console
|
|
25
21
|
|
|
26
22
|
logger = get_logger(__name__)
|
|
27
23
|
|
|
@@ -78,7 +74,7 @@ def collect_commands() -> List[Dict[str, Any]]:
|
|
|
78
74
|
streamlit_logger.setLevel(original_level)
|
|
79
75
|
|
|
80
76
|
# Extract command and group objects
|
|
81
|
-
for
|
|
77
|
+
for _name, obj in inspect.getmembers(module):
|
|
82
78
|
# Handle Click commands and groups
|
|
83
79
|
if isinstance(obj, click.Command):
|
|
84
80
|
if isinstance(obj, click.Group):
|
|
@@ -118,7 +114,7 @@ def collect_commands() -> List[Dict[str, Any]]:
|
|
|
118
114
|
|
|
119
115
|
|
|
120
116
|
def get_current_command_state():
|
|
121
|
-
"""Collect all command metadata (names, groups, etc.)"""
|
|
117
|
+
"""Collect all command metadata (names, groups, etc.)."""
|
|
122
118
|
return collect_commands()
|
|
123
119
|
|
|
124
120
|
|
|
@@ -134,7 +130,11 @@ def load_lockfile():
|
|
|
134
130
|
"""Load the command state lockfile."""
|
|
135
131
|
if LOCKFILE_PATH.exists():
|
|
136
132
|
with open(LOCKFILE_PATH, "r") as f:
|
|
137
|
-
|
|
133
|
+
data = json.load(f)
|
|
134
|
+
# Handle both old format (array) and new format (object with "states" key)
|
|
135
|
+
if isinstance(data, dict) and "states" in data:
|
|
136
|
+
return data["states"]
|
|
137
|
+
return data if isinstance(data, list) else []
|
|
138
138
|
return []
|
|
139
139
|
|
|
140
140
|
|
|
@@ -175,7 +175,6 @@ def restore_command_state(hash_value):
|
|
|
175
175
|
@click.group(name="workflow")
|
|
176
176
|
def workflow():
|
|
177
177
|
"""Manage workflows - create, edit, import, export workflow commands."""
|
|
178
|
-
pass
|
|
179
178
|
|
|
180
179
|
|
|
181
180
|
# For backward compatibility, keep commands as an alias
|
|
@@ -397,7 +396,7 @@ def search_commands(query: str, daemon_only: bool, as_json: bool, is_global: boo
|
|
|
397
396
|
@click.option("--json", "as_json", is_flag=True, help="Output as JSON")
|
|
398
397
|
@click.option("--timeout", type=int, help="Execution timeout in seconds")
|
|
399
398
|
def execute_command(command_name: str, args: tuple, as_json: bool, timeout: Optional[int]):
|
|
400
|
-
"""Execute a command by name"""
|
|
399
|
+
"""Execute a command by name."""
|
|
401
400
|
try:
|
|
402
401
|
client = get_daemon_client()
|
|
403
402
|
result = client.execute_command(command_name=command_name, args=list(args), timeout=timeout)
|
|
@@ -425,7 +424,7 @@ def execute_command(command_name: str, args: tuple, as_json: bool, timeout: Opti
|
|
|
425
424
|
@click.argument("command_name")
|
|
426
425
|
@click.option("--json", "as_json", is_flag=True, help="Output as JSON")
|
|
427
426
|
def command_info(command_name: str, as_json: bool):
|
|
428
|
-
"""Show detailed information about a command"""
|
|
427
|
+
"""Show detailed information about a command."""
|
|
429
428
|
try:
|
|
430
429
|
client = get_daemon_client()
|
|
431
430
|
result = client.list_commands(all=True)
|
|
@@ -466,7 +465,7 @@ def command_info(command_name: str, as_json: bool):
|
|
|
466
465
|
console.print(f"Last Executed: {command['last_executed']}")
|
|
467
466
|
|
|
468
467
|
if command.get("code"):
|
|
469
|
-
console.print(
|
|
468
|
+
console.print("\n[bold]Code:[/bold]")
|
|
470
469
|
console.print(f"```{command['language']}")
|
|
471
470
|
console.print(command["code"])
|
|
472
471
|
console.print("```")
|
|
@@ -703,7 +702,7 @@ logger = get_logger()
|
|
|
703
702
|
return None
|
|
704
703
|
finally:
|
|
705
704
|
# Clean up temporary file
|
|
706
|
-
try:
|
|
705
|
+
try: # noqa: SIM105
|
|
707
706
|
os.unlink(temp_file_path)
|
|
708
707
|
except OSError:
|
|
709
708
|
pass
|
|
@@ -807,7 +806,7 @@ def add_command(command_name, group, description, template, language, shell, is_
|
|
|
807
806
|
language = language.lower()
|
|
808
807
|
|
|
809
808
|
# Determine shell type for shell commands
|
|
810
|
-
if language == "shell":
|
|
809
|
+
if language == "shell": # noqa: SIM102
|
|
811
810
|
if not shell:
|
|
812
811
|
# Default to $SHELL environment variable or bash
|
|
813
812
|
shell_env = os.environ.get("SHELL", "/bin/bash")
|
|
@@ -890,7 +889,7 @@ def add_command(command_name, group, description, template, language, shell, is_
|
|
|
890
889
|
)
|
|
891
890
|
else:
|
|
892
891
|
console.print(
|
|
893
|
-
|
|
892
|
+
"[dim]This command is local to this git repository. Use --global/-g to create global commands.[/dim]"
|
|
894
893
|
)
|
|
895
894
|
|
|
896
895
|
return 0
|
|
@@ -988,7 +987,7 @@ def export_commands(target, script, standalone, output, is_global):
|
|
|
988
987
|
return 1
|
|
989
988
|
|
|
990
989
|
# Add standalone wrapper if requested
|
|
991
|
-
if standalone:
|
|
990
|
+
if standalone: # noqa: SIM102
|
|
992
991
|
# Check if already has if __name__ == '__main__'
|
|
993
992
|
if "if __name__" not in code:
|
|
994
993
|
code += "\n\nif __name__ == '__main__':\n app()\n"
|
|
@@ -1153,7 +1152,7 @@ def import_commands(source, script, overwrite, name, group, description, interac
|
|
|
1153
1152
|
try:
|
|
1154
1153
|
tree = ast.parse(code)
|
|
1155
1154
|
description = ast.get_docstring(tree) or f"Imported from {source_path.name}"
|
|
1156
|
-
except:
|
|
1155
|
+
except Exception:
|
|
1157
1156
|
description = f"Imported from {source_path.name}"
|
|
1158
1157
|
|
|
1159
1158
|
# Save as JSON command
|
|
@@ -1199,9 +1198,9 @@ def import_commands(source, script, overwrite, name, group, description, interac
|
|
|
1199
1198
|
f"[yellow]Skipped {failed_count} command(s) (already exist, use --overwrite to replace)[/yellow]"
|
|
1200
1199
|
)
|
|
1201
1200
|
console.print("Skipped commands:")
|
|
1202
|
-
for
|
|
1203
|
-
if not
|
|
1204
|
-
console.print(f" - {
|
|
1201
|
+
for cmd_name, cmd_success in results.items():
|
|
1202
|
+
if not cmd_success:
|
|
1203
|
+
console.print(f" - {cmd_name}")
|
|
1205
1204
|
|
|
1206
1205
|
return 0
|
|
1207
1206
|
|
|
@@ -1357,9 +1356,6 @@ def extract_workflow_commands(output):
|
|
|
1357
1356
|
}
|
|
1358
1357
|
|
|
1359
1358
|
# Create a template based on command type
|
|
1360
|
-
# Replace hyphens with underscores for valid Python function names
|
|
1361
|
-
safe_name = cmd_name.replace("-", "_")
|
|
1362
|
-
|
|
1363
1359
|
if isinstance(cmd_obj, click.Group):
|
|
1364
1360
|
# For groups, create a template
|
|
1365
1361
|
command_info[
|
mcli/app/completion_helpers.py
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
Completion helpers for MCLI that provide tab completion without loading heavy modules
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
|
-
from typing import
|
|
5
|
+
from typing import List
|
|
6
6
|
|
|
7
7
|
import click
|
|
8
8
|
from click.shell_completion import CompletionItem
|
|
@@ -103,7 +103,7 @@ LAZY_COMMAND_COMPLETIONS = {
|
|
|
103
103
|
|
|
104
104
|
|
|
105
105
|
def get_completion_items(cmd_path: List[str], incomplete: str = "") -> List[CompletionItem]:
|
|
106
|
-
"""Get completion items for a given command path without loading modules"""
|
|
106
|
+
"""Get completion items for a given command path without loading modules."""
|
|
107
107
|
items = []
|
|
108
108
|
|
|
109
109
|
# Navigate to the completion data for this path
|
|
@@ -131,7 +131,7 @@ def get_completion_items(cmd_path: List[str], incomplete: str = "") -> List[Comp
|
|
|
131
131
|
|
|
132
132
|
|
|
133
133
|
class CompletionAwareLazyGroup(click.Group):
|
|
134
|
-
"""A Click group that provides completion without loading modules"""
|
|
134
|
+
"""A Click group that provides completion without loading modules."""
|
|
135
135
|
|
|
136
136
|
def __init__(self, name, import_path, *args, **kwargs):
|
|
137
137
|
self.import_path = import_path
|
|
@@ -147,7 +147,7 @@ class CompletionAwareLazyGroup(click.Group):
|
|
|
147
147
|
module_path, attr_name = self.import_path.rsplit(".", 1)
|
|
148
148
|
module = importlib.import_module(module_path)
|
|
149
149
|
self._loaded_group = getattr(module, attr_name)
|
|
150
|
-
except Exception
|
|
150
|
+
except Exception:
|
|
151
151
|
# Return a dummy group that shows an error
|
|
152
152
|
def error_callback():
|
|
153
153
|
click.echo(f"Error: Command group {self.name} is not available")
|
|
@@ -201,5 +201,5 @@ class CompletionAwareLazyGroup(click.Group):
|
|
|
201
201
|
def create_completion_aware_lazy_group(
|
|
202
202
|
name: str, import_path: str, help_text: str = None
|
|
203
203
|
) -> CompletionAwareLazyGroup:
|
|
204
|
-
"""Create a completion-aware lazy group"""
|
|
204
|
+
"""Create a completion-aware lazy group."""
|
|
205
205
|
return CompletionAwareLazyGroup(name, import_path, help=help_text)
|
mcli/app/init_cmd.py
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
"""
|
|
2
2
|
Top-level initialization and teardown commands for MCLI.
|
|
3
3
|
"""
|
|
4
|
+
|
|
4
5
|
import json
|
|
5
6
|
import shutil
|
|
6
7
|
import subprocess
|
|
7
8
|
from datetime import datetime
|
|
8
|
-
from pathlib import Path
|
|
9
9
|
|
|
10
10
|
import click
|
|
11
11
|
from rich.prompt import Prompt
|
|
@@ -56,12 +56,12 @@ def init(is_global, git, force):
|
|
|
56
56
|
lockfile_path = workflows_dir / "commands.lock.json"
|
|
57
57
|
|
|
58
58
|
# Check if already initialized
|
|
59
|
-
if workflows_dir.exists() and not force:
|
|
59
|
+
if workflows_dir.exists() and not force: # noqa: SIM102
|
|
60
60
|
if lockfile_path.exists():
|
|
61
61
|
console.print(
|
|
62
62
|
f"[yellow]Workflows directory already initialized at:[/yellow] {workflows_dir}"
|
|
63
63
|
)
|
|
64
|
-
console.print(
|
|
64
|
+
console.print("[dim]Use --force to reinitialize[/dim]")
|
|
65
65
|
|
|
66
66
|
should_continue = Prompt.ask("Continue anyway?", choices=["y", "n"], default="n")
|
|
67
67
|
if should_continue.lower() != "y":
|
|
@@ -74,7 +74,7 @@ def init(is_global, git, force):
|
|
|
74
74
|
# Create README.md
|
|
75
75
|
readme_path = workflows_dir / "README.md"
|
|
76
76
|
if not readme_path.exists() or force:
|
|
77
|
-
|
|
77
|
+
"local" if in_git_repo else "global"
|
|
78
78
|
scope_desc = f"for repository: {git_root.name}" if in_git_repo else "globally"
|
|
79
79
|
|
|
80
80
|
readme_content = f"""# MCLI Custom Workflows
|
|
@@ -203,13 +203,13 @@ Thumbs.db
|
|
|
203
203
|
.idea/
|
|
204
204
|
"""
|
|
205
205
|
gitignore_path.write_text(gitignore_content)
|
|
206
|
-
console.print(
|
|
206
|
+
console.print("[green]✓[/green] Created .gitignore")
|
|
207
207
|
|
|
208
208
|
# Initialize git if requested
|
|
209
209
|
if git and not (workflows_dir / ".git").exists():
|
|
210
210
|
try:
|
|
211
211
|
subprocess.run(["git", "init"], cwd=workflows_dir, check=True, capture_output=True)
|
|
212
|
-
console.print(
|
|
212
|
+
console.print("[green]✓[/green] Initialized git repository in workflows directory")
|
|
213
213
|
|
|
214
214
|
# Create initial commit
|
|
215
215
|
subprocess.run(["git", "add", "."], cwd=workflows_dir, check=True, capture_output=True)
|
|
@@ -219,12 +219,12 @@ Thumbs.db
|
|
|
219
219
|
check=True,
|
|
220
220
|
capture_output=True,
|
|
221
221
|
)
|
|
222
|
-
console.print(
|
|
222
|
+
console.print("[green]✓[/green] Created initial commit")
|
|
223
223
|
|
|
224
224
|
except subprocess.CalledProcessError as e:
|
|
225
225
|
console.print(f"[yellow]⚠[/yellow] Git initialization failed: {e}")
|
|
226
226
|
except FileNotFoundError:
|
|
227
|
-
console.print(
|
|
227
|
+
console.print("[yellow]⚠[/yellow] Git not found. Skipping git initialization.")
|
|
228
228
|
|
|
229
229
|
# Summary
|
|
230
230
|
from rich.table import Table
|
|
@@ -258,11 +258,11 @@ Thumbs.db
|
|
|
258
258
|
|
|
259
259
|
if in_git_repo:
|
|
260
260
|
console.print(
|
|
261
|
-
|
|
261
|
+
"[dim]Tip: Workflows are local to this repository. Use --global for user-wide workflows.[/dim]"
|
|
262
262
|
)
|
|
263
263
|
else:
|
|
264
264
|
console.print(
|
|
265
|
-
|
|
265
|
+
"[dim]Tip: Use workflows in any git repository, or create local ones with 'mcli init' inside repos.[/dim]"
|
|
266
266
|
)
|
|
267
267
|
|
|
268
268
|
return 0
|
mcli/app/lock_cmd.py
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Top-level lock management commands for MCLI.
|
|
3
3
|
Manages workflow lockfile and verification.
|
|
4
4
|
"""
|
|
5
|
+
|
|
5
6
|
import hashlib
|
|
6
7
|
import json
|
|
7
8
|
from datetime import datetime
|
|
@@ -24,7 +25,11 @@ def load_lockfile():
|
|
|
24
25
|
"""Load the command state lockfile."""
|
|
25
26
|
if LOCKFILE_PATH.exists():
|
|
26
27
|
with open(LOCKFILE_PATH, "r") as f:
|
|
27
|
-
|
|
28
|
+
data = json.load(f)
|
|
29
|
+
# Handle both old format (array) and new format (object with "states" key)
|
|
30
|
+
if isinstance(data, dict) and "states" in data:
|
|
31
|
+
return data["states"]
|
|
32
|
+
return data if isinstance(data, list) else []
|
|
28
33
|
return []
|
|
29
34
|
|
|
30
35
|
|
|
@@ -63,7 +68,7 @@ def restore_command_state(hash_value):
|
|
|
63
68
|
|
|
64
69
|
|
|
65
70
|
def get_current_command_state():
|
|
66
|
-
"""Collect all command metadata (names, groups, etc.)"""
|
|
71
|
+
"""Collect all command metadata (names, groups, etc.)."""
|
|
67
72
|
# Import here to avoid circular imports
|
|
68
73
|
import importlib
|
|
69
74
|
import inspect
|
|
@@ -110,7 +115,7 @@ def get_current_command_state():
|
|
|
110
115
|
streamlit_logger.setLevel(original_level)
|
|
111
116
|
|
|
112
117
|
# Extract command and group objects
|
|
113
|
-
for
|
|
118
|
+
for _name, obj in inspect.getmembers(module):
|
|
114
119
|
# Handle Click commands and groups
|
|
115
120
|
if isinstance(obj, click.Command):
|
|
116
121
|
if isinstance(obj, click.Group):
|
|
@@ -160,7 +165,6 @@ def hash_command_state(commands):
|
|
|
160
165
|
@click.group(name="lock")
|
|
161
166
|
def lock():
|
|
162
167
|
"""Manage workflow lockfile and verification."""
|
|
163
|
-
pass
|
|
164
168
|
|
|
165
169
|
|
|
166
170
|
@lock.command("list")
|
|
@@ -229,11 +233,13 @@ def write_state(json_file):
|
|
|
229
233
|
@click.option(
|
|
230
234
|
"--global", "-g", "is_global", is_flag=True, help="Verify global commands instead of local"
|
|
231
235
|
)
|
|
232
|
-
|
|
236
|
+
@click.option("--code", "-c", is_flag=True, help="Also validate that workflow code is executable")
|
|
237
|
+
def verify_commands(is_global, code):
|
|
233
238
|
"""
|
|
234
|
-
Verify that custom commands match the lockfile.
|
|
239
|
+
Verify that custom commands match the lockfile and optionally validate code.
|
|
235
240
|
|
|
236
241
|
By default verifies local commands (if in git repo), use --global/-g for global commands.
|
|
242
|
+
Use --code/-c to also validate that workflow code is valid and executable.
|
|
237
243
|
"""
|
|
238
244
|
manager = get_command_manager(global_mode=is_global)
|
|
239
245
|
|
|
@@ -242,28 +248,77 @@ def verify_commands(is_global):
|
|
|
242
248
|
|
|
243
249
|
verification = manager.verify_lockfile()
|
|
244
250
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
251
|
+
has_issues = False
|
|
252
|
+
|
|
253
|
+
if not verification["valid"]:
|
|
254
|
+
has_issues = True
|
|
255
|
+
console.print("[yellow]Commands are out of sync with the lockfile:[/yellow]\n")
|
|
256
|
+
|
|
257
|
+
if verification["missing"]:
|
|
258
|
+
console.print("Missing commands (in lockfile but not found):")
|
|
259
|
+
for name in verification["missing"]:
|
|
260
|
+
console.print(f" - {name}")
|
|
261
|
+
|
|
262
|
+
if verification["extra"]:
|
|
263
|
+
console.print("\nExtra commands (not in lockfile):")
|
|
264
|
+
for name in verification["extra"]:
|
|
265
|
+
console.print(f" - {name}")
|
|
266
|
+
|
|
267
|
+
if verification["modified"]:
|
|
268
|
+
console.print("\nModified commands:")
|
|
269
|
+
for name in verification["modified"]:
|
|
270
|
+
console.print(f" - {name}")
|
|
271
|
+
|
|
272
|
+
console.print("\n[dim]Run 'mcli lock update' to sync the lockfile[/dim]\n")
|
|
273
|
+
|
|
274
|
+
# Validate workflow code if requested
|
|
275
|
+
if code:
|
|
276
|
+
console.print("[cyan]Validating workflow code...[/cyan]\n")
|
|
277
|
+
|
|
278
|
+
commands = manager.load_all_commands()
|
|
279
|
+
invalid_workflows = []
|
|
280
|
+
|
|
281
|
+
for cmd_data in commands:
|
|
282
|
+
if cmd_data.get("group") != "workflow":
|
|
283
|
+
continue
|
|
284
|
+
|
|
285
|
+
cmd_name = cmd_data.get("name")
|
|
286
|
+
temp_group = click.Group()
|
|
287
|
+
language = cmd_data.get("language", "python")
|
|
288
|
+
|
|
289
|
+
try:
|
|
290
|
+
if language == "shell":
|
|
291
|
+
success = manager.register_shell_command_with_click(cmd_data, temp_group)
|
|
292
|
+
else:
|
|
293
|
+
success = manager.register_command_with_click(cmd_data, temp_group)
|
|
294
|
+
|
|
295
|
+
if not success or not temp_group.commands.get(cmd_name):
|
|
296
|
+
invalid_workflows.append(
|
|
297
|
+
{"name": cmd_name, "reason": "Code does not define a valid Click command"}
|
|
298
|
+
)
|
|
299
|
+
except SyntaxError as e:
|
|
300
|
+
invalid_workflows.append({"name": cmd_name, "reason": f"Syntax error: {e}"})
|
|
301
|
+
except Exception as e:
|
|
302
|
+
invalid_workflows.append({"name": cmd_name, "reason": f"Failed to load: {e}"})
|
|
303
|
+
|
|
304
|
+
if invalid_workflows:
|
|
305
|
+
has_issues = True
|
|
306
|
+
console.print("[red]Invalid workflows found:[/red]\n")
|
|
307
|
+
|
|
308
|
+
for item in invalid_workflows:
|
|
309
|
+
console.print(f" [red]✗[/red] {item['name']}")
|
|
310
|
+
console.print(f" [dim]{item['reason']}[/dim]")
|
|
311
|
+
|
|
312
|
+
console.print("\n[yellow]Fix with:[/yellow] mcli workflow edit <workflow-name>")
|
|
313
|
+
console.print(
|
|
314
|
+
"[dim]Tip: Workflow code must define a Click command decorated with @click.command()[/dim]\n"
|
|
315
|
+
)
|
|
316
|
+
else:
|
|
317
|
+
console.print("[green]✓ All workflow code is valid[/green]\n")
|
|
265
318
|
|
|
266
|
-
|
|
319
|
+
if not has_issues:
|
|
320
|
+
console.print("[green]✓ All custom commands are verified[/green]")
|
|
321
|
+
return 0
|
|
267
322
|
|
|
268
323
|
return 1
|
|
269
324
|
|
mcli/app/main.py
CHANGED
|
@@ -1,19 +1,13 @@
|
|
|
1
|
-
import functools
|
|
2
1
|
import importlib
|
|
3
|
-
import inspect
|
|
4
2
|
import os
|
|
5
|
-
import platform
|
|
6
|
-
import sys
|
|
7
|
-
from functools import lru_cache
|
|
8
|
-
from importlib.metadata import metadata, version
|
|
9
3
|
from pathlib import Path
|
|
10
4
|
from typing import List, Optional
|
|
11
5
|
|
|
12
6
|
import click
|
|
13
7
|
import tomli
|
|
14
8
|
|
|
9
|
+
from mcli.lib.api.api import register_command_as_api
|
|
15
10
|
from mcli.lib.logger.logger import disable_runtime_tracing, enable_runtime_tracing, get_logger
|
|
16
|
-
from mcli.lib.ui.styling import info, success
|
|
17
11
|
|
|
18
12
|
# Defer performance optimizations until needed
|
|
19
13
|
_optimization_results = None
|
|
@@ -78,7 +72,7 @@ def discover_modules(base_path: Path, config_path: Optional[Path] = None) -> Lis
|
|
|
78
72
|
logger.warning(f"Error reading config file {config_path}: {e}")
|
|
79
73
|
config = {"paths": {"included_dirs": ["app", "self", "workflow", "public"]}}
|
|
80
74
|
else:
|
|
81
|
-
logger.warning(
|
|
75
|
+
logger.warning("Config file not found, using default configuration")
|
|
82
76
|
config = {"paths": {"included_dirs": ["app", "self", "workflow", "public"]}}
|
|
83
77
|
excluded_files = {"setup.py", "__init__.py"}
|
|
84
78
|
excluded_dirs = {"resources", "models", "scripts", "private", "venv", ".venv", "__pycache__"}
|
mcli/app/model/model.py
CHANGED
|
@@ -2,14 +2,10 @@ import base64
|
|
|
2
2
|
import json
|
|
3
3
|
import os
|
|
4
4
|
import queue
|
|
5
|
-
import shutil
|
|
6
|
-
import sys
|
|
7
|
-
import tempfile
|
|
8
5
|
import threading
|
|
9
6
|
import time
|
|
10
7
|
import uuid
|
|
11
|
-
from
|
|
12
|
-
from typing import Any, Dict, List, Optional, Tuple
|
|
8
|
+
from typing import Any, Dict, List, Tuple
|
|
13
9
|
|
|
14
10
|
import click
|
|
15
11
|
import cv2
|
|
@@ -323,7 +319,7 @@ class ComfyUIClient:
|
|
|
323
319
|
"subfolder": "v2v_input", # Custom subfolder for our workflow
|
|
324
320
|
"type": "input",
|
|
325
321
|
}
|
|
326
|
-
|
|
322
|
+
self.session.post(f"{self.api_url}/upload/image", json=data)
|
|
327
323
|
return filename, "v2v_input"
|
|
328
324
|
|
|
329
325
|
def wait_for_prompt(self, prompt_id: str) -> Dict[str, Any]:
|
|
@@ -418,7 +414,7 @@ class ComfyUIClient:
|
|
|
418
414
|
|
|
419
415
|
# Get the output image
|
|
420
416
|
output_node = None
|
|
421
|
-
for
|
|
417
|
+
for _node_id, node_output in result["outputs"].items():
|
|
422
418
|
if "images" in node_output:
|
|
423
419
|
output_node = node_output
|
|
424
420
|
break
|
|
@@ -678,7 +674,7 @@ class VideoToVideoGenerator:
|
|
|
678
674
|
with click.progressbar(
|
|
679
675
|
enumerate(frame_paths), length=len(frame_paths), label="Processing frames"
|
|
680
676
|
) as bar:
|
|
681
|
-
for
|
|
677
|
+
for _i, frame_path in bar:
|
|
682
678
|
processed_frame_path = self.comfyui_client.process_frame(
|
|
683
679
|
frame_path=frame_path,
|
|
684
680
|
prompt=prompt,
|
|
@@ -795,7 +791,6 @@ class VideoToVideoGenerator:
|
|
|
795
791
|
@click.group(name="model")
|
|
796
792
|
def model():
|
|
797
793
|
"""Video-to-video generation workflow using ComfyUI and Hunyuan video models."""
|
|
798
|
-
pass
|
|
799
794
|
|
|
800
795
|
|
|
801
796
|
@model.command()
|
|
@@ -1012,7 +1007,7 @@ def check_comfyui():
|
|
|
1012
1007
|
|
|
1013
1008
|
# Check for hunyuan models
|
|
1014
1009
|
hunyuan_found = False
|
|
1015
|
-
for
|
|
1010
|
+
for _model_type, models in models_info.items():
|
|
1016
1011
|
for model in models:
|
|
1017
1012
|
if "hunyuan" in model.lower():
|
|
1018
1013
|
hunyuan_found = True
|
mcli/app/store_cmd.py
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Top-level store management commands for MCLI.
|
|
3
3
|
Manages command store - sync ~/.mcli/commands/ to git.
|
|
4
4
|
"""
|
|
5
|
+
|
|
5
6
|
import shutil
|
|
6
7
|
import subprocess
|
|
7
8
|
from datetime import datetime
|
|
@@ -20,7 +21,7 @@ COMMANDS_PATH = Path.home() / ".mcli" / "commands"
|
|
|
20
21
|
|
|
21
22
|
|
|
22
23
|
def _get_store_path() -> Path:
|
|
23
|
-
"""Get store path from config or default"""
|
|
24
|
+
"""Get store path from config or default."""
|
|
24
25
|
config_file = Path.home() / ".mcli" / "store.conf"
|
|
25
26
|
|
|
26
27
|
if config_file.exists():
|
|
@@ -34,15 +35,14 @@ def _get_store_path() -> Path:
|
|
|
34
35
|
|
|
35
36
|
@click.group(name="store")
|
|
36
37
|
def store():
|
|
37
|
-
"""Manage command store - sync ~/.mcli/commands/ to git"""
|
|
38
|
-
pass
|
|
38
|
+
"""Manage command store - sync ~/.mcli/commands/ to git."""
|
|
39
39
|
|
|
40
40
|
|
|
41
41
|
@store.command(name="init")
|
|
42
42
|
@click.option("--path", "-p", type=click.Path(), help=f"Store path (default: {DEFAULT_STORE_PATH})")
|
|
43
43
|
@click.option("--remote", "-r", help="Git remote URL (optional)")
|
|
44
44
|
def init_store(path, remote):
|
|
45
|
-
"""Initialize command store with git"""
|
|
45
|
+
"""Initialize command store with git."""
|
|
46
46
|
store_path = Path(path) if path else DEFAULT_STORE_PATH
|
|
47
47
|
|
|
48
48
|
try:
|
|
@@ -305,7 +305,7 @@ def sync_commands(message, is_global):
|
|
|
305
305
|
|
|
306
306
|
@store.command(name="status")
|
|
307
307
|
def store_status():
|
|
308
|
-
"""Show git status of command store"""
|
|
308
|
+
"""Show git status of command store."""
|
|
309
309
|
try:
|
|
310
310
|
store_path = _get_store_path()
|
|
311
311
|
|
|
@@ -341,7 +341,7 @@ def store_status():
|
|
|
341
341
|
@click.option("--remote", "-r", help="Set git remote URL")
|
|
342
342
|
@click.option("--path", "-p", type=click.Path(), help="Change store path")
|
|
343
343
|
def configure_store(remote, path):
|
|
344
|
-
"""Configure store settings"""
|
|
344
|
+
"""Configure store settings."""
|
|
345
345
|
try:
|
|
346
346
|
store_path = _get_store_path()
|
|
347
347
|
|
|
@@ -377,7 +377,7 @@ def configure_store(remote, path):
|
|
|
377
377
|
@store.command(name="list")
|
|
378
378
|
@click.option("--store-dir", "-s", is_flag=True, help="List store instead of local")
|
|
379
379
|
def list_commands(store_dir):
|
|
380
|
-
"""List all commands"""
|
|
380
|
+
"""List all commands."""
|
|
381
381
|
try:
|
|
382
382
|
if store_dir:
|
|
383
383
|
store_path = _get_store_path()
|
|
@@ -421,7 +421,7 @@ def list_commands(store_dir):
|
|
|
421
421
|
@click.argument("command_name")
|
|
422
422
|
@click.option("--store-dir", "-s", is_flag=True, help="Show from store instead of local")
|
|
423
423
|
def show_command(command_name, store_dir):
|
|
424
|
-
"""Show command file contents"""
|
|
424
|
+
"""Show command file contents."""
|
|
425
425
|
try:
|
|
426
426
|
if store_dir:
|
|
427
427
|
store_path = _get_store_path()
|
mcli/app/video/__init__.py
CHANGED
mcli/app/video/video.py
CHANGED
|
@@ -1,23 +1,11 @@
|
|
|
1
|
-
import base64
|
|
2
|
-
import json
|
|
3
1
|
import os
|
|
4
|
-
import queue
|
|
5
|
-
import shutil
|
|
6
|
-
import sys
|
|
7
|
-
import tempfile
|
|
8
|
-
import threading
|
|
9
|
-
import time
|
|
10
|
-
import uuid
|
|
11
|
-
from pathlib import Path
|
|
12
2
|
from typing import Any, Dict, List, Optional, Tuple
|
|
13
3
|
|
|
14
4
|
import click
|
|
15
5
|
import cv2
|
|
16
6
|
import numpy as np
|
|
17
|
-
import requests
|
|
18
7
|
from PIL import Image
|
|
19
|
-
from
|
|
20
|
-
from skimage import feature, morphology, restoration
|
|
8
|
+
from skimage import morphology
|
|
21
9
|
|
|
22
10
|
# Add this to your existing CONFIG
|
|
23
11
|
CONFIG = {"temp_dir": "./temp", "output_dir": "./output"}
|
|
@@ -1105,7 +1093,6 @@ def remove_overlay(input_video, output, fps, output_fps, context, method):
|
|
|
1105
1093
|
@click.group()
|
|
1106
1094
|
def main():
|
|
1107
1095
|
"""Advanced video overlay removal tool with intelligent content reconstruction."""
|
|
1108
|
-
pass
|
|
1109
1096
|
|
|
1110
1097
|
|
|
1111
1098
|
main.add_command(remove_overlay)
|