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/workflow/daemon/daemon.py
CHANGED
|
@@ -7,10 +7,10 @@ import sys
|
|
|
7
7
|
import tempfile
|
|
8
8
|
import time
|
|
9
9
|
import uuid
|
|
10
|
-
from dataclasses import
|
|
10
|
+
from dataclasses import dataclass
|
|
11
11
|
from datetime import datetime
|
|
12
12
|
from pathlib import Path
|
|
13
|
-
from typing import Any, Dict, List, Optional
|
|
13
|
+
from typing import Any, Dict, List, Optional
|
|
14
14
|
|
|
15
15
|
import click
|
|
16
16
|
import psutil
|
|
@@ -44,12 +44,11 @@ class CommandDatabase:
|
|
|
44
44
|
|
|
45
45
|
def __init__(self, db_path: Optional[str] = None):
|
|
46
46
|
logger.debug("CommandDatabase stub initialized - commands now managed via JSON files")
|
|
47
|
-
pass
|
|
48
47
|
|
|
49
48
|
|
|
50
49
|
@dataclass
|
|
51
50
|
class Command:
|
|
52
|
-
"""Represents a stored command"""
|
|
51
|
+
"""Represents a stored command."""
|
|
53
52
|
|
|
54
53
|
id: str
|
|
55
54
|
name: str
|
|
@@ -155,7 +154,7 @@ def start_command_file_watcher(db, watch_dir: str = None):
|
|
|
155
154
|
self._update_embeddings()
|
|
156
155
|
|
|
157
156
|
def init_database(self):
|
|
158
|
-
"""Initialize SQLite database"""
|
|
157
|
+
"""Initialize SQLite database."""
|
|
159
158
|
conn = sqlite3.connect(self.db_path)
|
|
160
159
|
cursor = conn.cursor()
|
|
161
160
|
|
|
@@ -213,7 +212,7 @@ def start_command_file_watcher(db, watch_dir: str = None):
|
|
|
213
212
|
conn.close()
|
|
214
213
|
|
|
215
214
|
def _update_embeddings(self):
|
|
216
|
-
"""Update TF-IDF embeddings for similarity search"""
|
|
215
|
+
"""Update TF-IDF embeddings for similarity search."""
|
|
217
216
|
commands = self.get_all_commands()
|
|
218
217
|
if not commands:
|
|
219
218
|
return
|
|
@@ -229,7 +228,7 @@ def start_command_file_watcher(db, watch_dir: str = None):
|
|
|
229
228
|
self.vectorizer.fit(texts)
|
|
230
229
|
|
|
231
230
|
def add_command(self, command: Command) -> str:
|
|
232
|
-
"""Add a new command to the database"""
|
|
231
|
+
"""Add a new command to the database."""
|
|
233
232
|
conn = sqlite3.connect(self.db_path)
|
|
234
233
|
cursor = conn.cursor()
|
|
235
234
|
|
|
@@ -269,7 +268,7 @@ def start_command_file_watcher(db, watch_dir: str = None):
|
|
|
269
268
|
conn.close()
|
|
270
269
|
|
|
271
270
|
def get_command(self, command_id: str) -> Optional[Command]:
|
|
272
|
-
"""Get a command by ID"""
|
|
271
|
+
"""Get a command by ID."""
|
|
273
272
|
conn = sqlite3.connect(self.db_path)
|
|
274
273
|
cursor = conn.cursor()
|
|
275
274
|
|
|
@@ -292,7 +291,7 @@ def start_command_file_watcher(db, watch_dir: str = None):
|
|
|
292
291
|
conn.close()
|
|
293
292
|
|
|
294
293
|
def get_all_commands(self, include_inactive: bool = False) -> List[Command]:
|
|
295
|
-
"""Get all commands, optionally including inactive ones"""
|
|
294
|
+
"""Get all commands, optionally including inactive ones."""
|
|
296
295
|
conn = sqlite3.connect(self.db_path)
|
|
297
296
|
cursor = conn.cursor()
|
|
298
297
|
try:
|
|
@@ -319,7 +318,7 @@ def start_command_file_watcher(db, watch_dir: str = None):
|
|
|
319
318
|
conn.close()
|
|
320
319
|
|
|
321
320
|
def search_commands(self, query: str, limit: int = 10) -> List[Command]:
|
|
322
|
-
"""Search commands by name, description, or tags"""
|
|
321
|
+
"""Search commands by name, description, or tags."""
|
|
323
322
|
conn = sqlite3.connect(self.db_path)
|
|
324
323
|
cursor = conn.cursor()
|
|
325
324
|
|
|
@@ -345,7 +344,7 @@ def start_command_file_watcher(db, watch_dir: str = None):
|
|
|
345
344
|
conn.close()
|
|
346
345
|
|
|
347
346
|
def find_similar_commands(self, query: str, limit: int = 5) -> List[tuple]:
|
|
348
|
-
"""Find similar commands using cosine similarity"""
|
|
347
|
+
"""Find similar commands using cosine similarity."""
|
|
349
348
|
commands = self.get_all_commands()
|
|
350
349
|
if not commands:
|
|
351
350
|
return []
|
|
@@ -383,7 +382,7 @@ def start_command_file_watcher(db, watch_dir: str = None):
|
|
|
383
382
|
return []
|
|
384
383
|
|
|
385
384
|
def update_command(self, command: Command) -> bool:
|
|
386
|
-
"""Update an existing command"""
|
|
385
|
+
"""Update an existing command."""
|
|
387
386
|
conn = sqlite3.connect(self.db_path)
|
|
388
387
|
cursor = conn.cursor()
|
|
389
388
|
|
|
@@ -420,7 +419,7 @@ def start_command_file_watcher(db, watch_dir: str = None):
|
|
|
420
419
|
conn.close()
|
|
421
420
|
|
|
422
421
|
def delete_command(self, command_id: str) -> bool:
|
|
423
|
-
"""Delete a command (soft delete)"""
|
|
422
|
+
"""Delete a command (soft delete)."""
|
|
424
423
|
conn = sqlite3.connect(self.db_path)
|
|
425
424
|
cursor = conn.cursor()
|
|
426
425
|
|
|
@@ -451,7 +450,7 @@ def start_command_file_watcher(db, watch_dir: str = None):
|
|
|
451
450
|
error: str = None,
|
|
452
451
|
execution_time_ms: int = None,
|
|
453
452
|
):
|
|
454
|
-
"""Record command execution"""
|
|
453
|
+
"""Record command execution."""
|
|
455
454
|
conn = sqlite3.connect(self.db_path)
|
|
456
455
|
cursor = conn.cursor()
|
|
457
456
|
|
|
@@ -495,7 +494,7 @@ def start_command_file_watcher(db, watch_dir: str = None):
|
|
|
495
494
|
conn.close()
|
|
496
495
|
|
|
497
496
|
def _row_to_command(self, row) -> Command:
|
|
498
|
-
"""Convert database row to Command object"""
|
|
497
|
+
"""Convert database row to Command object."""
|
|
499
498
|
return Command(
|
|
500
499
|
id=row[0],
|
|
501
500
|
name=row[1],
|
|
@@ -513,7 +512,7 @@ def start_command_file_watcher(db, watch_dir: str = None):
|
|
|
513
512
|
|
|
514
513
|
|
|
515
514
|
class CommandExecutor:
|
|
516
|
-
"""Handles safe execution of commands in different languages"""
|
|
515
|
+
"""Handles safe execution of commands in different languages."""
|
|
517
516
|
|
|
518
517
|
def __init__(self, temp_dir: Optional[str] = None):
|
|
519
518
|
self.temp_dir = Path(temp_dir) if temp_dir else Path(tempfile.gettempdir()) / "mcli_daemon"
|
|
@@ -528,7 +527,7 @@ class CommandExecutor:
|
|
|
528
527
|
}
|
|
529
528
|
|
|
530
529
|
def execute_command(self, command: Command, args: List[str] = None) -> Dict[str, Any]:
|
|
531
|
-
"""Execute a command safely"""
|
|
530
|
+
"""Execute a command safely."""
|
|
532
531
|
start_time = time.time()
|
|
533
532
|
|
|
534
533
|
try:
|
|
@@ -561,7 +560,7 @@ class CommandExecutor:
|
|
|
561
560
|
}
|
|
562
561
|
|
|
563
562
|
def _execute_python(self, command: Command, args: List[str]) -> Dict[str, str]:
|
|
564
|
-
"""Execute Python code safely"""
|
|
563
|
+
"""Execute Python code safely."""
|
|
565
564
|
# Create temporary file
|
|
566
565
|
script_file = self.temp_dir / f"{command.id}_{int(time.time())}.py"
|
|
567
566
|
|
|
@@ -587,7 +586,7 @@ class CommandExecutor:
|
|
|
587
586
|
script_file.unlink()
|
|
588
587
|
|
|
589
588
|
def _execute_node(self, command: Command, args: List[str]) -> Dict[str, str]:
|
|
590
|
-
"""Execute Node.js code safely"""
|
|
589
|
+
"""Execute Node.js code safely."""
|
|
591
590
|
script_file = self.temp_dir / f"{command.id}_{int(time.time())}.js"
|
|
592
591
|
|
|
593
592
|
try:
|
|
@@ -609,7 +608,7 @@ class CommandExecutor:
|
|
|
609
608
|
script_file.unlink()
|
|
610
609
|
|
|
611
610
|
def _execute_lua(self, command: Command, args: List[str]) -> Dict[str, str]:
|
|
612
|
-
"""Execute Lua code safely"""
|
|
611
|
+
"""Execute Lua code safely."""
|
|
613
612
|
script_file = self.temp_dir / f"{command.id}_{int(time.time())}.lua"
|
|
614
613
|
|
|
615
614
|
try:
|
|
@@ -631,7 +630,7 @@ class CommandExecutor:
|
|
|
631
630
|
script_file.unlink()
|
|
632
631
|
|
|
633
632
|
def _execute_shell(self, command: Command, args: List[str]) -> Dict[str, str]:
|
|
634
|
-
"""Execute shell commands safely"""
|
|
633
|
+
"""Execute shell commands safely."""
|
|
635
634
|
script_file = self.temp_dir / f"{command.id}_{int(time.time())}.sh"
|
|
636
635
|
|
|
637
636
|
try:
|
|
@@ -658,7 +657,7 @@ class CommandExecutor:
|
|
|
658
657
|
|
|
659
658
|
|
|
660
659
|
class DaemonService:
|
|
661
|
-
"""Background daemon service for command management"""
|
|
660
|
+
"""Background daemon service for command management."""
|
|
662
661
|
|
|
663
662
|
def __init__(self, config_path: Optional[str] = None):
|
|
664
663
|
# Load configuration from TOML
|
|
@@ -696,7 +695,7 @@ class DaemonService:
|
|
|
696
695
|
self.pid_file.parent.mkdir(parents=True, exist_ok=True)
|
|
697
696
|
|
|
698
697
|
def start(self):
|
|
699
|
-
"""Start the daemon service"""
|
|
698
|
+
"""Start the daemon service."""
|
|
700
699
|
if self.running:
|
|
701
700
|
logger.info("Daemon is already running")
|
|
702
701
|
return
|
|
@@ -734,7 +733,7 @@ class DaemonService:
|
|
|
734
733
|
self.stop()
|
|
735
734
|
|
|
736
735
|
def stop(self):
|
|
737
|
-
"""Stop the daemon service"""
|
|
736
|
+
"""Stop the daemon service."""
|
|
738
737
|
if not self.running:
|
|
739
738
|
return
|
|
740
739
|
|
|
@@ -747,13 +746,13 @@ class DaemonService:
|
|
|
747
746
|
logger.info("Daemon stopped")
|
|
748
747
|
|
|
749
748
|
def _signal_handler(self, signum, frame):
|
|
750
|
-
"""Handle shutdown signals"""
|
|
749
|
+
"""Handle shutdown signals."""
|
|
751
750
|
logger.info(f"Received signal {signum}, shutting down...")
|
|
752
751
|
self.stop()
|
|
753
752
|
sys.exit(0)
|
|
754
753
|
|
|
755
754
|
def _main_loop(self):
|
|
756
|
-
"""Main daemon loop"""
|
|
755
|
+
"""Main daemon loop."""
|
|
757
756
|
logger.info("Daemon main loop started")
|
|
758
757
|
|
|
759
758
|
while self.running:
|
|
@@ -768,7 +767,7 @@ class DaemonService:
|
|
|
768
767
|
time.sleep(5)
|
|
769
768
|
|
|
770
769
|
def status(self) -> Dict[str, Any]:
|
|
771
|
-
"""Get daemon status"""
|
|
770
|
+
"""Get daemon status."""
|
|
772
771
|
is_running = False
|
|
773
772
|
pid = None
|
|
774
773
|
|
|
@@ -791,21 +790,20 @@ class DaemonService:
|
|
|
791
790
|
# CLI Commands
|
|
792
791
|
@click.group(name="daemon")
|
|
793
792
|
def daemon():
|
|
794
|
-
"""Daemon service for command management"""
|
|
795
|
-
pass
|
|
793
|
+
"""Daemon service for command management."""
|
|
796
794
|
|
|
797
795
|
|
|
798
796
|
@daemon.command()
|
|
799
797
|
@click.option("--config", help="Path to configuration file")
|
|
800
798
|
def start(config: Optional[str]):
|
|
801
|
-
"""Start the daemon service"""
|
|
799
|
+
"""Start the daemon service."""
|
|
802
800
|
service = DaemonService(config)
|
|
803
801
|
service.start()
|
|
804
802
|
|
|
805
803
|
|
|
806
804
|
@daemon.command()
|
|
807
805
|
def stop():
|
|
808
|
-
"""Stop the daemon service"""
|
|
806
|
+
"""Stop the daemon service."""
|
|
809
807
|
pid_file = Path.home() / ".local" / "mcli" / "daemon" / "daemon.pid"
|
|
810
808
|
|
|
811
809
|
if not pid_file.exists():
|
|
@@ -833,7 +831,7 @@ def stop():
|
|
|
833
831
|
|
|
834
832
|
@daemon.command()
|
|
835
833
|
def status():
|
|
836
|
-
"""Show daemon status"""
|
|
834
|
+
"""Show daemon status."""
|
|
837
835
|
service = DaemonService()
|
|
838
836
|
status_info = service.status()
|
|
839
837
|
if status_info["running"]:
|
|
@@ -849,8 +847,7 @@ def status():
|
|
|
849
847
|
@click.option("--json", "as_json", is_flag=True, help="Output as JSON")
|
|
850
848
|
@click.option("--all", "show_all", is_flag=True, help="Show all commands, including inactive")
|
|
851
849
|
def list_commands(as_json, show_all):
|
|
852
|
-
"""List all available commands (optionally including inactive)"""
|
|
853
|
-
import sys
|
|
850
|
+
"""List all available commands (optionally including inactive)."""
|
|
854
851
|
|
|
855
852
|
service = DaemonService()
|
|
856
853
|
commands = service.db.get_all_commands(include_inactive=show_all)
|
|
@@ -887,8 +884,7 @@ def list_commands(as_json, show_all):
|
|
|
887
884
|
@click.argument("args", nargs=-1)
|
|
888
885
|
@click.option("--json", "as_json", is_flag=True, help="Output as JSON")
|
|
889
886
|
def execute_command(command_name, args, as_json):
|
|
890
|
-
"""Execute a command by name with optional arguments"""
|
|
891
|
-
import sys
|
|
887
|
+
"""Execute a command by name with optional arguments."""
|
|
892
888
|
|
|
893
889
|
service = DaemonService()
|
|
894
890
|
# Find command by name
|
|
@@ -4,24 +4,15 @@ Enhanced async daemon with Rust extensions and performance optimizations
|
|
|
4
4
|
|
|
5
5
|
import asyncio
|
|
6
6
|
import json
|
|
7
|
-
import os
|
|
8
7
|
import signal
|
|
9
8
|
import uuid
|
|
10
9
|
from datetime import datetime
|
|
11
10
|
from pathlib import Path
|
|
12
11
|
from typing import Any, Dict, List, Optional
|
|
13
12
|
|
|
14
|
-
import aiosqlite
|
|
15
|
-
import redis.asyncio as redis
|
|
16
|
-
|
|
17
13
|
from mcli.lib.logger.logger import get_logger
|
|
18
|
-
from mcli.lib.performance.rust_bridge import
|
|
19
|
-
|
|
20
|
-
get_file_watcher,
|
|
21
|
-
get_process_manager,
|
|
22
|
-
get_tfidf_vectorizer,
|
|
23
|
-
)
|
|
24
|
-
from mcli.lib.search.cached_vectorizer import CachedTfIdfVectorizer, SmartVectorizerManager
|
|
14
|
+
from mcli.lib.performance.rust_bridge import get_command_matcher, get_file_watcher
|
|
15
|
+
from mcli.lib.search.cached_vectorizer import SmartVectorizerManager
|
|
25
16
|
from mcli.workflow.daemon.async_command_database import (
|
|
26
17
|
AsyncCommandDatabase,
|
|
27
18
|
Command,
|
|
@@ -33,7 +24,7 @@ logger = get_logger(__name__)
|
|
|
33
24
|
|
|
34
25
|
|
|
35
26
|
class EnhancedDaemon:
|
|
36
|
-
"""High-performance async daemon with Rust extensions"""
|
|
27
|
+
"""High-performance async daemon with Rust extensions."""
|
|
37
28
|
|
|
38
29
|
def __init__(
|
|
39
30
|
self, db_path: Optional[str] = None, redis_url: Optional[str] = None, use_rust: bool = True
|
|
@@ -64,7 +55,7 @@ class EnhancedDaemon:
|
|
|
64
55
|
}
|
|
65
56
|
|
|
66
57
|
async def initialize(self):
|
|
67
|
-
"""Initialize all daemon components"""
|
|
58
|
+
"""Initialize all daemon components."""
|
|
68
59
|
logger.info("Initializing Enhanced Daemon...")
|
|
69
60
|
|
|
70
61
|
try:
|
|
@@ -96,7 +87,7 @@ class EnhancedDaemon:
|
|
|
96
87
|
raise
|
|
97
88
|
|
|
98
89
|
async def _initialize_rust_components(self):
|
|
99
|
-
"""Initialize Rust-based components"""
|
|
90
|
+
"""Initialize Rust-based components."""
|
|
100
91
|
try:
|
|
101
92
|
# File watcher
|
|
102
93
|
self.file_watcher = get_file_watcher(use_rust=self.use_rust)
|
|
@@ -138,7 +129,7 @@ class EnhancedDaemon:
|
|
|
138
129
|
logger.warning(f"Failed to initialize some Rust components: {e}")
|
|
139
130
|
|
|
140
131
|
def _setup_signal_handlers(self):
|
|
141
|
-
"""Setup signal handlers for graceful shutdown"""
|
|
132
|
+
"""Setup signal handlers for graceful shutdown."""
|
|
142
133
|
|
|
143
134
|
def signal_handler():
|
|
144
135
|
logger.info("Received shutdown signal")
|
|
@@ -152,7 +143,7 @@ class EnhancedDaemon:
|
|
|
152
143
|
signal.signal(sig, lambda s, f: asyncio.create_task(self.shutdown()))
|
|
153
144
|
|
|
154
145
|
async def start(self):
|
|
155
|
-
"""Start the daemon"""
|
|
146
|
+
"""Start the daemon."""
|
|
156
147
|
if self.running:
|
|
157
148
|
logger.warning("Daemon is already running")
|
|
158
149
|
return
|
|
@@ -182,7 +173,7 @@ class EnhancedDaemon:
|
|
|
182
173
|
logger.info("Enhanced Daemon stopped")
|
|
183
174
|
|
|
184
175
|
async def shutdown(self):
|
|
185
|
-
"""Graceful shutdown"""
|
|
176
|
+
"""Graceful shutdown."""
|
|
186
177
|
if not self.running:
|
|
187
178
|
return
|
|
188
179
|
|
|
@@ -208,7 +199,7 @@ class EnhancedDaemon:
|
|
|
208
199
|
await self.vectorizer_manager.close_all()
|
|
209
200
|
|
|
210
201
|
async def _file_watcher_loop(self):
|
|
211
|
-
"""Background loop for processing file system events"""
|
|
202
|
+
"""Background loop for processing file system events."""
|
|
212
203
|
if not self.file_watcher:
|
|
213
204
|
return
|
|
214
205
|
|
|
@@ -227,7 +218,7 @@ class EnhancedDaemon:
|
|
|
227
218
|
await asyncio.sleep(5) # Back off on error
|
|
228
219
|
|
|
229
220
|
async def _handle_file_event(self, event):
|
|
230
|
-
"""Handle a file system event"""
|
|
221
|
+
"""Handle a file system event."""
|
|
231
222
|
try:
|
|
232
223
|
event_type = event.get("event_type") or event.get("type", "unknown")
|
|
233
224
|
path = event.get("path", "")
|
|
@@ -244,7 +235,7 @@ class EnhancedDaemon:
|
|
|
244
235
|
logger.error(f"Error handling file event {event}: {e}")
|
|
245
236
|
|
|
246
237
|
async def _reload_command_file(self, file_path: str):
|
|
247
|
-
"""Reload a command from a JSON file"""
|
|
238
|
+
"""Reload a command from a JSON file."""
|
|
248
239
|
try:
|
|
249
240
|
with open(file_path, "r") as f:
|
|
250
241
|
data = json.load(f)
|
|
@@ -286,7 +277,7 @@ class EnhancedDaemon:
|
|
|
286
277
|
logger.error(f"Failed to reload command file {file_path}: {e}")
|
|
287
278
|
|
|
288
279
|
async def _remove_command_file(self, file_path: str):
|
|
289
|
-
"""Remove a command when its file is deleted"""
|
|
280
|
+
"""Remove a command when its file is deleted."""
|
|
290
281
|
try:
|
|
291
282
|
# Extract command ID from filename
|
|
292
283
|
command_id = Path(file_path).stem
|
|
@@ -300,7 +291,7 @@ class EnhancedDaemon:
|
|
|
300
291
|
logger.error(f"Failed to remove command file {file_path}: {e}")
|
|
301
292
|
|
|
302
293
|
async def _maintenance_loop(self):
|
|
303
|
-
"""Background maintenance tasks"""
|
|
294
|
+
"""Background maintenance tasks."""
|
|
304
295
|
while self.running:
|
|
305
296
|
try:
|
|
306
297
|
# Clean up finished processes
|
|
@@ -320,7 +311,7 @@ class EnhancedDaemon:
|
|
|
320
311
|
await asyncio.sleep(60)
|
|
321
312
|
|
|
322
313
|
async def _metrics_loop(self):
|
|
323
|
-
"""Background metrics collection"""
|
|
314
|
+
"""Background metrics collection."""
|
|
324
315
|
while self.running:
|
|
325
316
|
try:
|
|
326
317
|
# Log performance metrics every 10 minutes
|
|
@@ -339,7 +330,7 @@ class EnhancedDaemon:
|
|
|
339
330
|
await asyncio.sleep(60)
|
|
340
331
|
|
|
341
332
|
async def _update_search_indexes(self):
|
|
342
|
-
"""Update search indexes for better performance"""
|
|
333
|
+
"""Update search indexes for better performance."""
|
|
343
334
|
try:
|
|
344
335
|
# Get all commands
|
|
345
336
|
commands = await self.command_db.get_all_commands()
|
|
@@ -366,7 +357,7 @@ class EnhancedDaemon:
|
|
|
366
357
|
# Public API methods
|
|
367
358
|
|
|
368
359
|
async def add_command(self, command_data: Dict[str, Any]) -> str:
|
|
369
|
-
"""Add a new command"""
|
|
360
|
+
"""Add a new command."""
|
|
370
361
|
command = Command(
|
|
371
362
|
id=command_data.get("id") or str(uuid.uuid4()),
|
|
372
363
|
name=command_data["name"],
|
|
@@ -396,7 +387,7 @@ class EnhancedDaemon:
|
|
|
396
387
|
return command_id
|
|
397
388
|
|
|
398
389
|
async def search_commands(self, query: str, limit: int = 10) -> List[Dict[str, Any]]:
|
|
399
|
-
"""Search for commands"""
|
|
390
|
+
"""Search for commands."""
|
|
400
391
|
self.metrics["search_queries"] += 1
|
|
401
392
|
|
|
402
393
|
# Try Rust command matcher first
|
|
@@ -465,7 +456,7 @@ class EnhancedDaemon:
|
|
|
465
456
|
async def execute_command(
|
|
466
457
|
self, command_id: str, context: Optional[Dict[str, Any]] = None
|
|
467
458
|
) -> str:
|
|
468
|
-
"""Execute a command"""
|
|
459
|
+
"""Execute a command."""
|
|
469
460
|
self.metrics["commands_executed"] += 1
|
|
470
461
|
|
|
471
462
|
# Get command
|
|
@@ -498,7 +489,7 @@ class EnhancedDaemon:
|
|
|
498
489
|
return process_id
|
|
499
490
|
|
|
500
491
|
def _get_command_executor(self, language: str) -> str:
|
|
501
|
-
"""Get the appropriate executor for a language"""
|
|
492
|
+
"""Get the appropriate executor for a language."""
|
|
502
493
|
executors = {
|
|
503
494
|
"python": "python",
|
|
504
495
|
"node": "node",
|
|
@@ -509,7 +500,7 @@ class EnhancedDaemon:
|
|
|
509
500
|
return executors.get(language, "bash")
|
|
510
501
|
|
|
511
502
|
def _prepare_command_args(self, command: Command) -> List[str]:
|
|
512
|
-
"""Prepare command arguments based on language"""
|
|
503
|
+
"""Prepare command arguments based on language."""
|
|
513
504
|
if command.language == "python":
|
|
514
505
|
return ["-c", command.code]
|
|
515
506
|
elif command.language == "node":
|
|
@@ -522,7 +513,7 @@ class EnhancedDaemon:
|
|
|
522
513
|
return [command.code]
|
|
523
514
|
|
|
524
515
|
async def get_status(self) -> Dict[str, Any]:
|
|
525
|
-
"""Get daemon status"""
|
|
516
|
+
"""Get daemon status."""
|
|
526
517
|
return {
|
|
527
518
|
"running": self.running,
|
|
528
519
|
"uptime": (
|
|
@@ -546,7 +537,7 @@ _daemon_instance: Optional[EnhancedDaemon] = None
|
|
|
546
537
|
|
|
547
538
|
|
|
548
539
|
async def get_daemon() -> EnhancedDaemon:
|
|
549
|
-
"""Get the global daemon instance"""
|
|
540
|
+
"""Get the global daemon instance."""
|
|
550
541
|
global _daemon_instance
|
|
551
542
|
|
|
552
543
|
if _daemon_instance is None:
|
|
@@ -557,13 +548,13 @@ async def get_daemon() -> EnhancedDaemon:
|
|
|
557
548
|
|
|
558
549
|
|
|
559
550
|
async def start_daemon():
|
|
560
|
-
"""Start the global daemon"""
|
|
551
|
+
"""Start the global daemon."""
|
|
561
552
|
daemon = await get_daemon()
|
|
562
553
|
await daemon.start()
|
|
563
554
|
|
|
564
555
|
|
|
565
556
|
async def stop_daemon():
|
|
566
|
-
"""Stop the global daemon"""
|
|
557
|
+
"""Stop the global daemon."""
|
|
567
558
|
global _daemon_instance
|
|
568
559
|
|
|
569
560
|
if _daemon_instance:
|
|
@@ -14,15 +14,14 @@ API_BASE_URL = "http://localhost:8000"
|
|
|
14
14
|
|
|
15
15
|
@click.group(name="process")
|
|
16
16
|
def process_cli():
|
|
17
|
-
"""Docker-like process management commands"""
|
|
18
|
-
pass
|
|
17
|
+
"""Docker-like process management commands."""
|
|
19
18
|
|
|
20
19
|
|
|
21
20
|
@process_cli.command("ps")
|
|
22
21
|
@click.option("--all", "-a", is_flag=True, help="Show all processes including exited ones")
|
|
23
22
|
@click.option("--json", "as_json", is_flag=True, help="Output as JSON")
|
|
24
23
|
def list_processes(all: bool, as_json: bool):
|
|
25
|
-
"""List processes (like 'docker ps')"""
|
|
24
|
+
"""List processes (like 'docker ps')."""
|
|
26
25
|
try:
|
|
27
26
|
params = {"all": "true"} if all else {}
|
|
28
27
|
response = requests.get(f"{API_BASE_URL}/processes", params=params)
|
|
@@ -66,7 +65,7 @@ def list_processes(all: bool, as_json: bool):
|
|
|
66
65
|
def run_process(
|
|
67
66
|
command: str, args: tuple, name: Optional[str], detach: bool, working_dir: Optional[str]
|
|
68
67
|
):
|
|
69
|
-
"""Create and start a process (like 'docker run')"""
|
|
68
|
+
"""Create and start a process (like 'docker run')."""
|
|
70
69
|
try:
|
|
71
70
|
data = {
|
|
72
71
|
"name": name or f"proc-{command}",
|
|
@@ -98,7 +97,7 @@ def run_process(
|
|
|
98
97
|
@click.argument("process_id")
|
|
99
98
|
@click.option("--lines", "-n", type=int, help="Number of lines to show from end of logs")
|
|
100
99
|
def show_logs(process_id: str, lines: Optional[int]):
|
|
101
|
-
"""Show logs for a process (like 'docker logs')"""
|
|
100
|
+
"""Show logs for a process (like 'docker logs')."""
|
|
102
101
|
try:
|
|
103
102
|
params = {}
|
|
104
103
|
if lines:
|
|
@@ -128,7 +127,7 @@ def show_logs(process_id: str, lines: Optional[int]):
|
|
|
128
127
|
@click.argument("process_id")
|
|
129
128
|
@click.option("--json", "as_json", is_flag=True, help="Output as JSON")
|
|
130
129
|
def inspect_process(process_id: str, as_json: bool):
|
|
131
|
-
"""Show detailed information about a process (like 'docker inspect')"""
|
|
130
|
+
"""Show detailed information about a process (like 'docker inspect')."""
|
|
132
131
|
try:
|
|
133
132
|
response = requests.get(f"{API_BASE_URL}/processes/{process_id}")
|
|
134
133
|
|
|
@@ -150,7 +149,7 @@ def inspect_process(process_id: str, as_json: bool):
|
|
|
150
149
|
|
|
151
150
|
if info.get("stats"):
|
|
152
151
|
stats = info["stats"]
|
|
153
|
-
click.echo(
|
|
152
|
+
click.echo("\nResource Usage:")
|
|
154
153
|
click.echo(f" CPU: {stats.get('cpu_percent', 0):.1f}%")
|
|
155
154
|
click.echo(f" Memory: {stats.get('memory_mb', 0):.1f} MB")
|
|
156
155
|
click.echo(f" Uptime: {stats.get('uptime_seconds', 0)} seconds")
|
|
@@ -168,7 +167,7 @@ def inspect_process(process_id: str, as_json: bool):
|
|
|
168
167
|
@click.argument("process_id")
|
|
169
168
|
@click.option("--timeout", "-t", type=int, default=10, help="Timeout in seconds")
|
|
170
169
|
def stop_process(process_id: str, timeout: int):
|
|
171
|
-
"""Stop a process (like 'docker stop')"""
|
|
170
|
+
"""Stop a process (like 'docker stop')."""
|
|
172
171
|
try:
|
|
173
172
|
data = {"timeout": timeout}
|
|
174
173
|
response = requests.post(f"{API_BASE_URL}/processes/{process_id}/stop", json=data)
|
|
@@ -187,7 +186,7 @@ def stop_process(process_id: str, timeout: int):
|
|
|
187
186
|
@process_cli.command("start")
|
|
188
187
|
@click.argument("process_id")
|
|
189
188
|
def start_process(process_id: str):
|
|
190
|
-
"""Start a stopped process (like 'docker start')"""
|
|
189
|
+
"""Start a stopped process (like 'docker start')."""
|
|
191
190
|
try:
|
|
192
191
|
response = requests.post(f"{API_BASE_URL}/processes/{process_id}/start")
|
|
193
192
|
|
|
@@ -205,7 +204,7 @@ def start_process(process_id: str):
|
|
|
205
204
|
@process_cli.command("kill")
|
|
206
205
|
@click.argument("process_id")
|
|
207
206
|
def kill_process(process_id: str):
|
|
208
|
-
"""Kill a process (like 'docker kill')"""
|
|
207
|
+
"""Kill a process (like 'docker kill')."""
|
|
209
208
|
try:
|
|
210
209
|
response = requests.post(f"{API_BASE_URL}/processes/{process_id}/kill")
|
|
211
210
|
|
|
@@ -222,9 +221,9 @@ def kill_process(process_id: str):
|
|
|
222
221
|
|
|
223
222
|
@process_cli.command("rm")
|
|
224
223
|
@click.argument("process_id")
|
|
225
|
-
@click.option("--force", "-
|
|
224
|
+
@click.option("--force", "-", is_flag=True, help="Force remove running process")
|
|
226
225
|
def remove_process(process_id: str, force: bool):
|
|
227
|
-
"""Remove a process (like 'docker rm')"""
|
|
226
|
+
"""Remove a process (like 'docker rm')."""
|
|
228
227
|
try:
|
|
229
228
|
params = {"force": "true"} if force else {}
|
|
230
229
|
response = requests.delete(f"{API_BASE_URL}/processes/{process_id}", params=params)
|