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/chat/command_rag.py
ADDED
|
@@ -0,0 +1,514 @@
|
|
|
1
|
+
"""
|
|
2
|
+
RAG-based Command Search and Self-Referential System for MCLI Chatbot
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import asyncio
|
|
6
|
+
import json
|
|
7
|
+
import os
|
|
8
|
+
import re
|
|
9
|
+
import subprocess
|
|
10
|
+
import sys
|
|
11
|
+
from dataclasses import asdict, dataclass
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
from typing import Any, Dict, List, Optional, Tuple
|
|
14
|
+
|
|
15
|
+
import click
|
|
16
|
+
|
|
17
|
+
from mcli.lib.discovery.command_discovery import ClickCommandDiscovery, DiscoveredCommand
|
|
18
|
+
from mcli.lib.logger.logger import get_logger
|
|
19
|
+
from mcli.lib.search.cached_vectorizer import SmartVectorizerManager
|
|
20
|
+
|
|
21
|
+
logger = get_logger(__name__)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@dataclass
|
|
25
|
+
class CommandContext:
|
|
26
|
+
"""Rich context information about a command"""
|
|
27
|
+
|
|
28
|
+
command: DiscoveredCommand
|
|
29
|
+
help_text: str
|
|
30
|
+
parameters: List[Dict[str, Any]]
|
|
31
|
+
examples: List[str]
|
|
32
|
+
related_commands: List[str]
|
|
33
|
+
usage_patterns: List[str]
|
|
34
|
+
category: str
|
|
35
|
+
tags: List[str]
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class CommandRAGSystem:
|
|
39
|
+
"""
|
|
40
|
+
RAG (Retrieval Augmented Generation) system for MCLI commands
|
|
41
|
+
Provides semantic search and contextual command suggestions
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
def __init__(self, cache_dir: Optional[Path] = None):
|
|
45
|
+
self.cache_dir = cache_dir or Path.home() / ".mcli" / "command_cache"
|
|
46
|
+
self.cache_dir.mkdir(parents=True, exist_ok=True)
|
|
47
|
+
|
|
48
|
+
self.discovery = ClickCommandDiscovery()
|
|
49
|
+
self.vectorizer_manager = None
|
|
50
|
+
self.command_contexts: Dict[str, CommandContext] = {}
|
|
51
|
+
self.command_embeddings = None
|
|
52
|
+
self._initialized = False
|
|
53
|
+
|
|
54
|
+
async def initialize(self):
|
|
55
|
+
"""Initialize the RAG system"""
|
|
56
|
+
if self._initialized:
|
|
57
|
+
return
|
|
58
|
+
|
|
59
|
+
logger.info("Initializing Command RAG system...")
|
|
60
|
+
|
|
61
|
+
# Initialize vectorizer manager
|
|
62
|
+
self.vectorizer_manager = SmartVectorizerManager()
|
|
63
|
+
|
|
64
|
+
# Discover and analyze all commands
|
|
65
|
+
await self._discover_commands()
|
|
66
|
+
await self._build_command_index()
|
|
67
|
+
|
|
68
|
+
self._initialized = True
|
|
69
|
+
logger.info(f"RAG system initialized with {len(self.command_contexts)} commands")
|
|
70
|
+
|
|
71
|
+
async def _discover_commands(self):
|
|
72
|
+
"""Discover all available commands and build rich contexts"""
|
|
73
|
+
logger.info("Discovering available commands...")
|
|
74
|
+
|
|
75
|
+
# Get all commands from the discovery system
|
|
76
|
+
commands = self.discovery.discover_all_commands()
|
|
77
|
+
|
|
78
|
+
for cmd in commands:
|
|
79
|
+
try:
|
|
80
|
+
context = await self._build_command_context(cmd)
|
|
81
|
+
self.command_contexts[cmd.full_name] = context
|
|
82
|
+
except Exception as e:
|
|
83
|
+
logger.debug(f"Failed to build context for {cmd.full_name}: {e}")
|
|
84
|
+
|
|
85
|
+
async def _build_command_context(self, cmd: DiscoveredCommand) -> CommandContext:
|
|
86
|
+
"""Build rich context for a command"""
|
|
87
|
+
|
|
88
|
+
# Get help text by invoking the command with --help
|
|
89
|
+
help_text = await self._get_command_help(cmd)
|
|
90
|
+
|
|
91
|
+
# Extract parameters from the Click command
|
|
92
|
+
parameters = self._extract_parameters(cmd)
|
|
93
|
+
|
|
94
|
+
# Generate examples based on the command structure
|
|
95
|
+
examples = self._generate_examples(cmd, parameters)
|
|
96
|
+
|
|
97
|
+
# Find related commands
|
|
98
|
+
related_commands = self._find_related_commands(cmd)
|
|
99
|
+
|
|
100
|
+
# Extract usage patterns
|
|
101
|
+
usage_patterns = self._extract_usage_patterns(help_text, cmd)
|
|
102
|
+
|
|
103
|
+
# Categorize the command
|
|
104
|
+
category = self._categorize_command(cmd)
|
|
105
|
+
|
|
106
|
+
# Generate tags
|
|
107
|
+
tags = self._generate_tags(cmd, help_text, category)
|
|
108
|
+
|
|
109
|
+
return CommandContext(
|
|
110
|
+
command=cmd,
|
|
111
|
+
help_text=help_text,
|
|
112
|
+
parameters=parameters,
|
|
113
|
+
examples=examples,
|
|
114
|
+
related_commands=related_commands,
|
|
115
|
+
usage_patterns=usage_patterns,
|
|
116
|
+
category=category,
|
|
117
|
+
tags=tags,
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
async def _get_command_help(self, cmd: DiscoveredCommand) -> str:
|
|
121
|
+
"""Get help text for a command by invoking it"""
|
|
122
|
+
try:
|
|
123
|
+
# Build the command to get help
|
|
124
|
+
cmd_parts = ["python", "-m", "mcli"] + cmd.full_name.split(".") + ["--help"]
|
|
125
|
+
|
|
126
|
+
result = subprocess.run(cmd_parts, capture_output=True, text=True, timeout=10)
|
|
127
|
+
|
|
128
|
+
if result.returncode == 0:
|
|
129
|
+
return result.stdout
|
|
130
|
+
else:
|
|
131
|
+
return cmd.description or f"No help available for {cmd.full_name}"
|
|
132
|
+
|
|
133
|
+
except (subprocess.TimeoutExpired, FileNotFoundError, Exception) as e:
|
|
134
|
+
logger.debug(f"Failed to get help for {cmd.full_name}: {e}")
|
|
135
|
+
return cmd.description or f"Command: {cmd.full_name}"
|
|
136
|
+
|
|
137
|
+
def _extract_parameters(self, cmd: DiscoveredCommand) -> List[Dict[str, Any]]:
|
|
138
|
+
"""Extract parameter information from a Click command"""
|
|
139
|
+
parameters = []
|
|
140
|
+
|
|
141
|
+
if not cmd.callback:
|
|
142
|
+
return parameters
|
|
143
|
+
|
|
144
|
+
try:
|
|
145
|
+
# Get the Click command object
|
|
146
|
+
if hasattr(cmd.callback, "__click_params__"):
|
|
147
|
+
for param in cmd.callback.__click_params__:
|
|
148
|
+
param_info = {
|
|
149
|
+
"name": param.name,
|
|
150
|
+
"type": str(param.type),
|
|
151
|
+
"required": param.required,
|
|
152
|
+
"help": getattr(param, "help", ""),
|
|
153
|
+
"default": getattr(param, "default", None),
|
|
154
|
+
"is_flag": isinstance(param, click.Option) and param.is_flag,
|
|
155
|
+
"opts": getattr(param, "opts", []),
|
|
156
|
+
}
|
|
157
|
+
parameters.append(param_info)
|
|
158
|
+
except Exception as e:
|
|
159
|
+
logger.debug(f"Failed to extract parameters for {cmd.full_name}: {e}")
|
|
160
|
+
|
|
161
|
+
return parameters
|
|
162
|
+
|
|
163
|
+
def _generate_examples(
|
|
164
|
+
self, cmd: DiscoveredCommand, parameters: List[Dict[str, Any]]
|
|
165
|
+
) -> List[str]:
|
|
166
|
+
"""Generate usage examples for a command"""
|
|
167
|
+
examples = []
|
|
168
|
+
base_cmd = f"mcli {cmd.full_name.replace('.', ' ')}"
|
|
169
|
+
|
|
170
|
+
# Basic example
|
|
171
|
+
examples.append(base_cmd)
|
|
172
|
+
|
|
173
|
+
# Examples with common parameters
|
|
174
|
+
for param in parameters[:3]: # Limit to first 3 params
|
|
175
|
+
if param.get("is_flag"):
|
|
176
|
+
examples.append(
|
|
177
|
+
f"{base_cmd} {param['opts'][0] if param['opts'] else '--' + param['name']}"
|
|
178
|
+
)
|
|
179
|
+
elif not param.get("required"):
|
|
180
|
+
opt_name = param["opts"][0] if param["opts"] else f"--{param['name']}"
|
|
181
|
+
if param["type"] == "TEXT":
|
|
182
|
+
examples.append(f"{base_cmd} {opt_name} 'value'")
|
|
183
|
+
elif param["type"] == "INT":
|
|
184
|
+
examples.append(f"{base_cmd} {opt_name} 42")
|
|
185
|
+
else:
|
|
186
|
+
examples.append(f"{base_cmd} {opt_name} <value>")
|
|
187
|
+
|
|
188
|
+
return examples
|
|
189
|
+
|
|
190
|
+
def _find_related_commands(self, cmd: DiscoveredCommand) -> List[str]:
|
|
191
|
+
"""Find commands related to the given command"""
|
|
192
|
+
related = []
|
|
193
|
+
|
|
194
|
+
# Commands in the same group/module
|
|
195
|
+
for other_cmd in self.discovery.discovered_commands.values():
|
|
196
|
+
if other_cmd.full_name != cmd.full_name and other_cmd.module_name == cmd.module_name:
|
|
197
|
+
related.append(other_cmd.full_name)
|
|
198
|
+
|
|
199
|
+
# Commands with similar names
|
|
200
|
+
cmd_words = set(cmd.name.lower().split("_"))
|
|
201
|
+
for other_cmd in self.discovery.discovered_commands.values():
|
|
202
|
+
if other_cmd.full_name != cmd.full_name:
|
|
203
|
+
other_words = set(other_cmd.name.lower().split("_"))
|
|
204
|
+
if len(cmd_words.intersection(other_words)) > 0:
|
|
205
|
+
related.append(other_cmd.full_name)
|
|
206
|
+
|
|
207
|
+
return related[:5] # Limit to 5 related commands
|
|
208
|
+
|
|
209
|
+
def _extract_usage_patterns(self, help_text: str, cmd: DiscoveredCommand) -> List[str]:
|
|
210
|
+
"""Extract common usage patterns from help text"""
|
|
211
|
+
patterns = []
|
|
212
|
+
|
|
213
|
+
# Extract usage lines from help text
|
|
214
|
+
usage_section = re.search(
|
|
215
|
+
r"Usage:(.*?)(?:\n\n|\n[A-Z]|\Z)", help_text, re.DOTALL | re.IGNORECASE
|
|
216
|
+
)
|
|
217
|
+
if usage_section:
|
|
218
|
+
usage_lines = [
|
|
219
|
+
line.strip() for line in usage_section.group(1).split("\n") if line.strip()
|
|
220
|
+
]
|
|
221
|
+
patterns.extend(usage_lines)
|
|
222
|
+
|
|
223
|
+
# Generate basic patterns
|
|
224
|
+
base_pattern = f"mcli {cmd.full_name.replace('.', ' ')}"
|
|
225
|
+
patterns.append(base_pattern)
|
|
226
|
+
patterns.append(f"{base_pattern} [OPTIONS]")
|
|
227
|
+
|
|
228
|
+
return list(set(patterns)) # Remove duplicates
|
|
229
|
+
|
|
230
|
+
def _categorize_command(self, cmd: DiscoveredCommand) -> str:
|
|
231
|
+
"""Categorize the command based on its module and name"""
|
|
232
|
+
module_parts = cmd.module_name.split(".")
|
|
233
|
+
|
|
234
|
+
# Map modules to categories
|
|
235
|
+
category_map = {
|
|
236
|
+
"workflow": "Automation & Workflows",
|
|
237
|
+
"app": "Application Management",
|
|
238
|
+
"chat": "AI & Chat",
|
|
239
|
+
"self": "System Management",
|
|
240
|
+
"redis": "Cache & Performance",
|
|
241
|
+
"visual": "UI & Visualization",
|
|
242
|
+
"model": "AI Models",
|
|
243
|
+
"daemon": "Background Services",
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
for module_part in module_parts:
|
|
247
|
+
if module_part in category_map:
|
|
248
|
+
return category_map[module_part]
|
|
249
|
+
|
|
250
|
+
# Fallback categorization based on command name
|
|
251
|
+
name_lower = cmd.name.lower()
|
|
252
|
+
if any(word in name_lower for word in ["start", "stop", "restart", "status"]):
|
|
253
|
+
return "Service Management"
|
|
254
|
+
elif any(word in name_lower for word in ["list", "show", "info", "status"]):
|
|
255
|
+
return "Information & Monitoring"
|
|
256
|
+
elif any(word in name_lower for word in ["create", "add", "new"]):
|
|
257
|
+
return "Creation & Setup"
|
|
258
|
+
elif any(word in name_lower for word in ["delete", "remove", "clean"]):
|
|
259
|
+
return "Cleanup & Removal"
|
|
260
|
+
else:
|
|
261
|
+
return "General Commands"
|
|
262
|
+
|
|
263
|
+
def _generate_tags(self, cmd: DiscoveredCommand, help_text: str, category: str) -> List[str]:
|
|
264
|
+
"""Generate tags for better searchability"""
|
|
265
|
+
tags = []
|
|
266
|
+
|
|
267
|
+
# Add category as tag
|
|
268
|
+
tags.append(category.lower().replace(" & ", "_").replace(" ", "_"))
|
|
269
|
+
|
|
270
|
+
# Add module-based tags
|
|
271
|
+
module_parts = cmd.module_name.split(".")
|
|
272
|
+
tags.extend(module_parts)
|
|
273
|
+
|
|
274
|
+
# Add name-based tags
|
|
275
|
+
name_parts = cmd.name.split("_")
|
|
276
|
+
tags.extend(name_parts)
|
|
277
|
+
|
|
278
|
+
# Add tags based on help text content
|
|
279
|
+
help_lower = help_text.lower()
|
|
280
|
+
keyword_tags = {
|
|
281
|
+
"file": ["file", "files", "filename"],
|
|
282
|
+
"process": ["process", "processes", "pid"],
|
|
283
|
+
"server": ["server", "service", "daemon"],
|
|
284
|
+
"config": ["config", "configuration", "settings"],
|
|
285
|
+
"performance": ["performance", "optimization", "speed"],
|
|
286
|
+
"cache": ["cache", "caching", "redis"],
|
|
287
|
+
"async": ["async", "asynchronous", "concurrent"],
|
|
288
|
+
"monitor": ["monitor", "monitoring", "watch"],
|
|
289
|
+
"automation": ["automation", "workflow", "schedule"],
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
for tag, keywords in keyword_tags.items():
|
|
293
|
+
if any(keyword in help_lower for keyword in keywords):
|
|
294
|
+
tags.append(tag)
|
|
295
|
+
|
|
296
|
+
# Remove duplicates and return
|
|
297
|
+
return list(set(tags))
|
|
298
|
+
|
|
299
|
+
async def _build_command_index(self):
|
|
300
|
+
"""Build searchable index of all commands"""
|
|
301
|
+
logger.info("Building command search index...")
|
|
302
|
+
|
|
303
|
+
# Prepare documents for vectorization
|
|
304
|
+
documents = []
|
|
305
|
+
command_names = []
|
|
306
|
+
|
|
307
|
+
for cmd_name, context in self.command_contexts.items():
|
|
308
|
+
# Create a rich text representation of the command
|
|
309
|
+
doc_text = self._create_command_document(context)
|
|
310
|
+
documents.append(doc_text)
|
|
311
|
+
command_names.append(cmd_name)
|
|
312
|
+
|
|
313
|
+
if documents:
|
|
314
|
+
# Get vectorizer for command search
|
|
315
|
+
self.vectorizer = await self.vectorizer_manager.get_vectorizer(
|
|
316
|
+
domain="commands", max_features=2000, ngram_range=(1, 3)
|
|
317
|
+
)
|
|
318
|
+
|
|
319
|
+
# Build the search index
|
|
320
|
+
await self.vectorizer.fit_transform(documents)
|
|
321
|
+
logger.info(f"Command index built with {len(documents)} commands")
|
|
322
|
+
|
|
323
|
+
def _create_command_document(self, context: CommandContext) -> str:
|
|
324
|
+
"""Create a searchable document from command context"""
|
|
325
|
+
parts = [
|
|
326
|
+
f"Command: {context.command.full_name}",
|
|
327
|
+
f"Description: {context.command.description}",
|
|
328
|
+
f"Category: {context.category}",
|
|
329
|
+
f"Help: {context.help_text}",
|
|
330
|
+
f"Tags: {' '.join(context.tags)}",
|
|
331
|
+
]
|
|
332
|
+
|
|
333
|
+
# Add parameter information
|
|
334
|
+
for param in context.parameters:
|
|
335
|
+
parts.append(f"Parameter: {param['name']} ({param['type']}) - {param.get('help', '')}")
|
|
336
|
+
|
|
337
|
+
# Add examples
|
|
338
|
+
parts.extend([f"Example: {ex}" for ex in context.examples])
|
|
339
|
+
|
|
340
|
+
# Add usage patterns
|
|
341
|
+
parts.extend([f"Usage: {pattern}" for pattern in context.usage_patterns])
|
|
342
|
+
|
|
343
|
+
return " | ".join(parts)
|
|
344
|
+
|
|
345
|
+
async def search_commands(
|
|
346
|
+
self, query: str, limit: int = 10, category_filter: Optional[str] = None
|
|
347
|
+
) -> List[Tuple[CommandContext, float]]:
|
|
348
|
+
"""Search for commands using semantic similarity"""
|
|
349
|
+
if not self._initialized:
|
|
350
|
+
await self.initialize()
|
|
351
|
+
|
|
352
|
+
if not self.command_contexts:
|
|
353
|
+
return []
|
|
354
|
+
|
|
355
|
+
try:
|
|
356
|
+
# Get command names for search
|
|
357
|
+
command_names = list(self.command_contexts.keys())
|
|
358
|
+
documents = [
|
|
359
|
+
self._create_command_document(ctx) for ctx in self.command_contexts.values()
|
|
360
|
+
]
|
|
361
|
+
|
|
362
|
+
# Perform similarity search
|
|
363
|
+
results = await self.vectorizer.similarity_search(query, documents, limit * 2)
|
|
364
|
+
|
|
365
|
+
# Convert results to CommandContext objects with scores
|
|
366
|
+
command_results = []
|
|
367
|
+
for doc_idx, score in results:
|
|
368
|
+
if 0 <= doc_idx < len(command_names):
|
|
369
|
+
cmd_name = command_names[doc_idx]
|
|
370
|
+
context = self.command_contexts[cmd_name]
|
|
371
|
+
|
|
372
|
+
# Apply category filter if specified
|
|
373
|
+
if category_filter and category_filter.lower() not in context.category.lower():
|
|
374
|
+
continue
|
|
375
|
+
|
|
376
|
+
command_results.append((context, score))
|
|
377
|
+
|
|
378
|
+
return command_results[:limit]
|
|
379
|
+
|
|
380
|
+
except Exception as e:
|
|
381
|
+
logger.error(f"Command search failed: {e}")
|
|
382
|
+
return []
|
|
383
|
+
|
|
384
|
+
async def get_command_suggestions(
|
|
385
|
+
self, user_input: str, context_history: List[str] = None
|
|
386
|
+
) -> List[Dict[str, Any]]:
|
|
387
|
+
"""Get intelligent command suggestions based on user input and context"""
|
|
388
|
+
|
|
389
|
+
# Search for relevant commands
|
|
390
|
+
search_results = await self.search_commands(user_input, limit=5)
|
|
391
|
+
|
|
392
|
+
suggestions = []
|
|
393
|
+
for context, score in search_results:
|
|
394
|
+
suggestion = {
|
|
395
|
+
"command": context.command.full_name,
|
|
396
|
+
"name": context.command.name,
|
|
397
|
+
"description": context.command.description,
|
|
398
|
+
"category": context.category,
|
|
399
|
+
"confidence": score,
|
|
400
|
+
"examples": context.examples[:2], # Top 2 examples
|
|
401
|
+
"usage": context.usage_patterns[0] if context.usage_patterns else None,
|
|
402
|
+
"parameters": [
|
|
403
|
+
p for p in context.parameters if p.get("required")
|
|
404
|
+
], # Required params only
|
|
405
|
+
"tags": context.tags,
|
|
406
|
+
}
|
|
407
|
+
suggestions.append(suggestion)
|
|
408
|
+
|
|
409
|
+
return suggestions
|
|
410
|
+
|
|
411
|
+
async def get_command_details(self, command_name: str) -> Optional[Dict[str, Any]]:
|
|
412
|
+
"""Get detailed information about a specific command"""
|
|
413
|
+
context = self.command_contexts.get(command_name)
|
|
414
|
+
if not context:
|
|
415
|
+
return None
|
|
416
|
+
|
|
417
|
+
return {
|
|
418
|
+
"command": asdict(context.command),
|
|
419
|
+
"help_text": context.help_text,
|
|
420
|
+
"parameters": context.parameters,
|
|
421
|
+
"examples": context.examples,
|
|
422
|
+
"related_commands": context.related_commands,
|
|
423
|
+
"usage_patterns": context.usage_patterns,
|
|
424
|
+
"category": context.category,
|
|
425
|
+
"tags": context.tags,
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
async def analyze_user_intent(self, user_message: str) -> Dict[str, Any]:
|
|
429
|
+
"""Analyze user intent and suggest appropriate actions"""
|
|
430
|
+
intent_analysis = {
|
|
431
|
+
"intent_type": "unknown",
|
|
432
|
+
"confidence": 0.0,
|
|
433
|
+
"suggested_commands": [],
|
|
434
|
+
"action_keywords": [],
|
|
435
|
+
"entities": [],
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
message_lower = user_message.lower()
|
|
439
|
+
|
|
440
|
+
# Define intent patterns
|
|
441
|
+
intent_patterns = {
|
|
442
|
+
"start_service": ["start", "run", "launch", "begin", "activate", "turn on"],
|
|
443
|
+
"stop_service": ["stop", "halt", "terminate", "kill", "shutdown", "turn off"],
|
|
444
|
+
"status_check": ["status", "info", "show", "list", "check", "what is", "tell me about"],
|
|
445
|
+
"help_request": ["help", "how to", "how do i", "explain", "tutorial", "guide"],
|
|
446
|
+
"performance": ["performance", "optimize", "speed up", "faster", "slow", "cache"],
|
|
447
|
+
"automation": ["automate", "schedule", "workflow", "cron", "recurring"],
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
# Find matching intent
|
|
451
|
+
max_confidence = 0.0
|
|
452
|
+
detected_intent = "unknown"
|
|
453
|
+
|
|
454
|
+
for intent, keywords in intent_patterns.items():
|
|
455
|
+
keyword_matches = sum(1 for keyword in keywords if keyword in message_lower)
|
|
456
|
+
confidence = keyword_matches / len(keywords)
|
|
457
|
+
|
|
458
|
+
if confidence > max_confidence:
|
|
459
|
+
max_confidence = confidence
|
|
460
|
+
detected_intent = intent
|
|
461
|
+
intent_analysis["action_keywords"] = [kw for kw in keywords if kw in message_lower]
|
|
462
|
+
|
|
463
|
+
intent_analysis["intent_type"] = detected_intent
|
|
464
|
+
intent_analysis["confidence"] = max_confidence
|
|
465
|
+
|
|
466
|
+
# Get relevant commands based on intent
|
|
467
|
+
if max_confidence > 0:
|
|
468
|
+
search_results = await self.search_commands(user_message, limit=3)
|
|
469
|
+
intent_analysis["suggested_commands"] = [
|
|
470
|
+
{
|
|
471
|
+
"command": ctx.command.full_name,
|
|
472
|
+
"description": ctx.command.description,
|
|
473
|
+
"confidence": score,
|
|
474
|
+
}
|
|
475
|
+
for ctx, score in search_results
|
|
476
|
+
]
|
|
477
|
+
|
|
478
|
+
return intent_analysis
|
|
479
|
+
|
|
480
|
+
def get_system_capabilities(self) -> Dict[str, Any]:
|
|
481
|
+
"""Get information about system capabilities for self-reference"""
|
|
482
|
+
categories = {}
|
|
483
|
+
for context in self.command_contexts.values():
|
|
484
|
+
category = context.category
|
|
485
|
+
if category not in categories:
|
|
486
|
+
categories[category] = {"commands": [], "count": 0}
|
|
487
|
+
|
|
488
|
+
categories[category]["commands"].append(
|
|
489
|
+
{"name": context.command.full_name, "description": context.command.description}
|
|
490
|
+
)
|
|
491
|
+
categories[category]["count"] += 1
|
|
492
|
+
|
|
493
|
+
return {
|
|
494
|
+
"total_commands": len(self.command_contexts),
|
|
495
|
+
"categories": categories,
|
|
496
|
+
"available_services": [
|
|
497
|
+
name
|
|
498
|
+
for name, ctx in self.command_contexts.items()
|
|
499
|
+
if any(tag in ctx.tags for tag in ["service", "daemon", "server"])
|
|
500
|
+
],
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
|
|
504
|
+
# Global RAG system instance
|
|
505
|
+
_rag_system = None
|
|
506
|
+
|
|
507
|
+
|
|
508
|
+
async def get_command_rag_system() -> CommandRAGSystem:
|
|
509
|
+
"""Get or create the global RAG system instance"""
|
|
510
|
+
global _rag_system
|
|
511
|
+
if _rag_system is None:
|
|
512
|
+
_rag_system = CommandRAGSystem()
|
|
513
|
+
await _rag_system.initialize()
|
|
514
|
+
return _rag_system
|