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.
- mcli/app/__init__.py +0 -2
- mcli/app/commands_cmd.py +30 -26
- mcli/app/completion_helpers.py +5 -5
- mcli/app/init_cmd.py +10 -10
- mcli/app/lock_cmd.py +29 -24
- 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.2.dist-info → mcli_framework-7.12.4.dist-info}/METADATA +1 -1
- mcli_framework-7.12.4.dist-info/RECORD +279 -0
- mcli_framework-7.12.2.dist-info/RECORD +0 -279
- {mcli_framework-7.12.2.dist-info → mcli_framework-7.12.4.dist-info}/WHEEL +0 -0
- {mcli_framework-7.12.2.dist-info → mcli_framework-7.12.4.dist-info}/entry_points.txt +0 -0
- {mcli_framework-7.12.2.dist-info → mcli_framework-7.12.4.dist-info}/licenses/LICENSE +0 -0
- {mcli_framework-7.12.2.dist-info → mcli_framework-7.12.4.dist-info}/top_level.txt +0 -0
mcli/lib/api/api.py
CHANGED
|
@@ -1,20 +1,15 @@
|
|
|
1
1
|
import functools
|
|
2
2
|
import inspect
|
|
3
|
-
import json
|
|
4
3
|
import os
|
|
5
4
|
import random
|
|
6
5
|
import socket
|
|
7
|
-
import sys
|
|
8
6
|
import threading
|
|
9
7
|
import time
|
|
10
|
-
from contextlib import asynccontextmanager
|
|
11
8
|
from pathlib import Path
|
|
12
9
|
from typing import Any, Callable, Dict, List, Optional, Union
|
|
13
10
|
|
|
14
|
-
import click
|
|
15
|
-
import requests
|
|
16
11
|
import uvicorn
|
|
17
|
-
from fastapi import FastAPI
|
|
12
|
+
from fastapi import FastAPI
|
|
18
13
|
from fastapi.middleware.cors import CORSMiddleware
|
|
19
14
|
from pydantic import BaseModel, Field
|
|
20
15
|
|
|
@@ -228,7 +223,7 @@ class ClickToAPIDecorator:
|
|
|
228
223
|
fields = {}
|
|
229
224
|
|
|
230
225
|
for param_name, param in sig.parameters.items():
|
|
231
|
-
if param_name in ["
|
|
226
|
+
if param_name in ["sel", "ctx"]:
|
|
232
227
|
continue
|
|
233
228
|
|
|
234
229
|
# Get parameter type and default
|
|
@@ -243,7 +238,7 @@ class ClickToAPIDecorator:
|
|
|
243
238
|
# Handle boolean flags
|
|
244
239
|
param_type = bool
|
|
245
240
|
elif param_type in [int, float]:
|
|
246
|
-
param_type = param_type
|
|
241
|
+
param_type = param_type # noqa: SIM909
|
|
247
242
|
else:
|
|
248
243
|
param_type = str
|
|
249
244
|
|
|
@@ -544,7 +539,7 @@ def register_command_as_api(
|
|
|
544
539
|
logger.debug("API app not available, skipping endpoint registration")
|
|
545
540
|
return
|
|
546
541
|
|
|
547
|
-
logger.info(
|
|
542
|
+
logger.info("API app available, proceeding with registration")
|
|
548
543
|
|
|
549
544
|
decorator = ClickToAPIDecorator(
|
|
550
545
|
endpoint_path=endpoint_path,
|
mcli/lib/api/daemon_client.py
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import json
|
|
2
1
|
import time
|
|
3
2
|
from dataclasses import dataclass
|
|
4
3
|
from pathlib import Path
|
|
@@ -14,7 +13,7 @@ logger = get_logger(__name__)
|
|
|
14
13
|
|
|
15
14
|
@dataclass
|
|
16
15
|
class DaemonClientConfig:
|
|
17
|
-
"""Configuration for daemon client"""
|
|
16
|
+
"""Configuration for daemon client."""
|
|
18
17
|
|
|
19
18
|
host: str = "localhost"
|
|
20
19
|
port: int = 8000
|
|
@@ -24,7 +23,7 @@ class DaemonClientConfig:
|
|
|
24
23
|
|
|
25
24
|
|
|
26
25
|
class APIDaemonClient:
|
|
27
|
-
"""Client for interacting with the MCLI API Daemon or Flask shell daemon"""
|
|
26
|
+
"""Client for interacting with the MCLI API Daemon or Flask shell daemon."""
|
|
28
27
|
|
|
29
28
|
def __init__(self, config: Optional[DaemonClientConfig] = None, shell_mode: bool = False):
|
|
30
29
|
self.config = config or self._load_config()
|
|
@@ -37,8 +36,8 @@ class APIDaemonClient:
|
|
|
37
36
|
self.session = requests.Session()
|
|
38
37
|
|
|
39
38
|
def execute_shell_command(self, command: str) -> Dict[str, Any]:
|
|
40
|
-
"""Execute a raw shell command via the Flask test server"""
|
|
41
|
-
url =
|
|
39
|
+
"""Execute a raw shell command via the Flask test server."""
|
|
40
|
+
url = "http://localhost:5005/execute"
|
|
42
41
|
try:
|
|
43
42
|
response = self.session.post(
|
|
44
43
|
url, json={"command": command}, timeout=self.config.timeout
|
|
@@ -49,7 +48,7 @@ class APIDaemonClient:
|
|
|
49
48
|
raise Exception(f"Failed to connect to Flask shell daemon at {url}: {e}")
|
|
50
49
|
|
|
51
50
|
def _load_config(self) -> DaemonClientConfig:
|
|
52
|
-
"""Load configuration from config files"""
|
|
51
|
+
"""Load configuration from config files."""
|
|
53
52
|
config = DaemonClientConfig()
|
|
54
53
|
|
|
55
54
|
# Try to load from config.toml files
|
|
@@ -83,7 +82,7 @@ class APIDaemonClient:
|
|
|
83
82
|
params: Optional[Dict] = None,
|
|
84
83
|
**kwargs,
|
|
85
84
|
) -> Dict[str, Any]:
|
|
86
|
-
"""Make HTTP request to daemon with retry logic"""
|
|
85
|
+
"""Make HTTP request to daemon with retry logic."""
|
|
87
86
|
url = f"{self.base_url}{endpoint}"
|
|
88
87
|
for attempt in range(self.config.retry_attempts):
|
|
89
88
|
try:
|
|
@@ -107,11 +106,11 @@ class APIDaemonClient:
|
|
|
107
106
|
return {} # Always return a dict
|
|
108
107
|
|
|
109
108
|
def health_check(self) -> Dict[str, Any]:
|
|
110
|
-
"""Check daemon health"""
|
|
109
|
+
"""Check daemon health."""
|
|
111
110
|
return self._make_request("GET", "/health")
|
|
112
111
|
|
|
113
112
|
def status(self) -> Dict[str, Any]:
|
|
114
|
-
"""Get daemon status"""
|
|
113
|
+
"""Get daemon status."""
|
|
115
114
|
return self._make_request("GET", "/status")
|
|
116
115
|
|
|
117
116
|
def list_commands(self, all: bool = False) -> Dict[str, Any]:
|
|
@@ -120,7 +119,7 @@ class APIDaemonClient:
|
|
|
120
119
|
return self._make_request("GET", "/commands", params=params)
|
|
121
120
|
|
|
122
121
|
def get_command_details(self, command_id: str) -> Optional[Dict[str, Any]]:
|
|
123
|
-
"""Get detailed information about a specific command"""
|
|
122
|
+
"""Get detailed information about a specific command."""
|
|
124
123
|
return self._make_request("GET", f"/commands/{command_id}")
|
|
125
124
|
|
|
126
125
|
def execute_command(
|
|
@@ -130,7 +129,7 @@ class APIDaemonClient:
|
|
|
130
129
|
args: Optional[List[str]] = None,
|
|
131
130
|
timeout: Optional[int] = None,
|
|
132
131
|
) -> Dict[str, Any]:
|
|
133
|
-
"""Execute a command via the daemon"""
|
|
132
|
+
"""Execute a command via the daemon."""
|
|
134
133
|
if not command_id and not command_name:
|
|
135
134
|
raise ValueError("Either command_id or command_name must be provided")
|
|
136
135
|
|
|
@@ -146,15 +145,15 @@ class APIDaemonClient:
|
|
|
146
145
|
return self._make_request("POST", "/execute", data=data)
|
|
147
146
|
|
|
148
147
|
def start_daemon(self) -> Dict[str, Any]:
|
|
149
|
-
"""Start the daemon via HTTP"""
|
|
148
|
+
"""Start the daemon via HTTP."""
|
|
150
149
|
return self._make_request("POST", "/daemon/start")
|
|
151
150
|
|
|
152
151
|
def stop_daemon(self) -> Dict[str, Any]:
|
|
153
|
-
"""Stop the daemon via HTTP"""
|
|
152
|
+
"""Stop the daemon via HTTP."""
|
|
154
153
|
return self._make_request("POST", "/daemon/stop")
|
|
155
154
|
|
|
156
155
|
def is_running(self) -> bool:
|
|
157
|
-
"""Check if daemon is running"""
|
|
156
|
+
"""Check if daemon is running."""
|
|
158
157
|
try:
|
|
159
158
|
status = self.status()
|
|
160
159
|
return status.get("running", False)
|
|
@@ -162,7 +161,7 @@ class APIDaemonClient:
|
|
|
162
161
|
return False
|
|
163
162
|
|
|
164
163
|
def wait_for_daemon(self, timeout: int = 30) -> bool:
|
|
165
|
-
"""Wait for daemon to be ready"""
|
|
164
|
+
"""Wait for daemon to be ready."""
|
|
166
165
|
start_time = time.time()
|
|
167
166
|
while time.time() - start_time < timeout:
|
|
168
167
|
if self.is_running():
|
|
@@ -173,31 +172,31 @@ class APIDaemonClient:
|
|
|
173
172
|
|
|
174
173
|
# Convenience functions for easy usage
|
|
175
174
|
def execute_shell_command_via_flask(command: str) -> Dict[str, Any]:
|
|
176
|
-
"""Execute a raw shell command via the Flask test server (convenience function)"""
|
|
175
|
+
"""Execute a raw shell command via the Flask test server (convenience function)."""
|
|
177
176
|
client = APIDaemonClient(shell_mode=True)
|
|
178
177
|
return client.execute_shell_command(command)
|
|
179
178
|
|
|
180
179
|
|
|
181
180
|
def get_daemon_client() -> APIDaemonClient:
|
|
182
|
-
"""Get a configured daemon client"""
|
|
181
|
+
"""Get a configured daemon client."""
|
|
183
182
|
return APIDaemonClient()
|
|
184
183
|
|
|
185
184
|
|
|
186
185
|
def execute_command_via_daemon(
|
|
187
186
|
command_name: str, args: Optional[List[str]] = None
|
|
188
187
|
) -> Dict[str, Any]:
|
|
189
|
-
"""Execute a command via the daemon (convenience function)"""
|
|
188
|
+
"""Execute a command via the daemon (convenience function)."""
|
|
190
189
|
client = get_daemon_client()
|
|
191
190
|
return client.execute_command(command_name=command_name, args=args)
|
|
192
191
|
|
|
193
192
|
|
|
194
193
|
def check_daemon_status() -> Dict[str, Any]:
|
|
195
|
-
"""Check daemon status (convenience function)"""
|
|
194
|
+
"""Check daemon status (convenience function)."""
|
|
196
195
|
client = get_daemon_client()
|
|
197
196
|
return client.status()
|
|
198
197
|
|
|
199
198
|
|
|
200
199
|
def list_available_commands() -> Dict[str, Any]:
|
|
201
|
-
"""List available commands (convenience function)"""
|
|
200
|
+
"""List available commands (convenience function)."""
|
|
202
201
|
client = get_daemon_client()
|
|
203
202
|
return client.list_commands()
|
|
@@ -1,16 +1,14 @@
|
|
|
1
1
|
import json
|
|
2
2
|
import subprocess
|
|
3
|
-
from pathlib import Path
|
|
4
3
|
from typing import Any, Dict, List, Optional
|
|
5
4
|
|
|
6
5
|
from mcli.lib.logger.logger import get_logger
|
|
7
|
-
from mcli.lib.toml.toml import read_from_toml
|
|
8
6
|
|
|
9
7
|
logger = get_logger(__name__)
|
|
10
8
|
|
|
11
9
|
|
|
12
10
|
class LocalDaemonClient:
|
|
13
|
-
"""Client for interacting with the MCLI Daemon via CLI subprocess (local IPC)"""
|
|
11
|
+
"""Client for interacting with the MCLI Daemon via CLI subprocess (local IPC)."""
|
|
14
12
|
|
|
15
13
|
def __init__(self):
|
|
16
14
|
self.daemon_cmd = ["python", "-m", "mcli.workflow.daemon.daemon"]
|
mcli/lib/api/daemon_decorator.py
CHANGED
|
@@ -7,7 +7,7 @@ from typing import Any, Callable, Dict, List, Optional
|
|
|
7
7
|
from mcli.lib.logger.logger import get_logger
|
|
8
8
|
from mcli.lib.toml.toml import read_from_toml
|
|
9
9
|
|
|
10
|
-
from .daemon_client import
|
|
10
|
+
from .daemon_client import get_daemon_client
|
|
11
11
|
|
|
12
12
|
logger = get_logger(__name__)
|
|
13
13
|
|
|
@@ -77,7 +77,7 @@ def daemon_command(
|
|
|
77
77
|
|
|
78
78
|
|
|
79
79
|
def _is_daemon_routing_enabled() -> bool:
|
|
80
|
-
"""Check if daemon routing is enabled in configuration"""
|
|
80
|
+
"""Check if daemon routing is enabled in configuration."""
|
|
81
81
|
# Check environment variable
|
|
82
82
|
if os.environ.get("MCLI_DAEMON_ROUTING", "false").lower() in ("true", "1", "yes"):
|
|
83
83
|
return True
|
|
@@ -102,7 +102,7 @@ def _is_daemon_routing_enabled() -> bool:
|
|
|
102
102
|
|
|
103
103
|
|
|
104
104
|
def _convert_to_command_args(args: tuple, kwargs: dict, func: Callable) -> List[str]:
|
|
105
|
-
"""Convert function arguments to command line arguments"""
|
|
105
|
+
"""Convert function arguments to command line arguments."""
|
|
106
106
|
cmd_args = []
|
|
107
107
|
|
|
108
108
|
# Get function signature
|
|
@@ -140,7 +140,7 @@ def _convert_to_command_args(args: tuple, kwargs: dict, func: Callable) -> List[
|
|
|
140
140
|
# *args
|
|
141
141
|
if isinstance(param_value, (list, tuple)):
|
|
142
142
|
cmd_args.extend([str(arg) for arg in param_value])
|
|
143
|
-
elif param.kind == inspect.Parameter.VAR_KEYWORD:
|
|
143
|
+
elif param.kind == inspect.Parameter.VAR_KEYWORD: # noqa: SIM102
|
|
144
144
|
# **kwargs
|
|
145
145
|
if isinstance(param_value, dict):
|
|
146
146
|
for key, value in param_value.items():
|
|
@@ -199,7 +199,7 @@ def daemon_group(
|
|
|
199
199
|
|
|
200
200
|
# Convenience function to check if daemon is available
|
|
201
201
|
def is_daemon_available() -> bool:
|
|
202
|
-
"""Check if the API daemon is available and running"""
|
|
202
|
+
"""Check if the API daemon is available and running."""
|
|
203
203
|
try:
|
|
204
204
|
client = get_daemon_client()
|
|
205
205
|
return client.is_running()
|
|
@@ -209,7 +209,7 @@ def is_daemon_available() -> bool:
|
|
|
209
209
|
|
|
210
210
|
# Convenience function to get daemon status
|
|
211
211
|
def get_daemon_status() -> Optional[Dict[str, Any]]:
|
|
212
|
-
"""Get daemon status if available"""
|
|
212
|
+
"""Get daemon status if available."""
|
|
213
213
|
try:
|
|
214
214
|
client = get_daemon_client()
|
|
215
215
|
return client.status()
|
mcli/lib/api/mcli_decorators.py
CHANGED
|
@@ -6,17 +6,13 @@ adding API endpoints and background processing capabilities while maintaining
|
|
|
6
6
|
the familiar Click interface. Users only need to import mcli and get everything.
|
|
7
7
|
"""
|
|
8
8
|
|
|
9
|
-
import functools
|
|
10
|
-
import inspect
|
|
11
9
|
import os
|
|
12
10
|
import time
|
|
13
|
-
from pathlib import Path
|
|
14
11
|
from typing import Any, Callable, Dict, List, Optional, Union
|
|
15
12
|
|
|
16
13
|
import click
|
|
17
14
|
|
|
18
15
|
from mcli.lib.logger.logger import get_logger
|
|
19
|
-
from mcli.lib.toml.toml import read_from_toml
|
|
20
16
|
|
|
21
17
|
from .api import api_endpoint as _api_endpoint
|
|
22
18
|
from .api import get_api_app, start_api_server, stop_api_server
|
|
@@ -590,7 +586,7 @@ def get_network_credentials():
|
|
|
590
586
|
|
|
591
587
|
# Re-export Click types and classes
|
|
592
588
|
def _get_click_types():
|
|
593
|
-
"""Get Click types for re-export"""
|
|
589
|
+
"""Get Click types for re-export."""
|
|
594
590
|
import click
|
|
595
591
|
|
|
596
592
|
return {
|
|
@@ -950,14 +946,14 @@ def status_check():
|
|
|
950
946
|
|
|
951
947
|
|
|
952
948
|
class ChatCommandGroup(click.Group):
|
|
953
|
-
"""Special command group that provides chat-based interaction"""
|
|
949
|
+
"""Special command group that provides chat-based interaction."""
|
|
954
950
|
|
|
955
951
|
def __init__(self, *args, **kwargs):
|
|
956
952
|
super().__init__(*args, **kwargs)
|
|
957
953
|
self.chat_client = None
|
|
958
954
|
|
|
959
955
|
def get_help(self, ctx):
|
|
960
|
-
"""Start interactive chat session instead of showing normal help"""
|
|
956
|
+
"""Start interactive chat session instead of showing normal help."""
|
|
961
957
|
from mcli.chat.chat import ChatClient
|
|
962
958
|
|
|
963
959
|
self.chat_client = ChatClient()
|
|
@@ -969,7 +965,7 @@ from typing import Any, Callable
|
|
|
969
965
|
|
|
970
966
|
|
|
971
967
|
def chat(**kwargs) -> Callable[[Callable[..., Any]], click.Group]:
|
|
972
|
-
"""Create a chat command group that provides an interactive LLM-powered interface"""
|
|
968
|
+
"""Create a chat command group that provides an interactive LLM-powered interface."""
|
|
973
969
|
kwargs.setdefault("invoke_without_command", True)
|
|
974
970
|
kwargs.setdefault("no_args_is_help", False)
|
|
975
971
|
return click.group(cls=ChatCommandGroup, **kwargs)
|
mcli/lib/auth/__init__.py
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
from .auth import *
|
mcli/lib/auth/auth.py
CHANGED
|
@@ -6,7 +6,7 @@ from .token_manager import TokenManager
|
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
def configure():
|
|
9
|
-
"""Configure authentication token"""
|
|
9
|
+
"""Configure authentication token."""
|
|
10
10
|
token_manager = TokenManager()
|
|
11
11
|
|
|
12
12
|
# Prompt for token, hiding input
|
|
@@ -25,7 +25,7 @@ def configure():
|
|
|
25
25
|
|
|
26
26
|
|
|
27
27
|
def show_token():
|
|
28
|
-
"""Display the current authentication token"""
|
|
28
|
+
"""Display the current authentication token."""
|
|
29
29
|
token_manager = TokenManager()
|
|
30
30
|
token = token_manager.get_token()
|
|
31
31
|
|
|
@@ -36,7 +36,7 @@ def show_token():
|
|
|
36
36
|
|
|
37
37
|
|
|
38
38
|
def clear_token():
|
|
39
|
-
"""Clear the stored authentication token"""
|
|
39
|
+
"""Clear the stored authentication token."""
|
|
40
40
|
token_manager = TokenManager()
|
|
41
41
|
|
|
42
42
|
if click.confirm("Are you sure you want to clear the stored token?"):
|
|
@@ -77,8 +77,7 @@ def get_mcli_basic_auth() -> Optional[str]:
|
|
|
77
77
|
|
|
78
78
|
@click.group(name="auth")
|
|
79
79
|
def auth():
|
|
80
|
-
"""Authentication commands"""
|
|
81
|
-
pass
|
|
80
|
+
"""Authentication commands."""
|
|
82
81
|
|
|
83
82
|
|
|
84
83
|
if __name__ == "__main__":
|
mcli/lib/auth/mcli_manager.py
CHANGED
|
@@ -1,11 +1,6 @@
|
|
|
1
1
|
import base64
|
|
2
|
-
import json
|
|
3
|
-
import time
|
|
4
|
-
from pathlib import Path
|
|
5
2
|
from urllib.request import urlopen
|
|
6
3
|
|
|
7
|
-
import click
|
|
8
|
-
|
|
9
4
|
from mcli.lib.logger.logger import get_logger
|
|
10
5
|
|
|
11
6
|
logger = get_logger(__name__)
|
|
@@ -20,24 +15,24 @@ class MCLIManager:
|
|
|
20
15
|
self.env_url = env_url
|
|
21
16
|
|
|
22
17
|
def create_mcli_basic_auth_token(self):
|
|
23
|
-
"""Create a basic auth token for MCLI authentication"""
|
|
18
|
+
"""Create a basic auth token for MCLI authentication."""
|
|
24
19
|
basic_content_bytes = "BA:BA".encode("ASCII")
|
|
25
20
|
basic_token_b64 = base64.b64encode(basic_content_bytes).decode("ASCII")
|
|
26
21
|
return basic_token_b64
|
|
27
22
|
|
|
28
23
|
def create_mcli_basic_auth_header(self, token: str):
|
|
29
|
-
"""Create a basic auth header for MCLI authentication"""
|
|
24
|
+
"""Create a basic auth header for MCLI authentication."""
|
|
30
25
|
return "Basic " + token
|
|
31
26
|
|
|
32
27
|
def mcli_as_dev_user(self, url, authHeader):
|
|
33
|
-
"""Connect to MCLI as a dev user"""
|
|
28
|
+
"""Connect to MCLI as a dev user."""
|
|
34
29
|
src = urlopen(url + "/remote/mcli.py").read()
|
|
35
30
|
exec_scope = {}
|
|
36
31
|
exec(src, exec_scope)
|
|
37
32
|
return exec_scope["get_mcli"](url=url, authz=authHeader)
|
|
38
33
|
|
|
39
34
|
def mcli_as_basic_user(self):
|
|
40
|
-
"""Connect to MCLI as a BA user"""
|
|
35
|
+
"""Connect to MCLI as a BA user."""
|
|
41
36
|
url = self.env_url
|
|
42
37
|
token = self.create_mcli_basic_auth_token()
|
|
43
38
|
basicAuthHeader = self.create_mcli_basic_auth_header(token)
|
|
@@ -46,7 +41,7 @@ class MCLIManager:
|
|
|
46
41
|
|
|
47
42
|
|
|
48
43
|
class MCLIConnectionParams:
|
|
49
|
-
"""A picklable class to store MCLI connection parameters"""
|
|
44
|
+
"""A picklable class to store MCLI connection parameters."""
|
|
50
45
|
|
|
51
46
|
def __init__(self, url, auth_token):
|
|
52
47
|
self.url = url
|
|
@@ -60,7 +55,7 @@ class MCLIInstance:
|
|
|
60
55
|
self._mcli = self._initialize_mcli()
|
|
61
56
|
|
|
62
57
|
def _normalize_url(self, url):
|
|
63
|
-
"""Normalize URL to ensure it's properly formatted"""
|
|
58
|
+
"""Normalize URL to ensure it's properly formatted."""
|
|
64
59
|
if not url:
|
|
65
60
|
raise ValueError("URL cannot be empty")
|
|
66
61
|
|
|
@@ -71,7 +66,7 @@ class MCLIInstance:
|
|
|
71
66
|
return url.rstrip("/")
|
|
72
67
|
|
|
73
68
|
def _initialize_mcli(self):
|
|
74
|
-
"""Initialize the MCLI connection"""
|
|
69
|
+
"""Initialize the MCLI connection."""
|
|
75
70
|
mcli_url = f"{self.url}/remote/mcli.py"
|
|
76
71
|
|
|
77
72
|
try:
|
mcli/lib/auth/token_util.py
CHANGED
|
@@ -69,7 +69,7 @@ def execute(action, args):
|
|
|
69
69
|
|
|
70
70
|
elif action == "PROVISION_THIRDPARTY":
|
|
71
71
|
ensure_directory_exists(DEV_SECRETS_ROOT + "thirdParty")
|
|
72
|
-
mcli = mcli_as_basic_user()
|
|
72
|
+
mcli = mcli_as_basic_user() # noqa: F841
|
|
73
73
|
|
|
74
74
|
else:
|
|
75
75
|
help(action, args)
|
|
@@ -759,7 +759,7 @@ def get_current_version(file_path=VERSION_FILE_PATH):
|
|
|
759
759
|
logger.info(f"Error reading file {file_path}: {e}")
|
|
760
760
|
return None
|
|
761
761
|
|
|
762
|
-
if version
|
|
762
|
+
if version is None:
|
|
763
763
|
raise ValueError(f"File '{file_path}' does not contain version.")
|
|
764
764
|
elif is_major_minor_patch(version):
|
|
765
765
|
return version
|
|
@@ -866,7 +866,7 @@ def align_version(
|
|
|
866
866
|
for key in platform_pkgs:
|
|
867
867
|
if key in data[K_DEPS]:
|
|
868
868
|
val = data[K_DEPS][key]
|
|
869
|
-
if val
|
|
869
|
+
if val is None or val != maj_min:
|
|
870
870
|
data[K_DEPS][key] = maj_min
|
|
871
871
|
modified = True
|
|
872
872
|
|
|
@@ -875,7 +875,7 @@ def align_version(
|
|
|
875
875
|
# update version to major.minor.patch
|
|
876
876
|
if (
|
|
877
877
|
K_VERSION not in data
|
|
878
|
-
or data[K_VERSION]
|
|
878
|
+
or data[K_VERSION] is None
|
|
879
879
|
or data[K_VERSION] != version
|
|
880
880
|
):
|
|
881
881
|
data[K_VERSION] = version
|
|
@@ -891,7 +891,7 @@ def align_version(
|
|
|
891
891
|
# update compatibleToVersion to previous major.minor
|
|
892
892
|
if (
|
|
893
893
|
K_COMPAT not in data
|
|
894
|
-
or data[K_COMPAT]
|
|
894
|
+
or data[K_COMPAT] is None
|
|
895
895
|
or data[K_COMPAT] != compat_version
|
|
896
896
|
):
|
|
897
897
|
data[K_COMPAT] = compat_version
|
mcli/lib/config/__init__.py
CHANGED
|
@@ -1 +1,29 @@
|
|
|
1
|
-
|
|
1
|
+
"""Config module for MCLI."""
|
|
2
|
+
|
|
3
|
+
from .config import (
|
|
4
|
+
DEV_SECRETS_ROOT,
|
|
5
|
+
ENDPOINT,
|
|
6
|
+
PACKAGES_TO_SYNC,
|
|
7
|
+
PATH_TO_PACKAGE_REPO,
|
|
8
|
+
PRIVATE_KEY_PATH,
|
|
9
|
+
USER_CONFIG_ROOT,
|
|
10
|
+
USER_INFO_FILE,
|
|
11
|
+
get_config_directory,
|
|
12
|
+
get_config_file_name,
|
|
13
|
+
get_config_for_file,
|
|
14
|
+
get_mcli_rc,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
__all__ = [
|
|
18
|
+
"DEV_SECRETS_ROOT",
|
|
19
|
+
"ENDPOINT",
|
|
20
|
+
"PACKAGES_TO_SYNC",
|
|
21
|
+
"PATH_TO_PACKAGE_REPO",
|
|
22
|
+
"PRIVATE_KEY_PATH",
|
|
23
|
+
"USER_CONFIG_ROOT",
|
|
24
|
+
"USER_INFO_FILE",
|
|
25
|
+
"get_config_directory",
|
|
26
|
+
"get_config_file_name",
|
|
27
|
+
"get_config_for_file",
|
|
28
|
+
"get_mcli_rc",
|
|
29
|
+
]
|
mcli/lib/config/config.py
CHANGED
mcli/lib/custom_commands.py
CHANGED
|
@@ -14,7 +14,7 @@ logger = get_logger(__name__)
|
|
|
14
14
|
|
|
15
15
|
@dataclass
|
|
16
16
|
class DiscoveredCommand:
|
|
17
|
-
"""Represents a discovered Click command"""
|
|
17
|
+
"""Represents a discovered Click command."""
|
|
18
18
|
|
|
19
19
|
name: str
|
|
20
20
|
full_name: str # e.g., "workflow.file.oxps_to_pdf"
|
|
@@ -27,14 +27,14 @@ class DiscoveredCommand:
|
|
|
27
27
|
|
|
28
28
|
|
|
29
29
|
class ClickCommandDiscovery:
|
|
30
|
-
"""Discovers all Click commands in the MCLI application"""
|
|
30
|
+
"""Discovers all Click commands in the MCLI application."""
|
|
31
31
|
|
|
32
32
|
def __init__(self, base_path: Optional[Path] = None):
|
|
33
33
|
self.base_path = base_path or Path(__file__).parent.parent.parent
|
|
34
34
|
self.discovered_commands: Dict[str, DiscoveredCommand] = {}
|
|
35
35
|
|
|
36
36
|
def discover_all_commands(self) -> List[DiscoveredCommand]:
|
|
37
|
-
"""Discover all Click commands in the application"""
|
|
37
|
+
"""Discover all Click commands in the application."""
|
|
38
38
|
self.discovered_commands.clear()
|
|
39
39
|
|
|
40
40
|
# Get the included directories from config
|
|
@@ -63,7 +63,7 @@ class ClickCommandDiscovery:
|
|
|
63
63
|
return list(self.discovered_commands.values())
|
|
64
64
|
|
|
65
65
|
def _discover_in_directory(self, directory: str):
|
|
66
|
-
"""Discover commands in a specific directory"""
|
|
66
|
+
"""Discover commands in a specific directory."""
|
|
67
67
|
if "/" in directory:
|
|
68
68
|
# Handle nested paths like "workflow/daemon"
|
|
69
69
|
parts = directory.split("/")
|
|
@@ -90,7 +90,7 @@ class ClickCommandDiscovery:
|
|
|
90
90
|
logger.debug(f"Error discovering commands in {py_file}: {e}")
|
|
91
91
|
|
|
92
92
|
def _discover_in_file(self, py_file: Path, base_directory: str):
|
|
93
|
-
"""Discover commands in a specific Python file"""
|
|
93
|
+
"""Discover commands in a specific Python file."""
|
|
94
94
|
# Convert file path to module name
|
|
95
95
|
relative_path = py_file.relative_to(self.base_path.parent)
|
|
96
96
|
module_name = str(relative_path).replace("/", ".").replace(".py", "")
|
|
@@ -101,13 +101,13 @@ class ClickCommandDiscovery:
|
|
|
101
101
|
|
|
102
102
|
try:
|
|
103
103
|
# Import the module
|
|
104
|
-
if module_name not in sys.modules:
|
|
104
|
+
if module_name not in sys.modules: # noqa: SIM401
|
|
105
105
|
module = importlib.import_module(module_name)
|
|
106
106
|
else:
|
|
107
107
|
module = sys.modules[module_name]
|
|
108
108
|
|
|
109
109
|
# Find Click objects
|
|
110
|
-
for
|
|
110
|
+
for _name, obj in inspect.getmembers(module):
|
|
111
111
|
if isinstance(obj, click.Group):
|
|
112
112
|
self._register_group(obj, module_name, base_directory)
|
|
113
113
|
elif isinstance(obj, click.Command):
|
|
@@ -117,7 +117,7 @@ class ClickCommandDiscovery:
|
|
|
117
117
|
logger.debug(f"Could not import or inspect module {module_name}: {e}")
|
|
118
118
|
|
|
119
119
|
def _register_group(self, group: click.Group, module_name: str, base_directory: str):
|
|
120
|
-
"""Register a Click group and its commands"""
|
|
120
|
+
"""Register a Click group and its commands."""
|
|
121
121
|
group_full_name = f"{base_directory}.{group.name}" if group.name else base_directory
|
|
122
122
|
|
|
123
123
|
# Register the group itself
|
|
@@ -156,7 +156,7 @@ class ClickCommandDiscovery:
|
|
|
156
156
|
self._register_group_recursive(cmd, cmd_full_name, module_name)
|
|
157
157
|
|
|
158
158
|
def _register_command(self, command: click.Command, module_name: str, base_directory: str):
|
|
159
|
-
"""Register a standalone Click command"""
|
|
159
|
+
"""Register a standalone Click command."""
|
|
160
160
|
cmd_full_name = f"{base_directory}.{command.name}" if command.name else base_directory
|
|
161
161
|
|
|
162
162
|
cmd = DiscoveredCommand(
|
|
@@ -172,7 +172,7 @@ class ClickCommandDiscovery:
|
|
|
172
172
|
self.discovered_commands[cmd_full_name] = cmd
|
|
173
173
|
|
|
174
174
|
def _register_group_recursive(self, group: click.Group, parent_name: str, module_name: str):
|
|
175
|
-
"""Recursively register nested group commands"""
|
|
175
|
+
"""Recursively register nested group commands."""
|
|
176
176
|
for cmd_name, cmd in group.commands.items():
|
|
177
177
|
cmd_full_name = f"{parent_name}.{cmd_name}"
|
|
178
178
|
|
|
@@ -192,7 +192,7 @@ class ClickCommandDiscovery:
|
|
|
192
192
|
self._register_group_recursive(cmd, cmd_full_name, module_name)
|
|
193
193
|
|
|
194
194
|
def get_commands(self, include_groups: bool = True) -> List[Dict[str, Any]]:
|
|
195
|
-
"""Get all discovered commands as dictionaries"""
|
|
195
|
+
"""Get all discovered commands as dictionaries."""
|
|
196
196
|
commands = []
|
|
197
197
|
|
|
198
198
|
for cmd in self.discovered_commands.values():
|
|
@@ -224,7 +224,7 @@ class ClickCommandDiscovery:
|
|
|
224
224
|
return sorted(commands, key=lambda x: x["full_name"])
|
|
225
225
|
|
|
226
226
|
def search_commands(self, query: str) -> List[Dict[str, Any]]:
|
|
227
|
-
"""Search commands by name, description, or module"""
|
|
227
|
+
"""Search commands by name, description, or module."""
|
|
228
228
|
query = query.lower()
|
|
229
229
|
all_commands = self.get_commands()
|
|
230
230
|
|
|
@@ -241,7 +241,7 @@ class ClickCommandDiscovery:
|
|
|
241
241
|
return matching_commands
|
|
242
242
|
|
|
243
243
|
def get_command_by_name(self, name: str) -> Optional[DiscoveredCommand]:
|
|
244
|
-
"""Get a command by its name or full name"""
|
|
244
|
+
"""Get a command by its name or full name."""
|
|
245
245
|
# First try exact match by full name
|
|
246
246
|
if name in self.discovered_commands:
|
|
247
247
|
return self.discovered_commands[name]
|
|
@@ -259,7 +259,7 @@ _discovery_instance = None
|
|
|
259
259
|
|
|
260
260
|
|
|
261
261
|
def get_command_discovery() -> ClickCommandDiscovery:
|
|
262
|
-
"""Get a cached command discovery instance"""
|
|
262
|
+
"""Get a cached command discovery instance."""
|
|
263
263
|
global _discovery_instance
|
|
264
264
|
if _discovery_instance is None:
|
|
265
265
|
_discovery_instance = ClickCommandDiscovery()
|
|
@@ -268,7 +268,7 @@ def get_command_discovery() -> ClickCommandDiscovery:
|
|
|
268
268
|
|
|
269
269
|
|
|
270
270
|
def refresh_command_discovery():
|
|
271
|
-
"""Refresh the command discovery cache"""
|
|
271
|
+
"""Refresh the command discovery cache."""
|
|
272
272
|
global _discovery_instance
|
|
273
273
|
_discovery_instance = None
|
|
274
274
|
return get_command_discovery()
|