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
|
@@ -0,0 +1,502 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Bridge module for integrating Rust extensions with Python code
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import importlib.util
|
|
6
|
+
import os
|
|
7
|
+
import sys
|
|
8
|
+
from typing import Any, Dict, List, Optional, Union
|
|
9
|
+
|
|
10
|
+
from mcli.lib.logger.logger import get_logger
|
|
11
|
+
|
|
12
|
+
logger = get_logger(__name__)
|
|
13
|
+
|
|
14
|
+
# Global flags for Rust extension availability
|
|
15
|
+
_RUST_AVAILABLE = None
|
|
16
|
+
_RUST_TFIDF = None
|
|
17
|
+
_RUST_FILE_WATCHER = None
|
|
18
|
+
_RUST_COMMAND_MATCHER = None
|
|
19
|
+
_RUST_PROCESS_MANAGER = None
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def check_rust_extensions() -> Dict[str, bool]:
|
|
23
|
+
"""Check which Rust extensions are available"""
|
|
24
|
+
global _RUST_AVAILABLE, _RUST_TFIDF, _RUST_FILE_WATCHER, _RUST_COMMAND_MATCHER, _RUST_PROCESS_MANAGER
|
|
25
|
+
|
|
26
|
+
if _RUST_AVAILABLE is not None:
|
|
27
|
+
return {
|
|
28
|
+
"available": _RUST_AVAILABLE,
|
|
29
|
+
"tfidf": _RUST_TFIDF,
|
|
30
|
+
"file_watcher": _RUST_FILE_WATCHER,
|
|
31
|
+
"command_matcher": _RUST_COMMAND_MATCHER,
|
|
32
|
+
"process_manager": _RUST_PROCESS_MANAGER,
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
try:
|
|
36
|
+
import mcli_rust
|
|
37
|
+
|
|
38
|
+
_RUST_AVAILABLE = True
|
|
39
|
+
|
|
40
|
+
# Check individual components
|
|
41
|
+
_RUST_TFIDF = hasattr(mcli_rust, "TfIdfVectorizer")
|
|
42
|
+
_RUST_FILE_WATCHER = hasattr(mcli_rust, "FileWatcher")
|
|
43
|
+
_RUST_COMMAND_MATCHER = hasattr(mcli_rust, "CommandMatcher")
|
|
44
|
+
_RUST_PROCESS_MANAGER = hasattr(mcli_rust, "ProcessManager")
|
|
45
|
+
|
|
46
|
+
logger.info("Rust extensions loaded successfully")
|
|
47
|
+
|
|
48
|
+
except ImportError as e:
|
|
49
|
+
_RUST_AVAILABLE = False
|
|
50
|
+
_RUST_TFIDF = False
|
|
51
|
+
_RUST_FILE_WATCHER = False
|
|
52
|
+
_RUST_COMMAND_MATCHER = False
|
|
53
|
+
_RUST_PROCESS_MANAGER = False
|
|
54
|
+
|
|
55
|
+
logger.warning(f"Rust extensions not available: {e}")
|
|
56
|
+
|
|
57
|
+
return {
|
|
58
|
+
"available": _RUST_AVAILABLE,
|
|
59
|
+
"tfidf": _RUST_TFIDF,
|
|
60
|
+
"file_watcher": _RUST_FILE_WATCHER,
|
|
61
|
+
"command_matcher": _RUST_COMMAND_MATCHER,
|
|
62
|
+
"process_manager": _RUST_PROCESS_MANAGER,
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def get_tfidf_vectorizer(use_rust: bool = True, **kwargs):
|
|
67
|
+
"""Get the best available TF-IDF vectorizer"""
|
|
68
|
+
rust_status = check_rust_extensions()
|
|
69
|
+
|
|
70
|
+
if use_rust and rust_status["tfidf"]:
|
|
71
|
+
try:
|
|
72
|
+
import mcli_rust
|
|
73
|
+
|
|
74
|
+
return mcli_rust.TfIdfVectorizer(**kwargs)
|
|
75
|
+
except Exception as e:
|
|
76
|
+
logger.warning(f"Failed to create Rust TF-IDF vectorizer: {e}")
|
|
77
|
+
|
|
78
|
+
# Fallback to sklearn
|
|
79
|
+
try:
|
|
80
|
+
from sklearn.feature_extraction.text import TfidfVectorizer
|
|
81
|
+
|
|
82
|
+
return TfidfVectorizer(**kwargs)
|
|
83
|
+
except ImportError:
|
|
84
|
+
raise RuntimeError("No TF-IDF vectorizer implementation available")
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def get_file_watcher(use_rust: bool = True):
|
|
88
|
+
"""Get the best available file watcher"""
|
|
89
|
+
rust_status = check_rust_extensions()
|
|
90
|
+
|
|
91
|
+
if use_rust and rust_status["file_watcher"]:
|
|
92
|
+
try:
|
|
93
|
+
import mcli_rust
|
|
94
|
+
|
|
95
|
+
return mcli_rust.FileWatcher()
|
|
96
|
+
except Exception as e:
|
|
97
|
+
logger.warning(f"Failed to create Rust file watcher: {e}")
|
|
98
|
+
|
|
99
|
+
# Fallback to Python watchdog
|
|
100
|
+
try:
|
|
101
|
+
from mcli.lib.watcher.python_watcher import PythonFileWatcher
|
|
102
|
+
|
|
103
|
+
return PythonFileWatcher()
|
|
104
|
+
except ImportError:
|
|
105
|
+
raise RuntimeError("No file watcher implementation available")
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def get_command_matcher(use_rust: bool = True, **kwargs):
|
|
109
|
+
"""Get the best available command matcher"""
|
|
110
|
+
rust_status = check_rust_extensions()
|
|
111
|
+
|
|
112
|
+
if use_rust and rust_status["command_matcher"]:
|
|
113
|
+
try:
|
|
114
|
+
import mcli_rust
|
|
115
|
+
|
|
116
|
+
return mcli_rust.CommandMatcher(**kwargs)
|
|
117
|
+
except Exception as e:
|
|
118
|
+
logger.warning(f"Failed to create Rust command matcher: {e}")
|
|
119
|
+
|
|
120
|
+
# Fallback to Python implementation
|
|
121
|
+
try:
|
|
122
|
+
from mcli.lib.discovery.python_matcher import PythonCommandMatcher
|
|
123
|
+
|
|
124
|
+
return PythonCommandMatcher(**kwargs)
|
|
125
|
+
except ImportError:
|
|
126
|
+
raise RuntimeError("No command matcher implementation available")
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def get_process_manager(use_rust: bool = True):
|
|
130
|
+
"""Get the best available process manager"""
|
|
131
|
+
rust_status = check_rust_extensions()
|
|
132
|
+
|
|
133
|
+
if use_rust and rust_status["process_manager"]:
|
|
134
|
+
try:
|
|
135
|
+
import mcli_rust
|
|
136
|
+
|
|
137
|
+
return mcli_rust.ProcessManager()
|
|
138
|
+
except Exception as e:
|
|
139
|
+
logger.warning(f"Failed to create Rust process manager: {e}")
|
|
140
|
+
|
|
141
|
+
# Fallback to async Python implementation
|
|
142
|
+
try:
|
|
143
|
+
from mcli.workflow.daemon.async_process_manager import AsyncProcessManager
|
|
144
|
+
|
|
145
|
+
return AsyncProcessManager()
|
|
146
|
+
except ImportError:
|
|
147
|
+
raise RuntimeError("No process manager implementation available")
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
class PerformanceMonitor:
|
|
151
|
+
"""Monitor performance differences between Rust and Python implementations"""
|
|
152
|
+
|
|
153
|
+
def __init__(self):
|
|
154
|
+
self.benchmarks = {}
|
|
155
|
+
self.rust_status = check_rust_extensions()
|
|
156
|
+
|
|
157
|
+
def benchmark_tfidf(self, documents: List[str], queries: List[str]) -> Dict[str, Any]:
|
|
158
|
+
"""Benchmark TF-IDF performance"""
|
|
159
|
+
import time
|
|
160
|
+
|
|
161
|
+
results = {"rust": None, "python": None, "speedup": None}
|
|
162
|
+
|
|
163
|
+
# Benchmark Rust implementation
|
|
164
|
+
if self.rust_status["tfidf"]:
|
|
165
|
+
try:
|
|
166
|
+
vectorizer = get_tfidf_vectorizer(use_rust=True)
|
|
167
|
+
|
|
168
|
+
start_time = time.perf_counter()
|
|
169
|
+
vectors = vectorizer.fit_transform(documents)
|
|
170
|
+
for query in queries:
|
|
171
|
+
similarities = vectorizer.similarity(query, documents)
|
|
172
|
+
rust_time = time.perf_counter() - start_time
|
|
173
|
+
|
|
174
|
+
results["rust"] = rust_time
|
|
175
|
+
|
|
176
|
+
except Exception as e:
|
|
177
|
+
logger.warning(f"Rust TF-IDF benchmark failed: {e}")
|
|
178
|
+
|
|
179
|
+
# Benchmark Python implementation
|
|
180
|
+
try:
|
|
181
|
+
vectorizer = get_tfidf_vectorizer(use_rust=False)
|
|
182
|
+
|
|
183
|
+
start_time = time.perf_counter()
|
|
184
|
+
vectors = vectorizer.fit_transform(documents)
|
|
185
|
+
for query in queries:
|
|
186
|
+
query_vec = vectorizer.transform([query])
|
|
187
|
+
# Compute similarities manually for fair comparison
|
|
188
|
+
similarities = []
|
|
189
|
+
for doc_vec in vectors:
|
|
190
|
+
sim = self._cosine_similarity(query_vec[0], doc_vec)
|
|
191
|
+
similarities.append(sim)
|
|
192
|
+
python_time = time.perf_counter() - start_time
|
|
193
|
+
|
|
194
|
+
results["python"] = python_time
|
|
195
|
+
|
|
196
|
+
except Exception as e:
|
|
197
|
+
logger.warning(f"Python TF-IDF benchmark failed: {e}")
|
|
198
|
+
|
|
199
|
+
# Calculate speedup
|
|
200
|
+
if results["rust"] and results["python"]:
|
|
201
|
+
results["speedup"] = results["python"] / results["rust"]
|
|
202
|
+
|
|
203
|
+
return results
|
|
204
|
+
|
|
205
|
+
def _cosine_similarity(self, vec1, vec2):
|
|
206
|
+
"""Simple cosine similarity implementation"""
|
|
207
|
+
import numpy as np
|
|
208
|
+
|
|
209
|
+
dot_product = np.dot(
|
|
210
|
+
vec1.toarray()[0] if hasattr(vec1, "toarray") else vec1,
|
|
211
|
+
vec2.toarray()[0] if hasattr(vec2, "toarray") else vec2,
|
|
212
|
+
)
|
|
213
|
+
norm1 = np.linalg.norm(vec1.toarray()[0] if hasattr(vec1, "toarray") else vec1)
|
|
214
|
+
norm2 = np.linalg.norm(vec2.toarray()[0] if hasattr(vec2, "toarray") else vec2)
|
|
215
|
+
|
|
216
|
+
if norm1 == 0 or norm2 == 0:
|
|
217
|
+
return 0.0
|
|
218
|
+
|
|
219
|
+
return dot_product / (norm1 * norm2)
|
|
220
|
+
|
|
221
|
+
def benchmark_file_watching(self, test_dir: str, num_operations: int = 100) -> Dict[str, Any]:
|
|
222
|
+
"""Benchmark file watching performance"""
|
|
223
|
+
import os
|
|
224
|
+
import tempfile
|
|
225
|
+
import time
|
|
226
|
+
|
|
227
|
+
results = {"rust": None, "python": None, "speedup": None}
|
|
228
|
+
|
|
229
|
+
# Create test directory
|
|
230
|
+
with tempfile.TemporaryDirectory() as temp_dir:
|
|
231
|
+
# Benchmark Rust implementation
|
|
232
|
+
if self.rust_status["file_watcher"]:
|
|
233
|
+
try:
|
|
234
|
+
watcher = get_file_watcher(use_rust=True)
|
|
235
|
+
watcher.start_watching([temp_dir])
|
|
236
|
+
|
|
237
|
+
start_time = time.perf_counter()
|
|
238
|
+
for i in range(num_operations):
|
|
239
|
+
test_file = os.path.join(temp_dir, f"test_{i}.txt")
|
|
240
|
+
with open(test_file, "w") as f:
|
|
241
|
+
f.write(f"test content {i}")
|
|
242
|
+
os.remove(test_file)
|
|
243
|
+
|
|
244
|
+
# Give time for events to be processed
|
|
245
|
+
time.sleep(0.1)
|
|
246
|
+
events = watcher.get_events()
|
|
247
|
+
rust_time = time.perf_counter() - start_time
|
|
248
|
+
|
|
249
|
+
watcher.stop_watching()
|
|
250
|
+
results["rust"] = rust_time
|
|
251
|
+
|
|
252
|
+
except Exception as e:
|
|
253
|
+
logger.warning(f"Rust file watcher benchmark failed: {e}")
|
|
254
|
+
|
|
255
|
+
# Benchmark Python implementation would require similar setup
|
|
256
|
+
# but is more complex due to different API
|
|
257
|
+
|
|
258
|
+
return results
|
|
259
|
+
|
|
260
|
+
def get_system_info(self) -> Dict[str, Any]:
|
|
261
|
+
"""Get system information relevant to performance"""
|
|
262
|
+
import platform
|
|
263
|
+
|
|
264
|
+
import psutil
|
|
265
|
+
|
|
266
|
+
return {
|
|
267
|
+
"platform": platform.platform(),
|
|
268
|
+
"python_version": platform.python_version(),
|
|
269
|
+
"cpu_count": psutil.cpu_count(),
|
|
270
|
+
"memory_total": psutil.virtual_memory().total,
|
|
271
|
+
"rust_extensions": self.rust_status,
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
# Fallback implementations for when Rust extensions are not available
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
class PythonFileWatcher:
|
|
279
|
+
"""Fallback Python file watcher using watchdog"""
|
|
280
|
+
|
|
281
|
+
def __init__(self):
|
|
282
|
+
from watchdog.events import FileSystemEventHandler
|
|
283
|
+
from watchdog.observers import Observer
|
|
284
|
+
|
|
285
|
+
class EventHandler(FileSystemEventHandler):
|
|
286
|
+
def __init__(self):
|
|
287
|
+
self.events = []
|
|
288
|
+
|
|
289
|
+
def on_any_event(self, event):
|
|
290
|
+
self.events.append(
|
|
291
|
+
{
|
|
292
|
+
"event_type": event.event_type,
|
|
293
|
+
"path": event.src_path,
|
|
294
|
+
"is_directory": event.is_directory,
|
|
295
|
+
}
|
|
296
|
+
)
|
|
297
|
+
|
|
298
|
+
self.observer = Observer()
|
|
299
|
+
self.handler = EventHandler()
|
|
300
|
+
self.is_watching = False
|
|
301
|
+
|
|
302
|
+
def start_watching(self, paths: List[str], recursive: bool = True):
|
|
303
|
+
from watchdog.observers import Observer
|
|
304
|
+
|
|
305
|
+
for path in paths:
|
|
306
|
+
self.observer.schedule(self.handler, path, recursive=recursive)
|
|
307
|
+
|
|
308
|
+
self.observer.start()
|
|
309
|
+
self.is_watching = True
|
|
310
|
+
|
|
311
|
+
def stop_watching(self):
|
|
312
|
+
if self.is_watching:
|
|
313
|
+
self.observer.stop()
|
|
314
|
+
self.observer.join()
|
|
315
|
+
self.is_watching = False
|
|
316
|
+
|
|
317
|
+
def get_events(self):
|
|
318
|
+
events = self.handler.events.copy()
|
|
319
|
+
self.handler.events.clear()
|
|
320
|
+
return events
|
|
321
|
+
|
|
322
|
+
|
|
323
|
+
class PythonCommandMatcher:
|
|
324
|
+
"""Fallback Python command matcher"""
|
|
325
|
+
|
|
326
|
+
def __init__(self, fuzzy_threshold: float = 0.3):
|
|
327
|
+
self.fuzzy_threshold = fuzzy_threshold
|
|
328
|
+
self.commands = []
|
|
329
|
+
|
|
330
|
+
def add_commands(self, commands: List[Dict[str, Any]]):
|
|
331
|
+
self.commands.extend(commands)
|
|
332
|
+
|
|
333
|
+
def search(self, query: str, limit: int = 10) -> List[Dict[str, Any]]:
|
|
334
|
+
import re
|
|
335
|
+
|
|
336
|
+
results = []
|
|
337
|
+
query_lower = query.lower()
|
|
338
|
+
|
|
339
|
+
for cmd in self.commands:
|
|
340
|
+
score = 0.0
|
|
341
|
+
matched_fields = []
|
|
342
|
+
|
|
343
|
+
# Exact name match
|
|
344
|
+
if cmd["name"].lower() == query_lower:
|
|
345
|
+
score = 1.0
|
|
346
|
+
matched_fields.append("name")
|
|
347
|
+
# Prefix match
|
|
348
|
+
elif cmd["name"].lower().startswith(query_lower):
|
|
349
|
+
score = 0.9
|
|
350
|
+
matched_fields.append("name")
|
|
351
|
+
# Contains match
|
|
352
|
+
elif query_lower in cmd["name"].lower():
|
|
353
|
+
score = 0.7
|
|
354
|
+
matched_fields.append("name")
|
|
355
|
+
# Description match
|
|
356
|
+
elif query_lower in cmd.get("description", "").lower():
|
|
357
|
+
score = 0.5
|
|
358
|
+
matched_fields.append("description")
|
|
359
|
+
|
|
360
|
+
if score >= self.fuzzy_threshold:
|
|
361
|
+
results.append(
|
|
362
|
+
{
|
|
363
|
+
"command": cmd,
|
|
364
|
+
"score": score,
|
|
365
|
+
"match_type": "python_fallback",
|
|
366
|
+
"matched_fields": matched_fields,
|
|
367
|
+
}
|
|
368
|
+
)
|
|
369
|
+
|
|
370
|
+
results.sort(key=lambda x: x["score"], reverse=True)
|
|
371
|
+
return results[:limit]
|
|
372
|
+
|
|
373
|
+
|
|
374
|
+
# Auto-check Rust extensions on module import
|
|
375
|
+
_RUST_EXTENSIONS_STATUS = check_rust_extensions()
|
|
376
|
+
|
|
377
|
+
|
|
378
|
+
def print_performance_summary():
|
|
379
|
+
"""Print a stunning visual summary of available performance optimizations"""
|
|
380
|
+
try:
|
|
381
|
+
from rich.columns import Columns
|
|
382
|
+
from rich.panel import Panel
|
|
383
|
+
from rich.rule import Rule
|
|
384
|
+
from rich.text import Text
|
|
385
|
+
|
|
386
|
+
from mcli.lib.ui.visual_effects import ColorfulOutput, MCLIBanner, VisualTable, console
|
|
387
|
+
|
|
388
|
+
status = check_rust_extensions()
|
|
389
|
+
|
|
390
|
+
console.print()
|
|
391
|
+
console.print(Rule("🚀 MCLI Performance Optimizations Summary", style="bright_green"))
|
|
392
|
+
console.print()
|
|
393
|
+
|
|
394
|
+
# Check all optimization status
|
|
395
|
+
optimization_data = {
|
|
396
|
+
"rust": {"success": status["available"], "extensions": status},
|
|
397
|
+
"uvloop": {"success": False},
|
|
398
|
+
"redis": {"success": False},
|
|
399
|
+
"aiosqlite": {"success": False},
|
|
400
|
+
"python": {
|
|
401
|
+
"success": True,
|
|
402
|
+
"reason": "GC tuning, bytecode optimization, recursion limit adjustment",
|
|
403
|
+
},
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
# Check UVLoop
|
|
407
|
+
try:
|
|
408
|
+
import uvloop
|
|
409
|
+
|
|
410
|
+
optimization_data["uvloop"]["success"] = True
|
|
411
|
+
optimization_data["uvloop"]["reason"] = "High-performance event loop active"
|
|
412
|
+
except ImportError:
|
|
413
|
+
optimization_data["uvloop"]["reason"] = "Package not installed"
|
|
414
|
+
|
|
415
|
+
# Check Redis
|
|
416
|
+
try:
|
|
417
|
+
import redis
|
|
418
|
+
|
|
419
|
+
# Try to ping Redis server
|
|
420
|
+
client = redis.Redis(host="localhost", port=6379, decode_responses=True)
|
|
421
|
+
client.ping()
|
|
422
|
+
client.close()
|
|
423
|
+
optimization_data["redis"]["success"] = True
|
|
424
|
+
optimization_data["redis"]["reason"] = "Cache server connected"
|
|
425
|
+
except ImportError:
|
|
426
|
+
optimization_data["redis"]["reason"] = "Package not installed"
|
|
427
|
+
except Exception:
|
|
428
|
+
optimization_data["redis"]["reason"] = "Server not available"
|
|
429
|
+
|
|
430
|
+
# Check AIOSQLite
|
|
431
|
+
try:
|
|
432
|
+
import aiosqlite
|
|
433
|
+
|
|
434
|
+
optimization_data["aiosqlite"]["success"] = True
|
|
435
|
+
optimization_data["aiosqlite"]["reason"] = "Async database operations enabled"
|
|
436
|
+
except ImportError:
|
|
437
|
+
optimization_data["aiosqlite"]["reason"] = "Package not installed"
|
|
438
|
+
|
|
439
|
+
# Show performance table
|
|
440
|
+
performance_table = VisualTable.create_performance_table(optimization_data)
|
|
441
|
+
console.print(performance_table)
|
|
442
|
+
console.print()
|
|
443
|
+
|
|
444
|
+
# Show Rust extensions details if available
|
|
445
|
+
if status["available"]:
|
|
446
|
+
rust_table = VisualTable.create_rust_extensions_table(status)
|
|
447
|
+
console.print(rust_table)
|
|
448
|
+
console.print()
|
|
449
|
+
|
|
450
|
+
# Show celebration
|
|
451
|
+
ColorfulOutput.success("All Rust extensions loaded - Maximum performance activated!")
|
|
452
|
+
else:
|
|
453
|
+
ColorfulOutput.warning("Rust extensions not available - Using Python fallbacks")
|
|
454
|
+
console.print()
|
|
455
|
+
|
|
456
|
+
console.print(Rule("System Ready", style="bright_blue"))
|
|
457
|
+
console.print()
|
|
458
|
+
|
|
459
|
+
except ImportError:
|
|
460
|
+
# Fallback to original simple output if visual effects not available
|
|
461
|
+
status = check_rust_extensions()
|
|
462
|
+
|
|
463
|
+
print("\n🚀 MCLI Performance Optimizations Summary:")
|
|
464
|
+
print("=" * 50)
|
|
465
|
+
|
|
466
|
+
if status["available"]:
|
|
467
|
+
print("✅ Rust extensions loaded successfully!")
|
|
468
|
+
print(f" • TF-IDF Vectorizer: {'✅' if status['tfidf'] else '❌'}")
|
|
469
|
+
print(f" • File Watcher: {'✅' if status['file_watcher'] else '❌'}")
|
|
470
|
+
print(f" • Command Matcher: {'✅' if status['command_matcher'] else '❌'}")
|
|
471
|
+
print(f" • Process Manager: {'✅' if status['process_manager'] else '❌'}")
|
|
472
|
+
else:
|
|
473
|
+
print("⚠️ Rust extensions not available - using Python fallbacks")
|
|
474
|
+
|
|
475
|
+
# Check other optimizations
|
|
476
|
+
try:
|
|
477
|
+
import uvloop
|
|
478
|
+
|
|
479
|
+
print("✅ UVLoop available for async performance")
|
|
480
|
+
except ImportError:
|
|
481
|
+
print("⚠️ UVLoop not available - using default asyncio")
|
|
482
|
+
|
|
483
|
+
try:
|
|
484
|
+
import redis
|
|
485
|
+
|
|
486
|
+
print("✅ Redis available for caching")
|
|
487
|
+
except ImportError:
|
|
488
|
+
print("⚠️ Redis not available - caching disabled")
|
|
489
|
+
|
|
490
|
+
try:
|
|
491
|
+
import aiosqlite
|
|
492
|
+
|
|
493
|
+
print("✅ AIOSQLite available for async database operations")
|
|
494
|
+
except ImportError:
|
|
495
|
+
print("⚠️ AIOSQLite not available - using sync SQLite")
|
|
496
|
+
|
|
497
|
+
print()
|
|
498
|
+
|
|
499
|
+
|
|
500
|
+
# Print summary when module is imported (can be enabled with env var)
|
|
501
|
+
if os.environ.get("MCLI_SHOW_PERFORMANCE_SUMMARY", "0").lower() not in ("0", "false", "no"):
|
|
502
|
+
print_performance_summary()
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
"""
|
|
2
|
+
UVLoop configuration for enhanced asyncio performance
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import asyncio
|
|
6
|
+
import os
|
|
7
|
+
import sys
|
|
8
|
+
from typing import Optional
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def install_uvloop(force: bool = False) -> bool:
|
|
12
|
+
"""
|
|
13
|
+
Install uvloop as the default event loop policy for better performance.
|
|
14
|
+
|
|
15
|
+
Args:
|
|
16
|
+
force: Force installation even on non-Unix systems
|
|
17
|
+
|
|
18
|
+
Returns:
|
|
19
|
+
True if uvloop was successfully installed, False otherwise
|
|
20
|
+
"""
|
|
21
|
+
# Check if uvloop should be used
|
|
22
|
+
if not should_use_uvloop() and not force:
|
|
23
|
+
return False
|
|
24
|
+
|
|
25
|
+
try:
|
|
26
|
+
import uvloop
|
|
27
|
+
|
|
28
|
+
# Install uvloop as the default event loop policy
|
|
29
|
+
asyncio.set_event_loop_policy(uvloop.EventLoopPolicy())
|
|
30
|
+
|
|
31
|
+
# Verify installation
|
|
32
|
+
loop = asyncio.new_event_loop()
|
|
33
|
+
is_uvloop = "uvloop" in str(type(loop))
|
|
34
|
+
loop.close()
|
|
35
|
+
|
|
36
|
+
if is_uvloop:
|
|
37
|
+
# Only print if explicitly requested
|
|
38
|
+
if os.environ.get("MCLI_VERBOSE_UVLOOP", "0").lower() in ("1", "true", "yes"):
|
|
39
|
+
print("✓ UVLoop installed successfully for enhanced async performance")
|
|
40
|
+
return True
|
|
41
|
+
else:
|
|
42
|
+
if os.environ.get("MCLI_VERBOSE_UVLOOP", "0").lower() in ("1", "true", "yes"):
|
|
43
|
+
print("⚠ UVLoop installation failed - using default asyncio")
|
|
44
|
+
return False
|
|
45
|
+
|
|
46
|
+
except ImportError:
|
|
47
|
+
if os.environ.get("MCLI_VERBOSE_UVLOOP", "0").lower() in ("1", "true", "yes"):
|
|
48
|
+
print("⚠ UVLoop not available - install with: pip install uvloop")
|
|
49
|
+
return False
|
|
50
|
+
except Exception as e:
|
|
51
|
+
if os.environ.get("MCLI_VERBOSE_UVLOOP", "0").lower() in ("1", "true", "yes"):
|
|
52
|
+
print(f"⚠ UVLoop installation failed: {e}")
|
|
53
|
+
return False
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def should_use_uvloop() -> bool:
|
|
57
|
+
"""
|
|
58
|
+
Determine if uvloop should be used based on platform and environment.
|
|
59
|
+
|
|
60
|
+
Returns:
|
|
61
|
+
True if uvloop should be used, False otherwise
|
|
62
|
+
"""
|
|
63
|
+
# Only use uvloop on Unix-like systems
|
|
64
|
+
if sys.platform not in ("linux", "darwin"):
|
|
65
|
+
return False
|
|
66
|
+
|
|
67
|
+
# Check environment variable override
|
|
68
|
+
disable_uvloop = os.environ.get("MCLI_DISABLE_UVLOOP", "").lower()
|
|
69
|
+
if disable_uvloop in ("1", "true", "yes"):
|
|
70
|
+
return False
|
|
71
|
+
|
|
72
|
+
# Check if we're in certain environments where uvloop might cause issues
|
|
73
|
+
if is_jupyter_environment():
|
|
74
|
+
return False
|
|
75
|
+
|
|
76
|
+
return True
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def is_jupyter_environment() -> bool:
|
|
80
|
+
"""Check if running in Jupyter/IPython environment."""
|
|
81
|
+
try:
|
|
82
|
+
import IPython
|
|
83
|
+
|
|
84
|
+
return IPython.get_ipython() is not None
|
|
85
|
+
except ImportError:
|
|
86
|
+
return False
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def get_event_loop_info() -> dict:
|
|
90
|
+
"""Get information about the current event loop."""
|
|
91
|
+
try:
|
|
92
|
+
loop = asyncio.get_running_loop()
|
|
93
|
+
loop_type = str(type(loop))
|
|
94
|
+
|
|
95
|
+
info = {
|
|
96
|
+
"type": loop_type,
|
|
97
|
+
"is_uvloop": "uvloop" in loop_type,
|
|
98
|
+
"is_running": True,
|
|
99
|
+
"debug": loop.get_debug(),
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
# Get additional uvloop-specific info if available
|
|
103
|
+
if hasattr(loop, "_csock"):
|
|
104
|
+
info["uvloop_version"] = getattr(loop, "_uvloop_version", "unknown")
|
|
105
|
+
|
|
106
|
+
return info
|
|
107
|
+
|
|
108
|
+
except RuntimeError:
|
|
109
|
+
return {"type": "No running loop", "is_uvloop": False, "is_running": False, "debug": False}
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def configure_event_loop_for_performance():
|
|
113
|
+
"""Configure the event loop for optimal performance."""
|
|
114
|
+
try:
|
|
115
|
+
loop = asyncio.get_running_loop()
|
|
116
|
+
|
|
117
|
+
# Disable debug mode in production
|
|
118
|
+
if not os.environ.get("MCLI_DEBUG"):
|
|
119
|
+
loop.set_debug(False)
|
|
120
|
+
|
|
121
|
+
# Set task factory for better performance monitoring if needed
|
|
122
|
+
if os.environ.get("MCLI_MONITOR_TASKS"):
|
|
123
|
+
loop.set_task_factory(create_monitored_task)
|
|
124
|
+
|
|
125
|
+
except RuntimeError:
|
|
126
|
+
# No running loop - configuration will apply to next loop
|
|
127
|
+
pass
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def create_monitored_task(loop, coro):
|
|
131
|
+
"""Custom task factory for monitoring task performance."""
|
|
132
|
+
import time
|
|
133
|
+
|
|
134
|
+
class MonitoredTask(asyncio.Task):
|
|
135
|
+
def __init__(self, coro, *, loop=None):
|
|
136
|
+
super().__init__(coro, loop=loop)
|
|
137
|
+
self._start_time = time.perf_counter()
|
|
138
|
+
|
|
139
|
+
def _step(self, exc=None):
|
|
140
|
+
start = time.perf_counter()
|
|
141
|
+
try:
|
|
142
|
+
result = super()._step(exc)
|
|
143
|
+
return result
|
|
144
|
+
finally:
|
|
145
|
+
duration = time.perf_counter() - start
|
|
146
|
+
if duration > 0.1: # Log slow operations
|
|
147
|
+
print(f"Slow task step: {self.get_name()} took {duration:.3f}s")
|
|
148
|
+
|
|
149
|
+
return MonitoredTask(coro, loop=loop)
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
# Auto-install uvloop when module is imported (can be disabled with env var)
|
|
153
|
+
if os.environ.get("MCLI_AUTO_UVLOOP", "1").lower() not in ("0", "false", "no"):
|
|
154
|
+
install_uvloop()
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import pickle
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from typing import Callable, Generic, Type, TypeVar
|
|
5
|
+
|
|
6
|
+
import click
|
|
7
|
+
|
|
8
|
+
T = TypeVar("T")
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class ObjectCache(Generic[T]):
|
|
12
|
+
def __init__(self, class_type: Type[T], app_name: str, factory: Callable[[], T] = None):
|
|
13
|
+
self.class_type = class_type
|
|
14
|
+
self.app_name = app_name
|
|
15
|
+
self.factory = factory or class_type
|
|
16
|
+
|
|
17
|
+
def get_cache_path(self) -> Path:
|
|
18
|
+
cache_dir = Path(os.path.expanduser(f"~/.cache/{self.app_name}"))
|
|
19
|
+
cache_dir.mkdir(parents=True, exist_ok=True)
|
|
20
|
+
return cache_dir / f"{self.class_type.__name__.lower()}.pickle"
|
|
21
|
+
|
|
22
|
+
def get_or_create(self) -> T:
|
|
23
|
+
cache_path = self.get_cache_path()
|
|
24
|
+
|
|
25
|
+
if cache_path.exists():
|
|
26
|
+
try:
|
|
27
|
+
with open(cache_path, "rb") as f:
|
|
28
|
+
obj = pickle.load(f)
|
|
29
|
+
if isinstance(obj, self.class_type):
|
|
30
|
+
return obj
|
|
31
|
+
except (pickle.UnpicklingError, EOFError):
|
|
32
|
+
cache_path.unlink()
|
|
33
|
+
|
|
34
|
+
# Create new instance if no cache exists or loading failed
|
|
35
|
+
obj = self.factory()
|
|
36
|
+
|
|
37
|
+
# Save to cache
|
|
38
|
+
with open(cache_path, "wb") as f:
|
|
39
|
+
pickle.dump(obj, f)
|
|
40
|
+
|
|
41
|
+
return obj
|
|
42
|
+
|
|
43
|
+
def clear(self) -> None:
|
|
44
|
+
"""Remove the cached object if it exists."""
|
|
45
|
+
cache_path = self.get_cache_path()
|
|
46
|
+
if cache_path.exists():
|
|
47
|
+
cache_path.unlink()
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
__all__ = ["ObjectCache"]
|