mcli-framework 7.0.0__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/chat_cmd.py +42 -0
- mcli/app/commands_cmd.py +226 -0
- mcli/app/completion_cmd.py +216 -0
- mcli/app/completion_helpers.py +288 -0
- mcli/app/cron_test_cmd.py +697 -0
- mcli/app/logs_cmd.py +419 -0
- mcli/app/main.py +492 -0
- mcli/app/model/model.py +1060 -0
- mcli/app/model_cmd.py +227 -0
- mcli/app/redis_cmd.py +269 -0
- mcli/app/video/video.py +1114 -0
- mcli/app/visual_cmd.py +303 -0
- mcli/chat/chat.py +2409 -0
- mcli/chat/command_rag.py +514 -0
- mcli/chat/enhanced_chat.py +652 -0
- mcli/chat/system_controller.py +1010 -0
- mcli/chat/system_integration.py +1016 -0
- mcli/cli.py +25 -0
- mcli/config.toml +20 -0
- mcli/lib/api/api.py +586 -0
- mcli/lib/api/daemon_client.py +203 -0
- mcli/lib/api/daemon_client_local.py +44 -0
- mcli/lib/api/daemon_decorator.py +217 -0
- mcli/lib/api/mcli_decorators.py +1032 -0
- mcli/lib/auth/auth.py +85 -0
- mcli/lib/auth/aws_manager.py +85 -0
- mcli/lib/auth/azure_manager.py +91 -0
- mcli/lib/auth/credential_manager.py +192 -0
- mcli/lib/auth/gcp_manager.py +93 -0
- mcli/lib/auth/key_manager.py +117 -0
- mcli/lib/auth/mcli_manager.py +93 -0
- mcli/lib/auth/token_manager.py +75 -0
- mcli/lib/auth/token_util.py +1011 -0
- mcli/lib/config/config.py +47 -0
- mcli/lib/discovery/__init__.py +1 -0
- mcli/lib/discovery/command_discovery.py +274 -0
- mcli/lib/erd/erd.py +1345 -0
- mcli/lib/erd/generate_graph.py +453 -0
- mcli/lib/files/files.py +76 -0
- mcli/lib/fs/fs.py +109 -0
- mcli/lib/lib.py +29 -0
- mcli/lib/logger/logger.py +611 -0
- mcli/lib/performance/optimizer.py +409 -0
- mcli/lib/performance/rust_bridge.py +502 -0
- mcli/lib/performance/uvloop_config.py +154 -0
- mcli/lib/pickles/pickles.py +50 -0
- mcli/lib/search/cached_vectorizer.py +479 -0
- mcli/lib/services/data_pipeline.py +460 -0
- mcli/lib/services/lsh_client.py +441 -0
- mcli/lib/services/redis_service.py +387 -0
- mcli/lib/shell/shell.py +137 -0
- mcli/lib/toml/toml.py +33 -0
- mcli/lib/ui/styling.py +47 -0
- mcli/lib/ui/visual_effects.py +634 -0
- mcli/lib/watcher/watcher.py +185 -0
- mcli/ml/api/app.py +215 -0
- mcli/ml/api/middleware.py +224 -0
- mcli/ml/api/routers/admin_router.py +12 -0
- mcli/ml/api/routers/auth_router.py +244 -0
- mcli/ml/api/routers/backtest_router.py +12 -0
- mcli/ml/api/routers/data_router.py +12 -0
- mcli/ml/api/routers/model_router.py +302 -0
- mcli/ml/api/routers/monitoring_router.py +12 -0
- mcli/ml/api/routers/portfolio_router.py +12 -0
- mcli/ml/api/routers/prediction_router.py +267 -0
- mcli/ml/api/routers/trade_router.py +12 -0
- mcli/ml/api/routers/websocket_router.py +76 -0
- mcli/ml/api/schemas.py +64 -0
- mcli/ml/auth/auth_manager.py +425 -0
- mcli/ml/auth/models.py +154 -0
- mcli/ml/auth/permissions.py +302 -0
- mcli/ml/backtesting/backtest_engine.py +502 -0
- mcli/ml/backtesting/performance_metrics.py +393 -0
- mcli/ml/cache.py +400 -0
- mcli/ml/cli/main.py +398 -0
- mcli/ml/config/settings.py +394 -0
- mcli/ml/configs/dvc_config.py +230 -0
- mcli/ml/configs/mlflow_config.py +131 -0
- mcli/ml/configs/mlops_manager.py +293 -0
- mcli/ml/dashboard/app.py +532 -0
- mcli/ml/dashboard/app_integrated.py +738 -0
- mcli/ml/dashboard/app_supabase.py +560 -0
- mcli/ml/dashboard/app_training.py +615 -0
- mcli/ml/dashboard/cli.py +51 -0
- mcli/ml/data_ingestion/api_connectors.py +501 -0
- mcli/ml/data_ingestion/data_pipeline.py +567 -0
- mcli/ml/data_ingestion/stream_processor.py +512 -0
- mcli/ml/database/migrations/env.py +94 -0
- mcli/ml/database/models.py +667 -0
- mcli/ml/database/session.py +200 -0
- mcli/ml/experimentation/ab_testing.py +845 -0
- mcli/ml/features/ensemble_features.py +607 -0
- mcli/ml/features/political_features.py +676 -0
- mcli/ml/features/recommendation_engine.py +809 -0
- mcli/ml/features/stock_features.py +573 -0
- mcli/ml/features/test_feature_engineering.py +346 -0
- mcli/ml/logging.py +85 -0
- mcli/ml/mlops/data_versioning.py +518 -0
- mcli/ml/mlops/experiment_tracker.py +377 -0
- mcli/ml/mlops/model_serving.py +481 -0
- mcli/ml/mlops/pipeline_orchestrator.py +614 -0
- mcli/ml/models/base_models.py +324 -0
- mcli/ml/models/ensemble_models.py +675 -0
- mcli/ml/models/recommendation_models.py +474 -0
- mcli/ml/models/test_models.py +487 -0
- mcli/ml/monitoring/drift_detection.py +676 -0
- mcli/ml/monitoring/metrics.py +45 -0
- mcli/ml/optimization/portfolio_optimizer.py +834 -0
- mcli/ml/preprocessing/data_cleaners.py +451 -0
- mcli/ml/preprocessing/feature_extractors.py +491 -0
- mcli/ml/preprocessing/ml_pipeline.py +382 -0
- mcli/ml/preprocessing/politician_trading_preprocessor.py +569 -0
- mcli/ml/preprocessing/test_preprocessing.py +294 -0
- mcli/ml/scripts/populate_sample_data.py +200 -0
- mcli/ml/tasks.py +400 -0
- mcli/ml/tests/test_integration.py +429 -0
- mcli/ml/tests/test_training_dashboard.py +387 -0
- mcli/public/oi/oi.py +15 -0
- mcli/public/public.py +4 -0
- mcli/self/self_cmd.py +1246 -0
- mcli/workflow/daemon/api_daemon.py +800 -0
- mcli/workflow/daemon/async_command_database.py +681 -0
- mcli/workflow/daemon/async_process_manager.py +591 -0
- mcli/workflow/daemon/client.py +530 -0
- mcli/workflow/daemon/commands.py +1196 -0
- mcli/workflow/daemon/daemon.py +905 -0
- mcli/workflow/daemon/daemon_api.py +59 -0
- mcli/workflow/daemon/enhanced_daemon.py +571 -0
- mcli/workflow/daemon/process_cli.py +244 -0
- mcli/workflow/daemon/process_manager.py +439 -0
- mcli/workflow/daemon/test_daemon.py +275 -0
- mcli/workflow/dashboard/dashboard_cmd.py +113 -0
- mcli/workflow/docker/docker.py +0 -0
- mcli/workflow/file/file.py +100 -0
- mcli/workflow/gcloud/config.toml +21 -0
- mcli/workflow/gcloud/gcloud.py +58 -0
- mcli/workflow/git_commit/ai_service.py +328 -0
- mcli/workflow/git_commit/commands.py +430 -0
- mcli/workflow/lsh_integration.py +355 -0
- mcli/workflow/model_service/client.py +594 -0
- mcli/workflow/model_service/download_and_run_efficient_models.py +288 -0
- mcli/workflow/model_service/lightweight_embedder.py +397 -0
- mcli/workflow/model_service/lightweight_model_server.py +714 -0
- mcli/workflow/model_service/lightweight_test.py +241 -0
- mcli/workflow/model_service/model_service.py +1955 -0
- mcli/workflow/model_service/ollama_efficient_runner.py +425 -0
- mcli/workflow/model_service/pdf_processor.py +386 -0
- mcli/workflow/model_service/test_efficient_runner.py +234 -0
- mcli/workflow/model_service/test_example.py +315 -0
- mcli/workflow/model_service/test_integration.py +131 -0
- mcli/workflow/model_service/test_new_features.py +149 -0
- mcli/workflow/openai/openai.py +99 -0
- mcli/workflow/politician_trading/commands.py +1790 -0
- mcli/workflow/politician_trading/config.py +134 -0
- mcli/workflow/politician_trading/connectivity.py +490 -0
- mcli/workflow/politician_trading/data_sources.py +395 -0
- mcli/workflow/politician_trading/database.py +410 -0
- mcli/workflow/politician_trading/demo.py +248 -0
- mcli/workflow/politician_trading/models.py +165 -0
- mcli/workflow/politician_trading/monitoring.py +413 -0
- mcli/workflow/politician_trading/scrapers.py +966 -0
- mcli/workflow/politician_trading/scrapers_california.py +412 -0
- mcli/workflow/politician_trading/scrapers_eu.py +377 -0
- mcli/workflow/politician_trading/scrapers_uk.py +350 -0
- mcli/workflow/politician_trading/scrapers_us_states.py +438 -0
- mcli/workflow/politician_trading/supabase_functions.py +354 -0
- mcli/workflow/politician_trading/workflow.py +852 -0
- mcli/workflow/registry/registry.py +180 -0
- mcli/workflow/repo/repo.py +223 -0
- mcli/workflow/scheduler/commands.py +493 -0
- mcli/workflow/scheduler/cron_parser.py +238 -0
- mcli/workflow/scheduler/job.py +182 -0
- mcli/workflow/scheduler/monitor.py +139 -0
- mcli/workflow/scheduler/persistence.py +324 -0
- mcli/workflow/scheduler/scheduler.py +679 -0
- mcli/workflow/sync/sync_cmd.py +437 -0
- mcli/workflow/sync/test_cmd.py +314 -0
- mcli/workflow/videos/videos.py +242 -0
- mcli/workflow/wakatime/wakatime.py +11 -0
- mcli/workflow/workflow.py +37 -0
- mcli_framework-7.0.0.dist-info/METADATA +479 -0
- mcli_framework-7.0.0.dist-info/RECORD +186 -0
- mcli_framework-7.0.0.dist-info/WHEEL +5 -0
- mcli_framework-7.0.0.dist-info/entry_points.txt +7 -0
- mcli_framework-7.0.0.dist-info/licenses/LICENSE +21 -0
- mcli_framework-7.0.0.dist-info/top_level.txt +1 -0
mcli/app/model_cmd.py
ADDED
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
"""Model management commands for MCLI."""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
import subprocess
|
|
5
|
+
import sys
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import Optional
|
|
8
|
+
|
|
9
|
+
import click
|
|
10
|
+
|
|
11
|
+
from mcli.lib.logger.logger import get_logger
|
|
12
|
+
from mcli.workflow.model_service.lightweight_model_server import (
|
|
13
|
+
LIGHTWEIGHT_MODELS,
|
|
14
|
+
LightweightModelServer,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
logger = get_logger(__name__)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@click.group()
|
|
21
|
+
def model():
|
|
22
|
+
"""Model management commands for offline and online model usage."""
|
|
23
|
+
pass
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@model.command()
|
|
27
|
+
@click.option("--list-available", "-l", is_flag=True, help="List all available lightweight models")
|
|
28
|
+
@click.option("--list-downloaded", "-d", is_flag=True, help="List downloaded models")
|
|
29
|
+
@click.option(
|
|
30
|
+
"--system-info", "-s", is_flag=True, help="Show system information and recommendations"
|
|
31
|
+
)
|
|
32
|
+
def list(list_available: bool, list_downloaded: bool, system_info: bool):
|
|
33
|
+
"""List available and downloaded models."""
|
|
34
|
+
server = LightweightModelServer()
|
|
35
|
+
|
|
36
|
+
if system_info:
|
|
37
|
+
info = server.get_system_info()
|
|
38
|
+
click.echo("๐ฅ๏ธ System Information:")
|
|
39
|
+
click.echo(f" CPU Cores: {info['cpu_count']}")
|
|
40
|
+
click.echo(f" RAM: {info['memory_gb']:.1f} GB")
|
|
41
|
+
click.echo(f" Free Disk: {info['disk_free_gb']:.1f} GB")
|
|
42
|
+
recommended = server.recommend_model()
|
|
43
|
+
click.echo(f" Recommended Model: {recommended}")
|
|
44
|
+
click.echo("")
|
|
45
|
+
|
|
46
|
+
if list_available or (not list_downloaded and not system_info):
|
|
47
|
+
click.echo("๐ Available Lightweight Models:")
|
|
48
|
+
click.echo("=" * 50)
|
|
49
|
+
|
|
50
|
+
downloaded_models = server.downloader.get_downloaded_models()
|
|
51
|
+
|
|
52
|
+
for key, info in LIGHTWEIGHT_MODELS.items():
|
|
53
|
+
status = "โ
Downloaded" if key in downloaded_models else "โณ Available"
|
|
54
|
+
click.echo(f"{status} - {info['name']} ({info['parameters']})")
|
|
55
|
+
click.echo(
|
|
56
|
+
f" Size: {info['size_mb']} MB | Efficiency: {info['efficiency_score']}/10"
|
|
57
|
+
)
|
|
58
|
+
click.echo(f" Type: {info['model_type']} | Tags: {', '.join(info['tags'])}")
|
|
59
|
+
click.echo()
|
|
60
|
+
|
|
61
|
+
if list_downloaded:
|
|
62
|
+
downloaded_models = server.downloader.get_downloaded_models()
|
|
63
|
+
if downloaded_models:
|
|
64
|
+
click.echo("๐ฆ Downloaded Models:")
|
|
65
|
+
click.echo("=" * 30)
|
|
66
|
+
for model in downloaded_models:
|
|
67
|
+
info = LIGHTWEIGHT_MODELS.get(model, {})
|
|
68
|
+
name = info.get("name", model)
|
|
69
|
+
params = info.get("parameters", "Unknown")
|
|
70
|
+
click.echo(f"โ
{name} ({params})")
|
|
71
|
+
else:
|
|
72
|
+
click.echo(
|
|
73
|
+
"No models downloaded yet. Use 'mcli model download <model>' to download a model."
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
@model.command()
|
|
78
|
+
@click.argument("model_name")
|
|
79
|
+
def download(model_name: str):
|
|
80
|
+
"""Download a specific lightweight model."""
|
|
81
|
+
if model_name not in LIGHTWEIGHT_MODELS:
|
|
82
|
+
click.echo(f"โ Model '{model_name}' not found.")
|
|
83
|
+
click.echo("Available models:")
|
|
84
|
+
for key in LIGHTWEIGHT_MODELS.keys():
|
|
85
|
+
click.echo(f" โข {key}")
|
|
86
|
+
sys.exit(1)
|
|
87
|
+
|
|
88
|
+
server = LightweightModelServer()
|
|
89
|
+
|
|
90
|
+
click.echo(f"Downloading model: {model_name}")
|
|
91
|
+
success = server.download_and_load_model(model_name)
|
|
92
|
+
|
|
93
|
+
if success:
|
|
94
|
+
click.echo(f"โ
Successfully downloaded {model_name}")
|
|
95
|
+
else:
|
|
96
|
+
click.echo(f"โ Failed to download {model_name}")
|
|
97
|
+
sys.exit(1)
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
@model.command()
|
|
101
|
+
@click.option("--model", "-m", help="Specific model to use")
|
|
102
|
+
@click.option("--port", "-p", default=8080, help="Port to run server on")
|
|
103
|
+
@click.option(
|
|
104
|
+
"--auto-download",
|
|
105
|
+
is_flag=True,
|
|
106
|
+
default=True,
|
|
107
|
+
help="Automatically download model if not available",
|
|
108
|
+
)
|
|
109
|
+
def start(model: Optional[str], port: int, auto_download: bool):
|
|
110
|
+
"""Start the lightweight model server."""
|
|
111
|
+
server = LightweightModelServer(port=port)
|
|
112
|
+
|
|
113
|
+
# Determine which model to use
|
|
114
|
+
if not model:
|
|
115
|
+
model = server.recommend_model()
|
|
116
|
+
click.echo(f"๐ฏ Using recommended model: {model}")
|
|
117
|
+
elif model not in LIGHTWEIGHT_MODELS:
|
|
118
|
+
click.echo(f"โ Model '{model}' not found.")
|
|
119
|
+
click.echo("Available models:")
|
|
120
|
+
for key in LIGHTWEIGHT_MODELS.keys():
|
|
121
|
+
click.echo(f" โข {key}")
|
|
122
|
+
sys.exit(1)
|
|
123
|
+
|
|
124
|
+
# Check if model is downloaded, download if needed
|
|
125
|
+
downloaded_models = server.downloader.get_downloaded_models()
|
|
126
|
+
if model not in downloaded_models:
|
|
127
|
+
if auto_download:
|
|
128
|
+
click.echo(f"๐ฅ Model {model} not found locally, downloading...")
|
|
129
|
+
success = server.download_and_load_model(model)
|
|
130
|
+
if not success:
|
|
131
|
+
click.echo(f"โ Failed to download {model}")
|
|
132
|
+
sys.exit(1)
|
|
133
|
+
else:
|
|
134
|
+
click.echo(
|
|
135
|
+
f"โ Model {model} not found locally. Use --auto-download to download automatically."
|
|
136
|
+
)
|
|
137
|
+
sys.exit(1)
|
|
138
|
+
else:
|
|
139
|
+
# Load the already downloaded model
|
|
140
|
+
success = server.download_and_load_model(model)
|
|
141
|
+
if not success:
|
|
142
|
+
click.echo(f"โ Failed to load {model}")
|
|
143
|
+
sys.exit(1)
|
|
144
|
+
|
|
145
|
+
# Start server
|
|
146
|
+
click.echo(f"๐ Starting lightweight server on port {port}...")
|
|
147
|
+
server.start_server()
|
|
148
|
+
|
|
149
|
+
click.echo(f"\n๐ Server running at:")
|
|
150
|
+
click.echo(f" - API: http://localhost:{port}")
|
|
151
|
+
click.echo(f" - Health: http://localhost:{port}/health")
|
|
152
|
+
click.echo(f" - Models: http://localhost:{port}/models")
|
|
153
|
+
click.echo(f"\n Press Ctrl+C to stop the server")
|
|
154
|
+
|
|
155
|
+
try:
|
|
156
|
+
# Keep server running
|
|
157
|
+
import time
|
|
158
|
+
|
|
159
|
+
while True:
|
|
160
|
+
time.sleep(1)
|
|
161
|
+
except KeyboardInterrupt:
|
|
162
|
+
click.echo("\n๐ Server stopped")
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
@model.command()
|
|
166
|
+
def recommend():
|
|
167
|
+
"""Get model recommendation based on system capabilities."""
|
|
168
|
+
server = LightweightModelServer()
|
|
169
|
+
recommended = server.recommend_model()
|
|
170
|
+
|
|
171
|
+
info = server.get_system_info()
|
|
172
|
+
click.echo("๐ System Analysis:")
|
|
173
|
+
click.echo(f" CPU Cores: {info['cpu_count']}")
|
|
174
|
+
click.echo(f" RAM: {info['memory_gb']:.1f} GB")
|
|
175
|
+
click.echo(f" Free Disk: {info['disk_free_gb']:.1f} GB")
|
|
176
|
+
click.echo("")
|
|
177
|
+
|
|
178
|
+
model_info = LIGHTWEIGHT_MODELS[recommended]
|
|
179
|
+
click.echo(f"๐ฏ Recommended Model: {recommended}")
|
|
180
|
+
click.echo(f" Name: {model_info['name']}")
|
|
181
|
+
click.echo(f" Description: {model_info['description']}")
|
|
182
|
+
click.echo(f" Parameters: {model_info['parameters']}")
|
|
183
|
+
click.echo(f" Size: {model_info['size_mb']} MB")
|
|
184
|
+
click.echo(f" Efficiency Score: {model_info['efficiency_score']}/10")
|
|
185
|
+
|
|
186
|
+
downloaded_models = server.downloader.get_downloaded_models()
|
|
187
|
+
if recommended not in downloaded_models:
|
|
188
|
+
click.echo(f"\n๐ก To download: mcli model download {recommended}")
|
|
189
|
+
else:
|
|
190
|
+
click.echo(f"\nโ
Model already downloaded")
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
@model.command()
|
|
194
|
+
@click.option("--port", "-p", default=8080, help="Port where server is running")
|
|
195
|
+
def status(port: int):
|
|
196
|
+
"""Check status of the lightweight model server."""
|
|
197
|
+
import requests
|
|
198
|
+
|
|
199
|
+
try:
|
|
200
|
+
response = requests.get(f"http://localhost:{port}/health", timeout=5)
|
|
201
|
+
if response.status_code == 200:
|
|
202
|
+
click.echo(f"โ
Server is running on port {port}")
|
|
203
|
+
|
|
204
|
+
# Get loaded models
|
|
205
|
+
models_response = requests.get(f"http://localhost:{port}/models", timeout=5)
|
|
206
|
+
if models_response.status_code == 200:
|
|
207
|
+
models_data = models_response.json()
|
|
208
|
+
models = models_data.get("models", [])
|
|
209
|
+
if models:
|
|
210
|
+
click.echo(f"๐ค Loaded models ({len(models)}):")
|
|
211
|
+
for model in models:
|
|
212
|
+
click.echo(f" - {model['name']} ({model['parameters']})")
|
|
213
|
+
else:
|
|
214
|
+
click.echo("โ ๏ธ No models currently loaded")
|
|
215
|
+
else:
|
|
216
|
+
click.echo(f"โ Server responded with status {response.status_code}")
|
|
217
|
+
|
|
218
|
+
except requests.exceptions.ConnectionError:
|
|
219
|
+
click.echo(f"โ No server running on port {port}")
|
|
220
|
+
except requests.exceptions.Timeout:
|
|
221
|
+
click.echo(f"โฐ Server on port {port} is not responding")
|
|
222
|
+
except Exception as e:
|
|
223
|
+
click.echo(f"โ Error checking server: {e}")
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
if __name__ == "__main__":
|
|
227
|
+
model()
|
mcli/app/redis_cmd.py
ADDED
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Redis service management commands
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import asyncio
|
|
6
|
+
|
|
7
|
+
import click
|
|
8
|
+
|
|
9
|
+
from mcli.lib.logger.logger import get_logger
|
|
10
|
+
from mcli.lib.services.redis_service import get_redis_service
|
|
11
|
+
|
|
12
|
+
logger = get_logger(__name__)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@click.group(name="redis")
|
|
16
|
+
def redis_group():
|
|
17
|
+
"""Manage Redis cache service"""
|
|
18
|
+
pass
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@redis_group.command(name="start")
|
|
22
|
+
def start_redis():
|
|
23
|
+
"""Start Redis server"""
|
|
24
|
+
|
|
25
|
+
async def _start():
|
|
26
|
+
service = await get_redis_service()
|
|
27
|
+
|
|
28
|
+
if await service.is_running():
|
|
29
|
+
click.echo("โ
Redis is already running")
|
|
30
|
+
status = await service.get_status()
|
|
31
|
+
click.echo(f" Host: {status['host']}")
|
|
32
|
+
click.echo(f" Port: {status['port']}")
|
|
33
|
+
click.echo(f" Uptime: {status.get('uptime', 0)} seconds")
|
|
34
|
+
return
|
|
35
|
+
|
|
36
|
+
click.echo("๐ Starting Redis server...")
|
|
37
|
+
success = await service.start()
|
|
38
|
+
|
|
39
|
+
if success:
|
|
40
|
+
click.echo("โ
Redis server started successfully")
|
|
41
|
+
status = await service.get_status()
|
|
42
|
+
click.echo(f" Connection URL: {await service.get_connection_url()}")
|
|
43
|
+
click.echo(f" Data directory: {status['data_dir']}")
|
|
44
|
+
click.echo(f" Process ID: {status['process_id']}")
|
|
45
|
+
else:
|
|
46
|
+
click.echo("โ Failed to start Redis server")
|
|
47
|
+
click.echo(" Make sure Redis is installed: brew install redis")
|
|
48
|
+
|
|
49
|
+
asyncio.run(_start())
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
@redis_group.command(name="stop")
|
|
53
|
+
def stop_redis():
|
|
54
|
+
"""Stop Redis server"""
|
|
55
|
+
|
|
56
|
+
async def _stop():
|
|
57
|
+
service = await get_redis_service()
|
|
58
|
+
|
|
59
|
+
if not await service.is_running():
|
|
60
|
+
click.echo("โน๏ธ Redis is not running")
|
|
61
|
+
return
|
|
62
|
+
|
|
63
|
+
click.echo("๐ Stopping Redis server...")
|
|
64
|
+
success = await service.stop()
|
|
65
|
+
|
|
66
|
+
if success:
|
|
67
|
+
click.echo("โ
Redis server stopped successfully")
|
|
68
|
+
else:
|
|
69
|
+
click.echo("โ Failed to stop Redis server")
|
|
70
|
+
|
|
71
|
+
asyncio.run(_stop())
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
@redis_group.command(name="restart")
|
|
75
|
+
def restart_redis():
|
|
76
|
+
"""Restart Redis server"""
|
|
77
|
+
|
|
78
|
+
async def _restart():
|
|
79
|
+
service = await get_redis_service()
|
|
80
|
+
|
|
81
|
+
click.echo("๐ Restarting Redis server...")
|
|
82
|
+
success = await service.restart()
|
|
83
|
+
|
|
84
|
+
if success:
|
|
85
|
+
click.echo("โ
Redis server restarted successfully")
|
|
86
|
+
status = await service.get_status()
|
|
87
|
+
click.echo(f" Connection URL: {await service.get_connection_url()}")
|
|
88
|
+
else:
|
|
89
|
+
click.echo("โ Failed to restart Redis server")
|
|
90
|
+
|
|
91
|
+
asyncio.run(_restart())
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
@redis_group.command(name="status")
|
|
95
|
+
def redis_status():
|
|
96
|
+
"""Show Redis server status"""
|
|
97
|
+
|
|
98
|
+
async def _status():
|
|
99
|
+
service = await get_redis_service()
|
|
100
|
+
status = await service.get_status()
|
|
101
|
+
|
|
102
|
+
if status["running"]:
|
|
103
|
+
click.echo("โ
Redis Status: RUNNING")
|
|
104
|
+
click.echo(f" Host: {status['host']}")
|
|
105
|
+
click.echo(f" Port: {status['port']}")
|
|
106
|
+
click.echo(f" Connection URL: {await service.get_connection_url()}")
|
|
107
|
+
click.echo(f" Version: {status.get('version', 'unknown')}")
|
|
108
|
+
click.echo(f" Memory Usage: {status.get('memory_usage', 'unknown')}")
|
|
109
|
+
click.echo(f" Connected Clients: {status.get('connected_clients', 0)}")
|
|
110
|
+
click.echo(f" Uptime: {status.get('uptime', 0)} seconds")
|
|
111
|
+
click.echo(f" Total Commands: {status.get('total_commands', 0):,}")
|
|
112
|
+
click.echo(f" Cache Hits: {status.get('keyspace_hits', 0):,}")
|
|
113
|
+
click.echo(f" Cache Misses: {status.get('keyspace_misses', 0):,}")
|
|
114
|
+
|
|
115
|
+
if status.get("keyspace_hits", 0) + status.get("keyspace_misses", 0) > 0:
|
|
116
|
+
hit_rate = (
|
|
117
|
+
status.get("keyspace_hits", 0)
|
|
118
|
+
/ (status.get("keyspace_hits", 0) + status.get("keyspace_misses", 0))
|
|
119
|
+
* 100
|
|
120
|
+
)
|
|
121
|
+
click.echo(f" Hit Rate: {hit_rate:.1f}%")
|
|
122
|
+
|
|
123
|
+
click.echo(f" Data Directory: {status['data_dir']}")
|
|
124
|
+
click.echo(f" Process ID: {status.get('process_id', 'unknown')}")
|
|
125
|
+
else:
|
|
126
|
+
click.echo("โ Redis Status: NOT RUNNING")
|
|
127
|
+
click.echo(f" Expected Host: {status['host']}")
|
|
128
|
+
click.echo(f" Expected Port: {status['port']}")
|
|
129
|
+
click.echo(f" Data Directory: {status['data_dir']}")
|
|
130
|
+
|
|
131
|
+
asyncio.run(_status())
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
@redis_group.command(name="test")
|
|
135
|
+
def test_redis():
|
|
136
|
+
"""Test Redis connection and performance"""
|
|
137
|
+
|
|
138
|
+
async def _test():
|
|
139
|
+
service = await get_redis_service()
|
|
140
|
+
|
|
141
|
+
if not await service.is_running():
|
|
142
|
+
click.echo("โ Redis is not running. Start it with: mcli redis start")
|
|
143
|
+
return
|
|
144
|
+
|
|
145
|
+
click.echo("๐งช Testing Redis connection...")
|
|
146
|
+
result = await service.test_connection()
|
|
147
|
+
|
|
148
|
+
if result["status"] == "success":
|
|
149
|
+
click.echo("โ
Redis connection test passed")
|
|
150
|
+
click.echo(f" Latency: {result['latency_ms']}ms")
|
|
151
|
+
click.echo(f" Connection URL: {result['connection_url']}")
|
|
152
|
+
click.echo(
|
|
153
|
+
f" Read/Write Test: {'โ
Passed' if result['test_result'] else 'โ Failed'}"
|
|
154
|
+
)
|
|
155
|
+
else:
|
|
156
|
+
click.echo("โ Redis connection test failed")
|
|
157
|
+
click.echo(f" Error: {result.get('error', 'unknown')}")
|
|
158
|
+
click.echo(f" Connection URL: {result.get('connection_url', 'unknown')}")
|
|
159
|
+
|
|
160
|
+
asyncio.run(_test())
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
@redis_group.command(name="info")
|
|
164
|
+
def redis_info():
|
|
165
|
+
"""Show detailed Redis server information"""
|
|
166
|
+
|
|
167
|
+
async def _info():
|
|
168
|
+
service = await get_redis_service()
|
|
169
|
+
|
|
170
|
+
if not await service.is_running():
|
|
171
|
+
click.echo("โ Redis is not running")
|
|
172
|
+
return
|
|
173
|
+
|
|
174
|
+
try:
|
|
175
|
+
if not service.redis_client:
|
|
176
|
+
import redis.asyncio as redis
|
|
177
|
+
|
|
178
|
+
service.redis_client = redis.Redis(
|
|
179
|
+
host=service.host, port=service.port, decode_responses=True
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
info = await service.redis_client.info()
|
|
183
|
+
|
|
184
|
+
click.echo("๐ Redis Server Information")
|
|
185
|
+
click.echo("=" * 40)
|
|
186
|
+
|
|
187
|
+
# Server info
|
|
188
|
+
click.echo(f"Redis Version: {info.get('redis_version', 'unknown')}")
|
|
189
|
+
click.echo(f"Redis Mode: {info.get('redis_mode', 'unknown')}")
|
|
190
|
+
click.echo(f"OS: {info.get('os', 'unknown')}")
|
|
191
|
+
click.echo(f"Architecture: {info.get('arch_bits', 'unknown')} bits")
|
|
192
|
+
click.echo(f"Uptime: {info.get('uptime_in_seconds', 0)} seconds")
|
|
193
|
+
|
|
194
|
+
click.echo()
|
|
195
|
+
click.echo("Memory:")
|
|
196
|
+
click.echo(f" Used Memory: {info.get('used_memory_human', 'unknown')}")
|
|
197
|
+
click.echo(f" Peak Memory: {info.get('used_memory_peak_human', 'unknown')}")
|
|
198
|
+
click.echo(f" Memory Fragmentation: {info.get('mem_fragmentation_ratio', 'unknown')}")
|
|
199
|
+
|
|
200
|
+
click.echo()
|
|
201
|
+
click.echo("Stats:")
|
|
202
|
+
click.echo(f" Total Connections: {info.get('total_connections_received', 0):,}")
|
|
203
|
+
click.echo(f" Total Commands: {info.get('total_commands_processed', 0):,}")
|
|
204
|
+
click.echo(f" Commands/sec: {info.get('instantaneous_ops_per_sec', 0)}")
|
|
205
|
+
click.echo(f" Keyspace Hits: {info.get('keyspace_hits', 0):,}")
|
|
206
|
+
click.echo(f" Keyspace Misses: {info.get('keyspace_misses', 0):,}")
|
|
207
|
+
|
|
208
|
+
if info.get("keyspace_hits", 0) + info.get("keyspace_misses", 0) > 0:
|
|
209
|
+
hit_rate = (
|
|
210
|
+
info.get("keyspace_hits", 0)
|
|
211
|
+
/ (info.get("keyspace_hits", 0) + info.get("keyspace_misses", 0))
|
|
212
|
+
* 100
|
|
213
|
+
)
|
|
214
|
+
click.echo(f" Hit Rate: {hit_rate:.2f}%")
|
|
215
|
+
|
|
216
|
+
click.echo()
|
|
217
|
+
click.echo("Persistence:")
|
|
218
|
+
click.echo(f" RDB Last Save: {info.get('rdb_last_save_time', 'unknown')}")
|
|
219
|
+
click.echo(f" AOF Enabled: {info.get('aof_enabled', 0) == 1}")
|
|
220
|
+
|
|
221
|
+
# Show keyspace info if available
|
|
222
|
+
db_info = {k: v for k, v in info.items() if k.startswith("db")}
|
|
223
|
+
if db_info:
|
|
224
|
+
click.echo()
|
|
225
|
+
click.echo("Databases:")
|
|
226
|
+
for db, stats in db_info.items():
|
|
227
|
+
click.echo(f" {db}: {stats}")
|
|
228
|
+
|
|
229
|
+
except Exception as e:
|
|
230
|
+
click.echo(f"โ Failed to get Redis info: {e}")
|
|
231
|
+
|
|
232
|
+
asyncio.run(_info())
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
@redis_group.command(name="logs")
|
|
236
|
+
@click.option("--lines", "-n", default=20, help="Number of log lines to show")
|
|
237
|
+
def redis_logs(lines):
|
|
238
|
+
"""Show Redis server logs"""
|
|
239
|
+
|
|
240
|
+
async def _logs():
|
|
241
|
+
service = await get_redis_service()
|
|
242
|
+
log_file = service.data_dir / "redis.log"
|
|
243
|
+
|
|
244
|
+
if not log_file.exists():
|
|
245
|
+
click.echo(f"โ Log file not found: {log_file}")
|
|
246
|
+
return
|
|
247
|
+
|
|
248
|
+
try:
|
|
249
|
+
# Read last N lines
|
|
250
|
+
with open(log_file, "r") as f:
|
|
251
|
+
all_lines = f.readlines()
|
|
252
|
+
recent_lines = all_lines[-lines:] if len(all_lines) > lines else all_lines
|
|
253
|
+
|
|
254
|
+
click.echo(f"๐ Redis Logs (last {len(recent_lines)} lines)")
|
|
255
|
+
click.echo("=" * 50)
|
|
256
|
+
|
|
257
|
+
for line in recent_lines:
|
|
258
|
+
click.echo(line.rstrip())
|
|
259
|
+
|
|
260
|
+
except Exception as e:
|
|
261
|
+
click.echo(f"โ Failed to read logs: {e}")
|
|
262
|
+
|
|
263
|
+
asyncio.run(_logs())
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
# Register with main CLI
|
|
267
|
+
def register_redis_commands(cli):
|
|
268
|
+
"""Register Redis commands with the main CLI"""
|
|
269
|
+
cli.add_command(redis_group)
|