kollabor 0.4.9__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.
Files changed (128) hide show
  1. core/__init__.py +18 -0
  2. core/application.py +578 -0
  3. core/cli.py +193 -0
  4. core/commands/__init__.py +43 -0
  5. core/commands/executor.py +277 -0
  6. core/commands/menu_renderer.py +319 -0
  7. core/commands/parser.py +186 -0
  8. core/commands/registry.py +331 -0
  9. core/commands/system_commands.py +479 -0
  10. core/config/__init__.py +7 -0
  11. core/config/llm_task_config.py +110 -0
  12. core/config/loader.py +501 -0
  13. core/config/manager.py +112 -0
  14. core/config/plugin_config_manager.py +346 -0
  15. core/config/plugin_schema.py +424 -0
  16. core/config/service.py +399 -0
  17. core/effects/__init__.py +1 -0
  18. core/events/__init__.py +12 -0
  19. core/events/bus.py +129 -0
  20. core/events/executor.py +154 -0
  21. core/events/models.py +258 -0
  22. core/events/processor.py +176 -0
  23. core/events/registry.py +289 -0
  24. core/fullscreen/__init__.py +19 -0
  25. core/fullscreen/command_integration.py +290 -0
  26. core/fullscreen/components/__init__.py +12 -0
  27. core/fullscreen/components/animation.py +258 -0
  28. core/fullscreen/components/drawing.py +160 -0
  29. core/fullscreen/components/matrix_components.py +177 -0
  30. core/fullscreen/manager.py +302 -0
  31. core/fullscreen/plugin.py +204 -0
  32. core/fullscreen/renderer.py +282 -0
  33. core/fullscreen/session.py +324 -0
  34. core/io/__init__.py +52 -0
  35. core/io/buffer_manager.py +362 -0
  36. core/io/config_status_view.py +272 -0
  37. core/io/core_status_views.py +410 -0
  38. core/io/input_errors.py +313 -0
  39. core/io/input_handler.py +2655 -0
  40. core/io/input_mode_manager.py +402 -0
  41. core/io/key_parser.py +344 -0
  42. core/io/layout.py +587 -0
  43. core/io/message_coordinator.py +204 -0
  44. core/io/message_renderer.py +601 -0
  45. core/io/modal_interaction_handler.py +315 -0
  46. core/io/raw_input_processor.py +946 -0
  47. core/io/status_renderer.py +845 -0
  48. core/io/terminal_renderer.py +586 -0
  49. core/io/terminal_state.py +551 -0
  50. core/io/visual_effects.py +734 -0
  51. core/llm/__init__.py +26 -0
  52. core/llm/api_communication_service.py +863 -0
  53. core/llm/conversation_logger.py +473 -0
  54. core/llm/conversation_manager.py +414 -0
  55. core/llm/file_operations_executor.py +1401 -0
  56. core/llm/hook_system.py +402 -0
  57. core/llm/llm_service.py +1629 -0
  58. core/llm/mcp_integration.py +386 -0
  59. core/llm/message_display_service.py +450 -0
  60. core/llm/model_router.py +214 -0
  61. core/llm/plugin_sdk.py +396 -0
  62. core/llm/response_parser.py +848 -0
  63. core/llm/response_processor.py +364 -0
  64. core/llm/tool_executor.py +520 -0
  65. core/logging/__init__.py +19 -0
  66. core/logging/setup.py +208 -0
  67. core/models/__init__.py +5 -0
  68. core/models/base.py +23 -0
  69. core/plugins/__init__.py +13 -0
  70. core/plugins/collector.py +212 -0
  71. core/plugins/discovery.py +386 -0
  72. core/plugins/factory.py +263 -0
  73. core/plugins/registry.py +152 -0
  74. core/storage/__init__.py +5 -0
  75. core/storage/state_manager.py +84 -0
  76. core/ui/__init__.py +6 -0
  77. core/ui/config_merger.py +176 -0
  78. core/ui/config_widgets.py +369 -0
  79. core/ui/live_modal_renderer.py +276 -0
  80. core/ui/modal_actions.py +162 -0
  81. core/ui/modal_overlay_renderer.py +373 -0
  82. core/ui/modal_renderer.py +591 -0
  83. core/ui/modal_state_manager.py +443 -0
  84. core/ui/widget_integration.py +222 -0
  85. core/ui/widgets/__init__.py +27 -0
  86. core/ui/widgets/base_widget.py +136 -0
  87. core/ui/widgets/checkbox.py +85 -0
  88. core/ui/widgets/dropdown.py +140 -0
  89. core/ui/widgets/label.py +78 -0
  90. core/ui/widgets/slider.py +185 -0
  91. core/ui/widgets/text_input.py +224 -0
  92. core/utils/__init__.py +11 -0
  93. core/utils/config_utils.py +656 -0
  94. core/utils/dict_utils.py +212 -0
  95. core/utils/error_utils.py +275 -0
  96. core/utils/key_reader.py +171 -0
  97. core/utils/plugin_utils.py +267 -0
  98. core/utils/prompt_renderer.py +151 -0
  99. kollabor-0.4.9.dist-info/METADATA +298 -0
  100. kollabor-0.4.9.dist-info/RECORD +128 -0
  101. kollabor-0.4.9.dist-info/WHEEL +5 -0
  102. kollabor-0.4.9.dist-info/entry_points.txt +2 -0
  103. kollabor-0.4.9.dist-info/licenses/LICENSE +21 -0
  104. kollabor-0.4.9.dist-info/top_level.txt +4 -0
  105. kollabor_cli_main.py +20 -0
  106. plugins/__init__.py +1 -0
  107. plugins/enhanced_input/__init__.py +18 -0
  108. plugins/enhanced_input/box_renderer.py +103 -0
  109. plugins/enhanced_input/box_styles.py +142 -0
  110. plugins/enhanced_input/color_engine.py +165 -0
  111. plugins/enhanced_input/config.py +150 -0
  112. plugins/enhanced_input/cursor_manager.py +72 -0
  113. plugins/enhanced_input/geometry.py +81 -0
  114. plugins/enhanced_input/state.py +130 -0
  115. plugins/enhanced_input/text_processor.py +115 -0
  116. plugins/enhanced_input_plugin.py +385 -0
  117. plugins/fullscreen/__init__.py +9 -0
  118. plugins/fullscreen/example_plugin.py +327 -0
  119. plugins/fullscreen/matrix_plugin.py +132 -0
  120. plugins/hook_monitoring_plugin.py +1299 -0
  121. plugins/query_enhancer_plugin.py +350 -0
  122. plugins/save_conversation_plugin.py +502 -0
  123. plugins/system_commands_plugin.py +93 -0
  124. plugins/tmux_plugin.py +795 -0
  125. plugins/workflow_enforcement_plugin.py +629 -0
  126. system_prompt/default.md +1286 -0
  127. system_prompt/default_win.md +265 -0
  128. system_prompt/example_with_trender.md +47 -0
@@ -0,0 +1,350 @@
1
+ """Query Enhancement Plugin for Kollabor CLI.
2
+
3
+ Uses a fast model to enhance user queries before sending to the main model,
4
+ dramatically improving response quality especially with "dumb" models.
5
+ """
6
+
7
+ import logging
8
+ import time
9
+ from typing import Any, Dict, List
10
+ import aiohttp
11
+
12
+ from core.events.models import Event, EventType, Hook, HookPriority
13
+
14
+ logger = logging.getLogger(__name__)
15
+
16
+
17
+ class QueryEnhancerPlugin:
18
+ """Plugin that enhances user queries using a fast model before main processing."""
19
+
20
+ @staticmethod
21
+ def get_default_config() -> Dict[str, Any]:
22
+ """Get default configuration for the query enhancer plugin."""
23
+ config = {
24
+ "plugins": {
25
+ "query_enhancer": {
26
+ "enabled": False,
27
+ "show_status": True,
28
+ "fast_model": {
29
+ "api_url": "http://localhost:1234",
30
+ "model": "qwen3-0.6b",
31
+ "temperature": 0.3,
32
+ "timeout": 5
33
+ },
34
+ "enhancement_prompt": """You are a query enhancement specialist. Your job is to improve user queries to get better responses from AI assistants.
35
+
36
+ Take this user query and enhance it by:
37
+ 1. Making it more specific and detailed
38
+ 2. Adding relevant context
39
+ 3. Clarifying any ambiguity
40
+ 4. Keeping the original intent
41
+
42
+ Return ONLY the enhanced query, nothing else.
43
+
44
+ Original query: {query}
45
+
46
+ Enhanced query:""",
47
+ "max_length": 500,
48
+ "min_query_length": 10,
49
+ "skip_enhancement_keywords": ["hi", "hello", "thanks", "thank you", "ok", "okay", "yes", "no"],
50
+ "performance_tracking": True
51
+ }
52
+ }
53
+ }
54
+ return config
55
+
56
+ @staticmethod
57
+ def get_startup_info(config) -> List[str]:
58
+ """Get plugin startup information for display."""
59
+ enabled = config.get("plugins.query_enhancer.enabled", True)
60
+ fast_model = config.get("plugins.query_enhancer.fast_model.model", "qwen/qwen3-1.5b")
61
+ return [
62
+ f"Enabled: {enabled}",
63
+ f"Fast Model: {fast_model}",
64
+ f"Enhancement: {'On' if enabled else 'Off'}"
65
+ ]
66
+
67
+ @staticmethod
68
+ def get_config_widgets() -> Dict[str, Any]:
69
+ """Get configuration widgets for this plugin."""
70
+ return {
71
+ "title": "Query Enhancer Plugin",
72
+ "widgets": [
73
+ {"type": "checkbox", "label": "Show Status", "config_path": "plugins.query_enhancer.show_status", "help": "Display query enhancement status"},
74
+ {"type": "text_input", "label": "Fast Model API URL", "config_path": "plugins.query_enhancer.fast_model.api_url", "placeholder": "http://localhost:1234", "help": "API URL for fast enhancement model"},
75
+ {"type": "text_input", "label": "Fast Model", "config_path": "plugins.query_enhancer.fast_model.model", "placeholder": "qwen3-0.6b", "help": "Model name for query enhancement"},
76
+ {"type": "slider", "label": "Fast Model Temperature", "config_path": "plugins.query_enhancer.fast_model.temperature", "min_value": 0.0, "max_value": 1.0, "step": 0.1, "help": "Creativity level for enhancement (0.0-1.0)"},
77
+ {"type": "slider", "label": "Fast Model Timeout", "config_path": "plugins.query_enhancer.fast_model.timeout", "min_value": 1, "max_value": 30, "step": 1, "help": "Timeout for enhancement requests (seconds)"},
78
+ {"type": "slider", "label": "Max Query Length", "config_path": "plugins.query_enhancer.max_length", "min_value": 100, "max_value": 2000, "step": 50, "help": "Maximum enhanced query length"},
79
+ {"type": "slider", "label": "Min Query Length", "config_path": "plugins.query_enhancer.min_query_length", "min_value": 1, "max_value": 50, "step": 1, "help": "Minimum query length to enhance"},
80
+ {"type": "checkbox", "label": "Performance Tracking", "config_path": "plugins.query_enhancer.performance_tracking", "help": "Track enhancement performance metrics"}
81
+ ]
82
+ }
83
+
84
+ def __init__(self, name: str, state_manager, event_bus, renderer, config) -> None:
85
+ """Initialize the query enhancer plugin.
86
+
87
+ Args:
88
+ name: Plugin name.
89
+ state_manager: State management system.
90
+ event_bus: Event bus for hook registration.
91
+ renderer: Terminal renderer.
92
+ config: Configuration manager.
93
+ """
94
+ self.name = name
95
+ self.state_manager = state_manager
96
+ self.event_bus = event_bus
97
+ self.renderer = renderer
98
+ self.config = config
99
+
100
+ # Plugin configuration
101
+ self.enabled = config.get("plugins.query_enhancer.enabled", True)
102
+ self.fast_model_config = config.get("plugins.query_enhancer.fast_model", {})
103
+ self.enhancement_prompt = config.get("plugins.query_enhancer.enhancement_prompt", "")
104
+ self.max_length = config.get("plugins.query_enhancer.max_length", 500)
105
+ self.min_query_length = config.get("plugins.query_enhancer.min_query_length", 10)
106
+ self.skip_keywords = config.get("plugins.query_enhancer.skip_enhancement_keywords", [])
107
+
108
+ # Performance tracking
109
+ self.stats = {
110
+ "total_queries": 0,
111
+ "enhanced_queries": 0,
112
+ "enhancement_failures": 0,
113
+ "avg_enhancement_time": 0.0,
114
+ "total_enhancement_time": 0.0
115
+ }
116
+
117
+ # HTTP session for API calls
118
+ self.session: aiohttp.ClientSession = None
119
+
120
+ # Register hooks
121
+ self.hooks = [
122
+ Hook(
123
+ name="enhance_user_query",
124
+ plugin_name=self.name,
125
+ event_type=EventType.USER_INPUT_PRE,
126
+ priority=HookPriority.PREPROCESSING.value,
127
+ callback=self._enhance_query_hook,
128
+ timeout=config.get("plugins.query_enhancer.fast_model.timeout", 5)
129
+ )
130
+ ]
131
+
132
+ logger.info(f"QueryEnhancer plugin '{name}' initialized with fast model: {self.fast_model_config.get('model', 'unknown')}")
133
+
134
+ def get_status_lines(self) -> Dict[str, List[str]]:
135
+ """Get status lines for the query enhancer plugin organized by area.
136
+
137
+ Returns:
138
+ Dictionary with status lines organized by area A, B, C.
139
+ """
140
+ # Check if status display is enabled for this plugin
141
+ show_status = self.config.get('plugins.query_enhancer.show_status', True)
142
+ if not show_status or not self.enabled:
143
+ return {"A": [], "B": [], "C": []}
144
+
145
+ success_rate = 0.0
146
+ if self.stats["total_queries"] > 0:
147
+ success_rate = (self.stats["enhanced_queries"] / self.stats["total_queries"]) * 100
148
+
149
+ # Enhancement stats go in area B (performance-related)
150
+ return {
151
+ "A": [],
152
+ "B": [
153
+ f"Enhanced: {self.stats['enhanced_queries']}/{self.stats['total_queries']} ({success_rate:.1f}%)",
154
+ f"Avg Time: {self.stats['avg_enhancement_time']:.2f}s"
155
+ ],
156
+ "C": []
157
+ }
158
+
159
+ async def initialize(self) -> None:
160
+ """Initialize the query enhancer plugin."""
161
+ if self.enabled:
162
+ # Create HTTP session for fast model API calls
163
+ timeout = aiohttp.ClientTimeout(total=self.fast_model_config.get("timeout", 5))
164
+ self.session = aiohttp.ClientSession(timeout=timeout)
165
+ logger.info("QueryEnhancer HTTP session initialized")
166
+
167
+ async def register_hooks(self) -> None:
168
+ """Register query enhancement hooks."""
169
+ if self.enabled:
170
+ for hook in self.hooks:
171
+ await self.event_bus.register_hook(hook)
172
+ logger.info("QueryEnhancer hooks registered")
173
+
174
+ async def shutdown(self) -> None:
175
+ """Shutdown the query enhancer plugin."""
176
+ if self.session:
177
+ await self.session.close()
178
+ logger.info("QueryEnhancer session closed")
179
+
180
+ async def _enhance_query_hook(self, data: Dict[str, Any], event: Event) -> Dict[str, Any]:
181
+ """Hook to enhance user queries before main processing.
182
+
183
+ Args:
184
+ data: Event data containing the user message.
185
+ event: The event object (used for hook system compatibility).
186
+
187
+ Returns:
188
+ Modified event data with enhanced query.
189
+ """
190
+ if not self.enabled:
191
+ return data
192
+
193
+ original_query = data.get("message", "").strip()
194
+ if not original_query:
195
+ return data
196
+
197
+ self.stats["total_queries"] += 1
198
+
199
+ # Skip enhancement for very short queries or common phrases
200
+ if (len(original_query) < self.min_query_length or
201
+ any(keyword.lower() in original_query.lower() for keyword in self.skip_keywords)):
202
+ logger.debug(f"Skipping enhancement for short/common query: {original_query[:50]}")
203
+ return data
204
+
205
+ start_time = time.time()
206
+ try:
207
+ enhanced_query = await self._enhance_query(original_query)
208
+ enhancement_time = time.time() - start_time
209
+
210
+ if enhanced_query and enhanced_query.strip() != original_query.strip():
211
+ # Update stats
212
+ self.stats["enhanced_queries"] += 1
213
+ self.stats["total_enhancement_time"] += enhancement_time
214
+ self.stats["avg_enhancement_time"] = (
215
+ self.stats["total_enhancement_time"] / self.stats["enhanced_queries"]
216
+ )
217
+
218
+ # Return enhanced query in expected format
219
+ enhanced_data = data.copy()
220
+ enhanced_data["enhanced_message"] = enhanced_query.strip()
221
+ logger.info(f"Enhanced query ({enhancement_time:.2f}s): {original_query[:50]}... → {enhanced_query[:50]}...")
222
+ return enhanced_data
223
+ else:
224
+ logger.debug("Enhancement did not improve query, using original")
225
+
226
+ except Exception as e:
227
+ self.stats["enhancement_failures"] += 1
228
+ logger.warning(f"Query enhancement failed: {e}, using original query")
229
+
230
+ return data
231
+
232
+ async def _enhance_query(self, query: str) -> str:
233
+ """Enhance a user query using the fast model.
234
+
235
+ Args:
236
+ query: The original user query.
237
+
238
+ Returns:
239
+ Enhanced query or original if enhancement fails.
240
+ """
241
+ if not self.session:
242
+ raise Exception("HTTP session not initialized")
243
+
244
+ # Prepare the enhancement prompt
245
+ prompt = self.enhancement_prompt.format(query=query)
246
+
247
+ # Prepare API payload
248
+ payload = {
249
+ "model": self.fast_model_config.get("model", "qwen3-0.6b"),
250
+ "messages": [
251
+ {"role": "user", "content": prompt}
252
+ ],
253
+ "temperature": self.fast_model_config.get("temperature", 0.3),
254
+ "max_tokens": self.max_length,
255
+ "stream": False
256
+ }
257
+
258
+ # Call the fast model API with timeout
259
+ api_url = self.fast_model_config.get("api_url", "http://localhost:1234")
260
+ timeout = self.fast_model_config.get("timeout", 5)
261
+
262
+ async with self.session.post(
263
+ f"{api_url}/v1/chat/completions",
264
+ json=payload,
265
+ timeout=aiohttp.ClientTimeout(total=timeout)
266
+ ) as response:
267
+ if response.status != 200:
268
+ raise Exception(f"API returned status {response.status}")
269
+
270
+ result = await response.json()
271
+ raw_response = result["choices"][0]["message"]["content"].strip()
272
+
273
+ # Clean up response by removing thinking tags and extracting clean content
274
+ enhanced_query = self._clean_response(raw_response)
275
+
276
+ # Basic validation of enhanced query
277
+ if len(enhanced_query) > self.max_length * 2: # Too long
278
+ raise Exception("Enhanced query too long")
279
+
280
+ if not enhanced_query or enhanced_query.lower() in ["none", "n/a", "null"]:
281
+ raise Exception("Invalid enhanced query")
282
+
283
+ return enhanced_query
284
+
285
+ def _clean_response(self, raw_response: str) -> str:
286
+ """Clean up model response by removing thinking tags and extracting enhanced query.
287
+
288
+ Args:
289
+ raw_response: Raw response from the fast model.
290
+
291
+ Returns:
292
+ Cleaned enhanced query.
293
+ """
294
+ import re
295
+
296
+ # Remove thinking tags and their content
297
+ cleaned = re.sub(r'<think>.*?</think>', '', raw_response, flags=re.DOTALL)
298
+ cleaned = re.sub(r'<thinking>.*?</thinking>', '', cleaned, flags=re.DOTALL)
299
+
300
+ # If response still starts with <think> (unclosed tag), extract everything after it
301
+ if cleaned.startswith('<think>'):
302
+ # Find the end of the thinking section and extract what comes after
303
+ lines = cleaned.split('\n')
304
+ enhanced_lines = []
305
+ in_thinking = True
306
+
307
+ for line in lines:
308
+ line = line.strip()
309
+ if not line:
310
+ continue
311
+
312
+ # Skip obvious thinking indicators
313
+ if (line.startswith('<think>') or
314
+ line.startswith('Okay,') or
315
+ line.startswith('Let me') or
316
+ line.startswith('First,') or
317
+ line.startswith('I should') or
318
+ line.startswith('Maybe') or
319
+ line.startswith('The user')):
320
+ continue
321
+
322
+ # Look for the actual enhanced query
323
+ if (not in_thinking or
324
+ line.startswith('Enhanced query:') or
325
+ line.startswith('Improved query:') or
326
+ (len(line) > 20 and '?' in line)): # Likely a question
327
+ in_thinking = False
328
+ if line.startswith(('Enhanced query:', 'Improved query:')):
329
+ line = line.split(':', 1)[1].strip()
330
+ if line:
331
+ enhanced_lines.append(line)
332
+
333
+ if enhanced_lines:
334
+ cleaned = ' '.join(enhanced_lines)
335
+
336
+ # Clean up any remaining artifacts
337
+ cleaned = cleaned.strip()
338
+ cleaned = re.sub(r'^(Enhanced query:|Improved query:|Query:)\s*', '', cleaned, flags=re.IGNORECASE)
339
+ cleaned = re.sub(r'\s+', ' ', cleaned) # Normalize whitespace
340
+
341
+ # If we still don't have a good result, try to extract the most query-like sentence
342
+ if not cleaned or len(cleaned) < 10:
343
+ sentences = raw_response.split('.')
344
+ for sentence in sentences:
345
+ sentence = sentence.strip()
346
+ if len(sentence) > 15 and ('?' in sentence or 'how' in sentence.lower()):
347
+ cleaned = sentence
348
+ break
349
+
350
+ return cleaned.strip() if cleaned else raw_response.strip()