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.

Files changed (186) hide show
  1. mcli/app/chat_cmd.py +42 -0
  2. mcli/app/commands_cmd.py +226 -0
  3. mcli/app/completion_cmd.py +216 -0
  4. mcli/app/completion_helpers.py +288 -0
  5. mcli/app/cron_test_cmd.py +697 -0
  6. mcli/app/logs_cmd.py +419 -0
  7. mcli/app/main.py +492 -0
  8. mcli/app/model/model.py +1060 -0
  9. mcli/app/model_cmd.py +227 -0
  10. mcli/app/redis_cmd.py +269 -0
  11. mcli/app/video/video.py +1114 -0
  12. mcli/app/visual_cmd.py +303 -0
  13. mcli/chat/chat.py +2409 -0
  14. mcli/chat/command_rag.py +514 -0
  15. mcli/chat/enhanced_chat.py +652 -0
  16. mcli/chat/system_controller.py +1010 -0
  17. mcli/chat/system_integration.py +1016 -0
  18. mcli/cli.py +25 -0
  19. mcli/config.toml +20 -0
  20. mcli/lib/api/api.py +586 -0
  21. mcli/lib/api/daemon_client.py +203 -0
  22. mcli/lib/api/daemon_client_local.py +44 -0
  23. mcli/lib/api/daemon_decorator.py +217 -0
  24. mcli/lib/api/mcli_decorators.py +1032 -0
  25. mcli/lib/auth/auth.py +85 -0
  26. mcli/lib/auth/aws_manager.py +85 -0
  27. mcli/lib/auth/azure_manager.py +91 -0
  28. mcli/lib/auth/credential_manager.py +192 -0
  29. mcli/lib/auth/gcp_manager.py +93 -0
  30. mcli/lib/auth/key_manager.py +117 -0
  31. mcli/lib/auth/mcli_manager.py +93 -0
  32. mcli/lib/auth/token_manager.py +75 -0
  33. mcli/lib/auth/token_util.py +1011 -0
  34. mcli/lib/config/config.py +47 -0
  35. mcli/lib/discovery/__init__.py +1 -0
  36. mcli/lib/discovery/command_discovery.py +274 -0
  37. mcli/lib/erd/erd.py +1345 -0
  38. mcli/lib/erd/generate_graph.py +453 -0
  39. mcli/lib/files/files.py +76 -0
  40. mcli/lib/fs/fs.py +109 -0
  41. mcli/lib/lib.py +29 -0
  42. mcli/lib/logger/logger.py +611 -0
  43. mcli/lib/performance/optimizer.py +409 -0
  44. mcli/lib/performance/rust_bridge.py +502 -0
  45. mcli/lib/performance/uvloop_config.py +154 -0
  46. mcli/lib/pickles/pickles.py +50 -0
  47. mcli/lib/search/cached_vectorizer.py +479 -0
  48. mcli/lib/services/data_pipeline.py +460 -0
  49. mcli/lib/services/lsh_client.py +441 -0
  50. mcli/lib/services/redis_service.py +387 -0
  51. mcli/lib/shell/shell.py +137 -0
  52. mcli/lib/toml/toml.py +33 -0
  53. mcli/lib/ui/styling.py +47 -0
  54. mcli/lib/ui/visual_effects.py +634 -0
  55. mcli/lib/watcher/watcher.py +185 -0
  56. mcli/ml/api/app.py +215 -0
  57. mcli/ml/api/middleware.py +224 -0
  58. mcli/ml/api/routers/admin_router.py +12 -0
  59. mcli/ml/api/routers/auth_router.py +244 -0
  60. mcli/ml/api/routers/backtest_router.py +12 -0
  61. mcli/ml/api/routers/data_router.py +12 -0
  62. mcli/ml/api/routers/model_router.py +302 -0
  63. mcli/ml/api/routers/monitoring_router.py +12 -0
  64. mcli/ml/api/routers/portfolio_router.py +12 -0
  65. mcli/ml/api/routers/prediction_router.py +267 -0
  66. mcli/ml/api/routers/trade_router.py +12 -0
  67. mcli/ml/api/routers/websocket_router.py +76 -0
  68. mcli/ml/api/schemas.py +64 -0
  69. mcli/ml/auth/auth_manager.py +425 -0
  70. mcli/ml/auth/models.py +154 -0
  71. mcli/ml/auth/permissions.py +302 -0
  72. mcli/ml/backtesting/backtest_engine.py +502 -0
  73. mcli/ml/backtesting/performance_metrics.py +393 -0
  74. mcli/ml/cache.py +400 -0
  75. mcli/ml/cli/main.py +398 -0
  76. mcli/ml/config/settings.py +394 -0
  77. mcli/ml/configs/dvc_config.py +230 -0
  78. mcli/ml/configs/mlflow_config.py +131 -0
  79. mcli/ml/configs/mlops_manager.py +293 -0
  80. mcli/ml/dashboard/app.py +532 -0
  81. mcli/ml/dashboard/app_integrated.py +738 -0
  82. mcli/ml/dashboard/app_supabase.py +560 -0
  83. mcli/ml/dashboard/app_training.py +615 -0
  84. mcli/ml/dashboard/cli.py +51 -0
  85. mcli/ml/data_ingestion/api_connectors.py +501 -0
  86. mcli/ml/data_ingestion/data_pipeline.py +567 -0
  87. mcli/ml/data_ingestion/stream_processor.py +512 -0
  88. mcli/ml/database/migrations/env.py +94 -0
  89. mcli/ml/database/models.py +667 -0
  90. mcli/ml/database/session.py +200 -0
  91. mcli/ml/experimentation/ab_testing.py +845 -0
  92. mcli/ml/features/ensemble_features.py +607 -0
  93. mcli/ml/features/political_features.py +676 -0
  94. mcli/ml/features/recommendation_engine.py +809 -0
  95. mcli/ml/features/stock_features.py +573 -0
  96. mcli/ml/features/test_feature_engineering.py +346 -0
  97. mcli/ml/logging.py +85 -0
  98. mcli/ml/mlops/data_versioning.py +518 -0
  99. mcli/ml/mlops/experiment_tracker.py +377 -0
  100. mcli/ml/mlops/model_serving.py +481 -0
  101. mcli/ml/mlops/pipeline_orchestrator.py +614 -0
  102. mcli/ml/models/base_models.py +324 -0
  103. mcli/ml/models/ensemble_models.py +675 -0
  104. mcli/ml/models/recommendation_models.py +474 -0
  105. mcli/ml/models/test_models.py +487 -0
  106. mcli/ml/monitoring/drift_detection.py +676 -0
  107. mcli/ml/monitoring/metrics.py +45 -0
  108. mcli/ml/optimization/portfolio_optimizer.py +834 -0
  109. mcli/ml/preprocessing/data_cleaners.py +451 -0
  110. mcli/ml/preprocessing/feature_extractors.py +491 -0
  111. mcli/ml/preprocessing/ml_pipeline.py +382 -0
  112. mcli/ml/preprocessing/politician_trading_preprocessor.py +569 -0
  113. mcli/ml/preprocessing/test_preprocessing.py +294 -0
  114. mcli/ml/scripts/populate_sample_data.py +200 -0
  115. mcli/ml/tasks.py +400 -0
  116. mcli/ml/tests/test_integration.py +429 -0
  117. mcli/ml/tests/test_training_dashboard.py +387 -0
  118. mcli/public/oi/oi.py +15 -0
  119. mcli/public/public.py +4 -0
  120. mcli/self/self_cmd.py +1246 -0
  121. mcli/workflow/daemon/api_daemon.py +800 -0
  122. mcli/workflow/daemon/async_command_database.py +681 -0
  123. mcli/workflow/daemon/async_process_manager.py +591 -0
  124. mcli/workflow/daemon/client.py +530 -0
  125. mcli/workflow/daemon/commands.py +1196 -0
  126. mcli/workflow/daemon/daemon.py +905 -0
  127. mcli/workflow/daemon/daemon_api.py +59 -0
  128. mcli/workflow/daemon/enhanced_daemon.py +571 -0
  129. mcli/workflow/daemon/process_cli.py +244 -0
  130. mcli/workflow/daemon/process_manager.py +439 -0
  131. mcli/workflow/daemon/test_daemon.py +275 -0
  132. mcli/workflow/dashboard/dashboard_cmd.py +113 -0
  133. mcli/workflow/docker/docker.py +0 -0
  134. mcli/workflow/file/file.py +100 -0
  135. mcli/workflow/gcloud/config.toml +21 -0
  136. mcli/workflow/gcloud/gcloud.py +58 -0
  137. mcli/workflow/git_commit/ai_service.py +328 -0
  138. mcli/workflow/git_commit/commands.py +430 -0
  139. mcli/workflow/lsh_integration.py +355 -0
  140. mcli/workflow/model_service/client.py +594 -0
  141. mcli/workflow/model_service/download_and_run_efficient_models.py +288 -0
  142. mcli/workflow/model_service/lightweight_embedder.py +397 -0
  143. mcli/workflow/model_service/lightweight_model_server.py +714 -0
  144. mcli/workflow/model_service/lightweight_test.py +241 -0
  145. mcli/workflow/model_service/model_service.py +1955 -0
  146. mcli/workflow/model_service/ollama_efficient_runner.py +425 -0
  147. mcli/workflow/model_service/pdf_processor.py +386 -0
  148. mcli/workflow/model_service/test_efficient_runner.py +234 -0
  149. mcli/workflow/model_service/test_example.py +315 -0
  150. mcli/workflow/model_service/test_integration.py +131 -0
  151. mcli/workflow/model_service/test_new_features.py +149 -0
  152. mcli/workflow/openai/openai.py +99 -0
  153. mcli/workflow/politician_trading/commands.py +1790 -0
  154. mcli/workflow/politician_trading/config.py +134 -0
  155. mcli/workflow/politician_trading/connectivity.py +490 -0
  156. mcli/workflow/politician_trading/data_sources.py +395 -0
  157. mcli/workflow/politician_trading/database.py +410 -0
  158. mcli/workflow/politician_trading/demo.py +248 -0
  159. mcli/workflow/politician_trading/models.py +165 -0
  160. mcli/workflow/politician_trading/monitoring.py +413 -0
  161. mcli/workflow/politician_trading/scrapers.py +966 -0
  162. mcli/workflow/politician_trading/scrapers_california.py +412 -0
  163. mcli/workflow/politician_trading/scrapers_eu.py +377 -0
  164. mcli/workflow/politician_trading/scrapers_uk.py +350 -0
  165. mcli/workflow/politician_trading/scrapers_us_states.py +438 -0
  166. mcli/workflow/politician_trading/supabase_functions.py +354 -0
  167. mcli/workflow/politician_trading/workflow.py +852 -0
  168. mcli/workflow/registry/registry.py +180 -0
  169. mcli/workflow/repo/repo.py +223 -0
  170. mcli/workflow/scheduler/commands.py +493 -0
  171. mcli/workflow/scheduler/cron_parser.py +238 -0
  172. mcli/workflow/scheduler/job.py +182 -0
  173. mcli/workflow/scheduler/monitor.py +139 -0
  174. mcli/workflow/scheduler/persistence.py +324 -0
  175. mcli/workflow/scheduler/scheduler.py +679 -0
  176. mcli/workflow/sync/sync_cmd.py +437 -0
  177. mcli/workflow/sync/test_cmd.py +314 -0
  178. mcli/workflow/videos/videos.py +242 -0
  179. mcli/workflow/wakatime/wakatime.py +11 -0
  180. mcli/workflow/workflow.py +37 -0
  181. mcli_framework-7.0.0.dist-info/METADATA +479 -0
  182. mcli_framework-7.0.0.dist-info/RECORD +186 -0
  183. mcli_framework-7.0.0.dist-info/WHEEL +5 -0
  184. mcli_framework-7.0.0.dist-info/entry_points.txt +7 -0
  185. mcli_framework-7.0.0.dist-info/licenses/LICENSE +21 -0
  186. mcli_framework-7.0.0.dist-info/top_level.txt +1 -0
@@ -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