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
|
@@ -1,17 +1,15 @@
|
|
|
1
1
|
import asyncio
|
|
2
2
|
import json
|
|
3
3
|
import os
|
|
4
|
-
import signal
|
|
5
4
|
import uuid
|
|
6
5
|
from contextlib import asynccontextmanager
|
|
7
6
|
from dataclasses import asdict, dataclass
|
|
8
7
|
from datetime import datetime
|
|
9
8
|
from enum import Enum
|
|
10
9
|
from pathlib import Path
|
|
11
|
-
from typing import
|
|
10
|
+
from typing import Dict, List, Optional
|
|
12
11
|
|
|
13
12
|
import aiosqlite
|
|
14
|
-
import psutil
|
|
15
13
|
import redis.asyncio as redis
|
|
16
14
|
|
|
17
15
|
from mcli.lib.logger.logger import get_logger
|
|
@@ -30,7 +28,7 @@ class ProcessStatus(Enum):
|
|
|
30
28
|
|
|
31
29
|
@dataclass
|
|
32
30
|
class ProcessInfo:
|
|
33
|
-
"""Information about a managed process"""
|
|
31
|
+
"""Information about a managed process."""
|
|
34
32
|
|
|
35
33
|
id: str
|
|
36
34
|
name: str
|
|
@@ -57,7 +55,7 @@ class ProcessInfo:
|
|
|
57
55
|
|
|
58
56
|
|
|
59
57
|
class AsyncProcessContainer:
|
|
60
|
-
"""Manages a single async process with enhanced monitoring"""
|
|
58
|
+
"""Manages a single async process with enhanced monitoring."""
|
|
61
59
|
|
|
62
60
|
def __init__(self, process_info: ProcessInfo, redis_client: Optional[redis.Redis] = None):
|
|
63
61
|
self.info = process_info
|
|
@@ -69,7 +67,7 @@ class AsyncProcessContainer:
|
|
|
69
67
|
self._setup_container_environment()
|
|
70
68
|
|
|
71
69
|
def _setup_container_environment(self):
|
|
72
|
-
"""Setup isolated environment for the process"""
|
|
70
|
+
"""Setup isolated environment for the process."""
|
|
73
71
|
base_dir = Path.home() / ".local" / "mcli" / "containers"
|
|
74
72
|
self.container_dir = base_dir / self.info.id
|
|
75
73
|
self.container_dir.mkdir(parents=True, exist_ok=True)
|
|
@@ -80,7 +78,7 @@ class AsyncProcessContainer:
|
|
|
80
78
|
json.dump(asdict(self.info), f, indent=2, default=str)
|
|
81
79
|
|
|
82
80
|
async def start(self, timeout: Optional[float] = None) -> bool:
|
|
83
|
-
"""Start the async process with optional timeout"""
|
|
81
|
+
"""Start the async process with optional timeout."""
|
|
84
82
|
try:
|
|
85
83
|
if self.process and self.process.returncode is None:
|
|
86
84
|
logger.warning(f"Process {self.info.id} is already running")
|
|
@@ -122,7 +120,7 @@ class AsyncProcessContainer:
|
|
|
122
120
|
return False
|
|
123
121
|
|
|
124
122
|
async def stop(self, timeout: float = 10.0) -> bool:
|
|
125
|
-
"""Stop the process gracefully with timeout"""
|
|
123
|
+
"""Stop the process gracefully with timeout."""
|
|
126
124
|
if not self.process or self.process.returncode is not None:
|
|
127
125
|
return True
|
|
128
126
|
|
|
@@ -161,7 +159,7 @@ class AsyncProcessContainer:
|
|
|
161
159
|
return False
|
|
162
160
|
|
|
163
161
|
async def kill(self) -> bool:
|
|
164
|
-
"""Force kill the process"""
|
|
162
|
+
"""Force kill the process."""
|
|
165
163
|
if not self.process or self.process.returncode is not None:
|
|
166
164
|
return True
|
|
167
165
|
|
|
@@ -191,7 +189,7 @@ class AsyncProcessContainer:
|
|
|
191
189
|
return False
|
|
192
190
|
|
|
193
191
|
async def wait(self, timeout: Optional[float] = None) -> int:
|
|
194
|
-
"""Wait for process to complete with optional timeout"""
|
|
192
|
+
"""Wait for process to complete with optional timeout."""
|
|
195
193
|
if not self.process:
|
|
196
194
|
raise RuntimeError("Process not started")
|
|
197
195
|
|
|
@@ -218,7 +216,7 @@ class AsyncProcessContainer:
|
|
|
218
216
|
return self.process.returncode
|
|
219
217
|
|
|
220
218
|
async def _monitor_stdout(self):
|
|
221
|
-
"""Monitor stdout and collect lines"""
|
|
219
|
+
"""Monitor stdout and collect lines."""
|
|
222
220
|
if not self.process or not self.process.stdout:
|
|
223
221
|
return
|
|
224
222
|
|
|
@@ -246,7 +244,7 @@ class AsyncProcessContainer:
|
|
|
246
244
|
logger.error(f"Error monitoring stdout for {self.info.id}: {e}")
|
|
247
245
|
|
|
248
246
|
async def _monitor_stderr(self):
|
|
249
|
-
"""Monitor stderr and collect lines"""
|
|
247
|
+
"""Monitor stderr and collect lines."""
|
|
250
248
|
if not self.process or not self.process.stderr:
|
|
251
249
|
return
|
|
252
250
|
|
|
@@ -274,7 +272,7 @@ class AsyncProcessContainer:
|
|
|
274
272
|
logger.error(f"Error monitoring stderr for {self.info.id}: {e}")
|
|
275
273
|
|
|
276
274
|
async def _timeout_handler(self, timeout: float):
|
|
277
|
-
"""Handle process timeout"""
|
|
275
|
+
"""Handle process timeout."""
|
|
278
276
|
await asyncio.sleep(timeout)
|
|
279
277
|
|
|
280
278
|
if self.process and self.process.returncode is None:
|
|
@@ -282,7 +280,7 @@ class AsyncProcessContainer:
|
|
|
282
280
|
await self.kill()
|
|
283
281
|
|
|
284
282
|
async def _cache_process_info(self):
|
|
285
|
-
"""Cache process info in Redis"""
|
|
283
|
+
"""Cache process info in Redis."""
|
|
286
284
|
if not self.redis_client:
|
|
287
285
|
return
|
|
288
286
|
|
|
@@ -306,7 +304,7 @@ class AsyncProcessContainer:
|
|
|
306
304
|
|
|
307
305
|
|
|
308
306
|
class AsyncProcessManager:
|
|
309
|
-
"""High-performance async process manager with SQLite and Redis"""
|
|
307
|
+
"""High-performance async process manager with SQLite and Redis."""
|
|
310
308
|
|
|
311
309
|
def __init__(self, db_path: Optional[str] = None, redis_url: Optional[str] = None):
|
|
312
310
|
if db_path is None:
|
|
@@ -325,13 +323,13 @@ class AsyncProcessManager:
|
|
|
325
323
|
self._db_pool_lock = asyncio.Lock()
|
|
326
324
|
|
|
327
325
|
async def initialize(self):
|
|
328
|
-
"""Initialize the process manager"""
|
|
326
|
+
"""Initialize the process manager."""
|
|
329
327
|
await self._init_database()
|
|
330
328
|
await self._init_redis()
|
|
331
329
|
await self._init_db_pool()
|
|
332
330
|
|
|
333
331
|
async def _init_database(self):
|
|
334
|
-
"""Initialize SQLite database with optimizations"""
|
|
332
|
+
"""Initialize SQLite database with optimizations."""
|
|
335
333
|
async with aiosqlite.connect(self.db_path) as db:
|
|
336
334
|
# Enable WAL mode for better concurrency
|
|
337
335
|
await db.execute("PRAGMA journal_mode=WAL")
|
|
@@ -370,7 +368,7 @@ class AsyncProcessManager:
|
|
|
370
368
|
await db.commit()
|
|
371
369
|
|
|
372
370
|
async def _init_redis(self):
|
|
373
|
-
"""Initialize Redis connection for caching"""
|
|
371
|
+
"""Initialize Redis connection for caching."""
|
|
374
372
|
try:
|
|
375
373
|
self.redis_client = redis.from_url(self.redis_url, decode_responses=True)
|
|
376
374
|
await self.redis_client.ping()
|
|
@@ -380,7 +378,7 @@ class AsyncProcessManager:
|
|
|
380
378
|
self.redis_client = None
|
|
381
379
|
|
|
382
380
|
async def _init_db_pool(self):
|
|
383
|
-
"""Initialize connection pool for SQLite"""
|
|
381
|
+
"""Initialize connection pool for SQLite."""
|
|
384
382
|
async with self._db_pool_lock:
|
|
385
383
|
for _ in range(self._db_pool_size):
|
|
386
384
|
conn = await aiosqlite.connect(self.db_path)
|
|
@@ -389,7 +387,7 @@ class AsyncProcessManager:
|
|
|
389
387
|
|
|
390
388
|
@asynccontextmanager
|
|
391
389
|
async def _get_db_connection(self):
|
|
392
|
-
"""Get a database connection from the pool"""
|
|
390
|
+
"""Get a database connection from the pool."""
|
|
393
391
|
async with self._db_pool_lock:
|
|
394
392
|
if self._db_pool:
|
|
395
393
|
conn = self._db_pool.pop()
|
|
@@ -415,7 +413,7 @@ class AsyncProcessManager:
|
|
|
415
413
|
environment: Optional[Dict[str, str]] = None,
|
|
416
414
|
timeout: Optional[float] = None,
|
|
417
415
|
) -> str:
|
|
418
|
-
"""Start a new async process"""
|
|
416
|
+
"""Start a new async process."""
|
|
419
417
|
process_info = ProcessInfo(
|
|
420
418
|
id=str(uuid.uuid4()),
|
|
421
419
|
name=name,
|
|
@@ -442,7 +440,7 @@ class AsyncProcessManager:
|
|
|
442
440
|
raise RuntimeError(f"Failed to start process: {name}")
|
|
443
441
|
|
|
444
442
|
async def stop_process(self, process_id: str, timeout: float = 10.0) -> bool:
|
|
445
|
-
"""Stop a process gracefully"""
|
|
443
|
+
"""Stop a process gracefully."""
|
|
446
444
|
if process_id not in self.processes:
|
|
447
445
|
raise KeyError(f"Process not found: {process_id}")
|
|
448
446
|
|
|
@@ -455,7 +453,7 @@ class AsyncProcessManager:
|
|
|
455
453
|
return success
|
|
456
454
|
|
|
457
455
|
async def kill_process(self, process_id: str) -> bool:
|
|
458
|
-
"""Force kill a process"""
|
|
456
|
+
"""Force kill a process."""
|
|
459
457
|
if process_id not in self.processes:
|
|
460
458
|
raise KeyError(f"Process not found: {process_id}")
|
|
461
459
|
|
|
@@ -468,7 +466,7 @@ class AsyncProcessManager:
|
|
|
468
466
|
return success
|
|
469
467
|
|
|
470
468
|
async def get_process_info(self, process_id: str) -> ProcessInfo:
|
|
471
|
-
"""Get process information"""
|
|
469
|
+
"""Get process information."""
|
|
472
470
|
if process_id in self.processes:
|
|
473
471
|
return self.processes[process_id].info
|
|
474
472
|
|
|
@@ -482,7 +480,7 @@ class AsyncProcessManager:
|
|
|
482
480
|
raise KeyError(f"Process not found: {process_id}")
|
|
483
481
|
|
|
484
482
|
async def list_processes(self, status_filter: Optional[str] = None) -> List[ProcessInfo]:
|
|
485
|
-
"""List all processes with optional status filter"""
|
|
483
|
+
"""List all processes with optional status filter."""
|
|
486
484
|
processes = []
|
|
487
485
|
|
|
488
486
|
# Add active processes
|
|
@@ -511,7 +509,7 @@ class AsyncProcessManager:
|
|
|
511
509
|
return processes
|
|
512
510
|
|
|
513
511
|
async def cleanup_finished(self) -> List[str]:
|
|
514
|
-
"""Remove finished processes from memory"""
|
|
512
|
+
"""Remove finished processes from memory."""
|
|
515
513
|
finished_ids = []
|
|
516
514
|
|
|
517
515
|
for process_id, container in list(self.processes.items()):
|
|
@@ -527,7 +525,7 @@ class AsyncProcessManager:
|
|
|
527
525
|
return finished_ids
|
|
528
526
|
|
|
529
527
|
async def _save_process_info(self, process_info: ProcessInfo):
|
|
530
|
-
"""Save process info to database"""
|
|
528
|
+
"""Save process info to database."""
|
|
531
529
|
async with self._get_db_connection() as db:
|
|
532
530
|
await db.execute(
|
|
533
531
|
"""
|
|
@@ -556,7 +554,7 @@ class AsyncProcessManager:
|
|
|
556
554
|
await db.commit()
|
|
557
555
|
|
|
558
556
|
def _row_to_process_info(self, row) -> ProcessInfo:
|
|
559
|
-
"""Convert database row to ProcessInfo"""
|
|
557
|
+
"""Convert database row to ProcessInfo."""
|
|
560
558
|
return ProcessInfo(
|
|
561
559
|
id=row[0],
|
|
562
560
|
name=row[1],
|
|
@@ -575,7 +573,7 @@ class AsyncProcessManager:
|
|
|
575
573
|
)
|
|
576
574
|
|
|
577
575
|
async def close(self):
|
|
578
|
-
"""Clean up resources"""
|
|
576
|
+
"""Clean up resources."""
|
|
579
577
|
# Close all active processes
|
|
580
578
|
for container in self.processes.values():
|
|
581
579
|
await container.stop()
|
mcli/workflow/daemon/client.py
CHANGED
|
@@ -1,8 +1,3 @@
|
|
|
1
|
-
import json
|
|
2
|
-
import os
|
|
3
|
-
import subprocess
|
|
4
|
-
import sys
|
|
5
|
-
import tempfile
|
|
6
1
|
import uuid
|
|
7
2
|
from datetime import datetime
|
|
8
3
|
from pathlib import Path
|
|
@@ -17,11 +12,10 @@ from .commands import Command, CommandDatabase, CommandExecutor
|
|
|
17
12
|
@click.group()
|
|
18
13
|
def client():
|
|
19
14
|
"""Client CLI for daemon commands."""
|
|
20
|
-
pass
|
|
21
15
|
|
|
22
16
|
|
|
23
17
|
class DaemonClient:
|
|
24
|
-
"""Client interface for interacting with the daemon service"""
|
|
18
|
+
"""Client interface for interacting with the daemon service."""
|
|
25
19
|
|
|
26
20
|
def __init__(self, timeout: int = 30):
|
|
27
21
|
self.db = CommandDatabase()
|
|
@@ -30,7 +24,7 @@ class DaemonClient:
|
|
|
30
24
|
self._validate_connection()
|
|
31
25
|
|
|
32
26
|
def _validate_connection(self):
|
|
33
|
-
"""Verify connection to the daemon service"""
|
|
27
|
+
"""Verify connection to the daemon service."""
|
|
34
28
|
try:
|
|
35
29
|
self.db.get_all_commands() # Simple query to test connection
|
|
36
30
|
except Exception as e:
|
|
@@ -45,7 +39,7 @@ class DaemonClient:
|
|
|
45
39
|
group: Optional[str] = None,
|
|
46
40
|
tags: Optional[List[str]] = None,
|
|
47
41
|
) -> str:
|
|
48
|
-
"""Add a command from a file"""
|
|
42
|
+
"""Add a command from a file."""
|
|
49
43
|
# Read the file
|
|
50
44
|
with open(file_path, "r") as f:
|
|
51
45
|
code = f.read()
|
|
@@ -84,12 +78,12 @@ class DaemonClient:
|
|
|
84
78
|
group: Optional[str] = None,
|
|
85
79
|
tags: Optional[List[str]] = None,
|
|
86
80
|
) -> str:
|
|
87
|
-
"""Add a command from stdin"""
|
|
81
|
+
"""Add a command from stdin."""
|
|
88
82
|
click.echo("Enter your code (Ctrl+D when done):")
|
|
89
83
|
|
|
90
84
|
# Read from stdin
|
|
91
85
|
lines = []
|
|
92
|
-
try:
|
|
86
|
+
try: # noqa: SIM105
|
|
93
87
|
while True:
|
|
94
88
|
line = input()
|
|
95
89
|
lines.append(line)
|
|
@@ -105,7 +99,7 @@ class DaemonClient:
|
|
|
105
99
|
command = Command(
|
|
106
100
|
id=str(uuid.uuid4()),
|
|
107
101
|
name=name,
|
|
108
|
-
description=description or
|
|
102
|
+
description=description or "Command from stdin",
|
|
109
103
|
code=code,
|
|
110
104
|
language=language,
|
|
111
105
|
group=group,
|
|
@@ -116,13 +110,13 @@ class DaemonClient:
|
|
|
116
110
|
return self.db.add_command(command)
|
|
117
111
|
|
|
118
112
|
def add_command_interactive(self) -> Optional[str]:
|
|
119
|
-
"""Interactive command creation"""
|
|
113
|
+
"""Interactive command creation."""
|
|
120
114
|
# Get command name
|
|
121
115
|
name = click.prompt("Command name", type=str)
|
|
122
116
|
|
|
123
117
|
# Check if name already exists
|
|
124
118
|
existing = self.db.search_commands(name, limit=1)
|
|
125
|
-
if existing and existing[0].name == name:
|
|
119
|
+
if existing and existing[0].name == name: # noqa: SIM102
|
|
126
120
|
if not click.confirm(f"Command '{name}' already exists. Overwrite?"):
|
|
127
121
|
return None
|
|
128
122
|
|
|
@@ -156,7 +150,7 @@ class DaemonClient:
|
|
|
156
150
|
else: # paste
|
|
157
151
|
click.echo("Paste your code below (Ctrl+D when done):")
|
|
158
152
|
lines = []
|
|
159
|
-
try:
|
|
153
|
+
try: # noqa: SIM105
|
|
160
154
|
while True:
|
|
161
155
|
line = input()
|
|
162
156
|
lines.append(line)
|
|
@@ -172,7 +166,7 @@ class DaemonClient:
|
|
|
172
166
|
command = Command(
|
|
173
167
|
id=str(uuid.uuid4()),
|
|
174
168
|
name=name,
|
|
175
|
-
description=description or
|
|
169
|
+
description=description or "Command from paste",
|
|
176
170
|
code=code,
|
|
177
171
|
language=language,
|
|
178
172
|
group=group,
|
|
@@ -183,7 +177,7 @@ class DaemonClient:
|
|
|
183
177
|
return self.db.add_command(command)
|
|
184
178
|
|
|
185
179
|
def execute_command(self, command_id: str, args: List[str] = None) -> Dict[str, Any]:
|
|
186
|
-
"""Execute a command"""
|
|
180
|
+
"""Execute a command."""
|
|
187
181
|
command = self.db.get_command(command_id)
|
|
188
182
|
if not command:
|
|
189
183
|
raise ValueError(f"Command '{command_id}' not found")
|
|
@@ -203,27 +197,27 @@ class DaemonClient:
|
|
|
203
197
|
return result
|
|
204
198
|
|
|
205
199
|
def search_commands(self, query: str, limit: int = 10) -> List[Command]:
|
|
206
|
-
"""Search for commands"""
|
|
200
|
+
"""Search for commands."""
|
|
207
201
|
return self.db.search_commands(query, limit)
|
|
208
202
|
|
|
209
203
|
def find_similar_commands(self, query: str, limit: int = 5) -> List[tuple]:
|
|
210
|
-
"""Find similar commands using cosine similarity"""
|
|
204
|
+
"""Find similar commands using cosine similarity."""
|
|
211
205
|
return self.db.find_similar_commands(query, limit)
|
|
212
206
|
|
|
213
207
|
def get_all_commands(self) -> List[Command]:
|
|
214
|
-
"""Get all commands"""
|
|
208
|
+
"""Get all commands."""
|
|
215
209
|
return self.db.get_all_commands()
|
|
216
210
|
|
|
217
211
|
def get_command(self, command_id: str) -> Optional[Command]:
|
|
218
|
-
"""Get a command by ID"""
|
|
212
|
+
"""Get a command by ID."""
|
|
219
213
|
return self.db.get_command(command_id)
|
|
220
214
|
|
|
221
215
|
def update_command(self, command: Command) -> bool:
|
|
222
|
-
"""Update a command"""
|
|
216
|
+
"""Update a command."""
|
|
223
217
|
return self.db.update_command(command)
|
|
224
218
|
|
|
225
219
|
def delete_command(self, command_id: str) -> bool:
|
|
226
|
-
"""Delete a command"""
|
|
220
|
+
"""Delete a command."""
|
|
227
221
|
return self.db.delete_command(command_id)
|
|
228
222
|
|
|
229
223
|
|
|
@@ -241,7 +235,7 @@ class DaemonClient:
|
|
|
241
235
|
@click.option("--group", help="Command group")
|
|
242
236
|
@click.option("--tags", help="Comma-separated tags")
|
|
243
237
|
def add_file(name: str, file_path: str, description: str, language: str, group: str, tags: str):
|
|
244
|
-
"""Add a command from a file"""
|
|
238
|
+
"""Add a command from a file."""
|
|
245
239
|
client = DaemonClient()
|
|
246
240
|
|
|
247
241
|
# Parse tags
|
|
@@ -273,7 +267,7 @@ def add_file(name: str, file_path: str, description: str, language: str, group:
|
|
|
273
267
|
@click.option("--group", help="Command group")
|
|
274
268
|
@click.option("--tags", help="Comma-separated tags")
|
|
275
269
|
def add_stdin(name: str, description: str, language: str, group: str, tags: str):
|
|
276
|
-
"""Add a command from stdin"""
|
|
270
|
+
"""Add a command from stdin."""
|
|
277
271
|
client = DaemonClient()
|
|
278
272
|
|
|
279
273
|
# Parse tags
|
|
@@ -290,7 +284,7 @@ def add_stdin(name: str, description: str, language: str, group: str, tags: str)
|
|
|
290
284
|
|
|
291
285
|
@client.command()
|
|
292
286
|
def add_interactive():
|
|
293
|
-
"""Add a command interactively"""
|
|
287
|
+
"""Add a command interactively."""
|
|
294
288
|
client = DaemonClient()
|
|
295
289
|
|
|
296
290
|
try:
|
|
@@ -307,7 +301,7 @@ def add_interactive():
|
|
|
307
301
|
@click.argument("command_id")
|
|
308
302
|
@click.argument("args", nargs=-1)
|
|
309
303
|
def execute(command_id: str, args: List[str]):
|
|
310
|
-
"""Execute a command"""
|
|
304
|
+
"""Execute a command."""
|
|
311
305
|
client = DaemonClient()
|
|
312
306
|
|
|
313
307
|
try:
|
|
@@ -334,7 +328,7 @@ def execute(command_id: str, args: List[str]):
|
|
|
334
328
|
@click.option("--limit", default=10, help="Maximum number of results")
|
|
335
329
|
@click.option("--similar", is_flag=True, help="Use similarity search")
|
|
336
330
|
def search(query: str, limit: int, similar: bool):
|
|
337
|
-
"""Search for commands"""
|
|
331
|
+
"""Search for commands."""
|
|
338
332
|
client = DaemonClient()
|
|
339
333
|
|
|
340
334
|
try:
|
|
@@ -370,7 +364,7 @@ def search(query: str, limit: int, similar: bool):
|
|
|
370
364
|
@click.option("--group", help="Filter by group")
|
|
371
365
|
@click.option("--language", help="Filter by language")
|
|
372
366
|
def list(group: str, language: str):
|
|
373
|
-
"""List all commands"""
|
|
367
|
+
"""List all commands."""
|
|
374
368
|
client = DaemonClient()
|
|
375
369
|
|
|
376
370
|
try:
|
|
@@ -403,7 +397,7 @@ def list(group: str, language: str):
|
|
|
403
397
|
@client.command()
|
|
404
398
|
@click.argument("command_id")
|
|
405
399
|
def show(command_id: str):
|
|
406
|
-
"""Show command details"""
|
|
400
|
+
"""Show command details."""
|
|
407
401
|
client = DaemonClient()
|
|
408
402
|
|
|
409
403
|
try:
|
|
@@ -438,7 +432,7 @@ def show(command_id: str):
|
|
|
438
432
|
@client.command()
|
|
439
433
|
@click.argument("command_id")
|
|
440
434
|
def delete(command_id: str):
|
|
441
|
-
"""Delete a command"""
|
|
435
|
+
"""Delete a command."""
|
|
442
436
|
client = DaemonClient()
|
|
443
437
|
|
|
444
438
|
try:
|
|
@@ -466,7 +460,7 @@ def delete(command_id: str):
|
|
|
466
460
|
@click.option("--group", help="New group")
|
|
467
461
|
@click.option("--tags", help="New tags (comma-separated)")
|
|
468
462
|
def edit(command_id: str, name: str, description: str, group: str, tags: str):
|
|
469
|
-
"""Edit a command"""
|
|
463
|
+
"""Edit a command."""
|
|
470
464
|
client = DaemonClient()
|
|
471
465
|
|
|
472
466
|
try:
|
|
@@ -498,7 +492,7 @@ def edit(command_id: str, name: str, description: str, group: str, tags: str):
|
|
|
498
492
|
|
|
499
493
|
@client.command()
|
|
500
494
|
def groups():
|
|
501
|
-
"""List all command groups"""
|
|
495
|
+
"""List all command groups."""
|
|
502
496
|
client = DaemonClient()
|
|
503
497
|
|
|
504
498
|
try:
|