cortex-llm 1.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.
Files changed (48) hide show
  1. cortex/__init__.py +73 -0
  2. cortex/__main__.py +83 -0
  3. cortex/config.py +329 -0
  4. cortex/conversation_manager.py +468 -0
  5. cortex/fine_tuning/__init__.py +8 -0
  6. cortex/fine_tuning/dataset.py +332 -0
  7. cortex/fine_tuning/mlx_lora_trainer.py +502 -0
  8. cortex/fine_tuning/trainer.py +957 -0
  9. cortex/fine_tuning/wizard.py +707 -0
  10. cortex/gpu_validator.py +467 -0
  11. cortex/inference_engine.py +727 -0
  12. cortex/metal/__init__.py +275 -0
  13. cortex/metal/gpu_validator.py +177 -0
  14. cortex/metal/memory_pool.py +886 -0
  15. cortex/metal/mlx_accelerator.py +678 -0
  16. cortex/metal/mlx_converter.py +638 -0
  17. cortex/metal/mps_optimizer.py +417 -0
  18. cortex/metal/optimizer.py +665 -0
  19. cortex/metal/performance_profiler.py +364 -0
  20. cortex/model_downloader.py +130 -0
  21. cortex/model_manager.py +2187 -0
  22. cortex/quantization/__init__.py +5 -0
  23. cortex/quantization/dynamic_quantizer.py +736 -0
  24. cortex/template_registry/__init__.py +15 -0
  25. cortex/template_registry/auto_detector.py +144 -0
  26. cortex/template_registry/config_manager.py +234 -0
  27. cortex/template_registry/interactive.py +260 -0
  28. cortex/template_registry/registry.py +347 -0
  29. cortex/template_registry/template_profiles/__init__.py +5 -0
  30. cortex/template_registry/template_profiles/base.py +142 -0
  31. cortex/template_registry/template_profiles/complex/__init__.py +5 -0
  32. cortex/template_registry/template_profiles/complex/reasoning.py +263 -0
  33. cortex/template_registry/template_profiles/standard/__init__.py +9 -0
  34. cortex/template_registry/template_profiles/standard/alpaca.py +73 -0
  35. cortex/template_registry/template_profiles/standard/chatml.py +82 -0
  36. cortex/template_registry/template_profiles/standard/gemma.py +103 -0
  37. cortex/template_registry/template_profiles/standard/llama.py +87 -0
  38. cortex/template_registry/template_profiles/standard/simple.py +65 -0
  39. cortex/ui/__init__.py +120 -0
  40. cortex/ui/cli.py +1685 -0
  41. cortex/ui/markdown_render.py +185 -0
  42. cortex/ui/terminal_app.py +534 -0
  43. cortex_llm-1.0.0.dist-info/METADATA +275 -0
  44. cortex_llm-1.0.0.dist-info/RECORD +48 -0
  45. cortex_llm-1.0.0.dist-info/WHEEL +5 -0
  46. cortex_llm-1.0.0.dist-info/entry_points.txt +2 -0
  47. cortex_llm-1.0.0.dist-info/licenses/LICENSE +21 -0
  48. cortex_llm-1.0.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,347 @@
1
+ """Main template registry for managing model templates."""
2
+
3
+ import logging
4
+ from typing import Dict, Any, Optional, List, Tuple
5
+ from pathlib import Path
6
+ from rich.console import Console
7
+
8
+ from cortex.template_registry.template_profiles.base import BaseTemplateProfile, TemplateType
9
+ from cortex.template_registry.template_profiles.standard import (
10
+ ChatMLProfile, LlamaProfile, AlpacaProfile, SimpleProfile, GemmaProfile
11
+ )
12
+ from cortex.template_registry.template_profiles.complex import ReasoningProfile
13
+ from cortex.template_registry.auto_detector import TemplateDetector
14
+ from cortex.template_registry.config_manager import TemplateConfigManager, ModelTemplateConfig
15
+ from cortex.template_registry.interactive import InteractiveTemplateSetup
16
+
17
+ logger = logging.getLogger(__name__)
18
+
19
+
20
+ class TemplateRegistry:
21
+ """Central registry for managing model templates."""
22
+
23
+ def __init__(self, config_path: Optional[Path] = None, console: Optional[Console] = None):
24
+ """Initialize the template registry.
25
+
26
+ Args:
27
+ config_path: Optional path to configuration file
28
+ console: Optional Rich console for interactive output
29
+ """
30
+ self.config_manager = TemplateConfigManager(config_path)
31
+ self.detector = TemplateDetector()
32
+ self.interactive = InteractiveTemplateSetup(console)
33
+ self.console = console or Console()
34
+
35
+ # Cache of loaded profiles
36
+ self._profile_cache: Dict[str, BaseTemplateProfile] = {}
37
+
38
+ # Profile type mapping
39
+ self._profile_types = {
40
+ TemplateType.CHATML: ChatMLProfile,
41
+ TemplateType.LLAMA: LlamaProfile,
42
+ TemplateType.ALPACA: AlpacaProfile,
43
+ TemplateType.REASONING: ReasoningProfile,
44
+ TemplateType.SIMPLE: SimpleProfile,
45
+ TemplateType.GEMMA: GemmaProfile,
46
+ TemplateType.CUSTOM: GemmaProfile # Use Gemma as default custom template
47
+ }
48
+
49
+ def setup_model(
50
+ self,
51
+ model_name: str,
52
+ tokenizer: Any = None,
53
+ interactive: bool = True,
54
+ force_setup: bool = False
55
+ ) -> BaseTemplateProfile:
56
+ """Setup or retrieve template for a model.
57
+
58
+ Args:
59
+ model_name: Name of the model
60
+ tokenizer: Optional tokenizer object
61
+ interactive: Whether to use interactive setup
62
+ force_setup: Force re-setup even if config exists
63
+
64
+ Returns:
65
+ Configured template profile
66
+ """
67
+ # Check cache first
68
+ if model_name in self._profile_cache and not force_setup:
69
+ logger.debug(f"Using cached profile for {model_name}")
70
+ return self._profile_cache[model_name]
71
+
72
+ # Check saved configuration
73
+ config = self.config_manager.get_model_config(model_name)
74
+ global_settings = self.config_manager.get_global_settings()
75
+
76
+ if config and not force_setup:
77
+ # Load saved configuration
78
+ profile = self._load_profile_from_config(config)
79
+ if profile:
80
+ self._profile_cache[model_name] = profile
81
+ logger.info(f"Loaded saved template configuration for {model_name}")
82
+ return profile
83
+
84
+ # Auto-detect if enabled
85
+ if global_settings.auto_detect or force_setup:
86
+ detected_profile, confidence = self.detector.detect_template(model_name, tokenizer=tokenizer)
87
+
88
+ # Check if we should prompt user
89
+ should_prompt = (
90
+ interactive and
91
+ global_settings.prompt_on_unknown and
92
+ (confidence < 0.5 or force_setup)
93
+ )
94
+
95
+ if should_prompt:
96
+ # Interactive setup
97
+ config = self.interactive.setup_model_template(model_name, tokenizer, config)
98
+ self.config_manager.save_model_config(model_name, config)
99
+ profile = self._load_profile_from_config(config)
100
+ else:
101
+ # Use detected profile
102
+ config = ModelTemplateConfig(
103
+ detected_type=detected_profile.config.template_type.value,
104
+ user_preference="auto",
105
+ custom_filters=detected_profile.config.custom_filters,
106
+ show_reasoning=False,
107
+ confidence=confidence
108
+ )
109
+
110
+ if global_settings.cache_templates:
111
+ self.config_manager.save_model_config(model_name, config)
112
+
113
+ profile = detected_profile
114
+
115
+ self._profile_cache[model_name] = profile
116
+ return profile
117
+
118
+ # Fallback to simple profile
119
+ logger.warning(f"Using fallback template for {model_name}")
120
+ profile = SimpleProfile()
121
+ self._profile_cache[model_name] = profile
122
+ return profile
123
+
124
+ def get_template(self, model_name: str) -> Optional[BaseTemplateProfile]:
125
+ """Get template for a model without setup.
126
+
127
+ Args:
128
+ model_name: Name of the model
129
+
130
+ Returns:
131
+ Template profile if configured, None otherwise
132
+ """
133
+ # Check cache
134
+ if model_name in self._profile_cache:
135
+ return self._profile_cache[model_name]
136
+
137
+ # Check saved configuration
138
+ config = self.config_manager.get_model_config(model_name)
139
+ if config:
140
+ profile = self._load_profile_from_config(config)
141
+ if profile:
142
+ self._profile_cache[model_name] = profile
143
+ return profile
144
+
145
+ return None
146
+
147
+ def configure_template(
148
+ self,
149
+ model_name: str,
150
+ interactive: bool = True,
151
+ **kwargs
152
+ ) -> BaseTemplateProfile:
153
+ """Configure or reconfigure template for a model.
154
+
155
+ Args:
156
+ model_name: Name of the model
157
+ interactive: Whether to use interactive configuration
158
+ **kwargs: Configuration overrides
159
+
160
+ Returns:
161
+ Updated template profile
162
+ """
163
+ # Get current configuration
164
+ config = self.config_manager.get_model_config(model_name)
165
+
166
+ if interactive:
167
+ if config:
168
+ # Quick adjust existing
169
+ config = self.interactive.quick_adjust_template(model_name, config)
170
+ else:
171
+ # Full setup
172
+ config = self.interactive.setup_model_template(model_name)
173
+
174
+ self.config_manager.save_model_config(model_name, config)
175
+ else:
176
+ # Apply kwargs overrides
177
+ if not config:
178
+ # Create default config
179
+ profile, confidence = self.detector.detect_template(model_name)
180
+ config = ModelTemplateConfig(
181
+ detected_type=profile.config.template_type.value,
182
+ user_preference="custom",
183
+ custom_filters=[],
184
+ confidence=confidence
185
+ )
186
+
187
+ # Apply overrides
188
+ for key, value in kwargs.items():
189
+ if hasattr(config, key):
190
+ setattr(config, key, value)
191
+
192
+ self.config_manager.save_model_config(model_name, config)
193
+
194
+ # Load and cache updated profile
195
+ profile = self._load_profile_from_config(config)
196
+ self._profile_cache[model_name] = profile
197
+ return profile
198
+
199
+ def _load_profile_from_config(self, config: ModelTemplateConfig) -> Optional[BaseTemplateProfile]:
200
+ """Load a profile based on configuration.
201
+
202
+ Args:
203
+ config: Model template configuration
204
+
205
+ Returns:
206
+ Configured profile or None
207
+ """
208
+ try:
209
+ # Determine template type
210
+ template_type = TemplateType(config.detected_type)
211
+
212
+ # Get profile class
213
+ profile_class = self._profile_types.get(template_type)
214
+ if not profile_class:
215
+ logger.warning(f"Unknown template type: {config.detected_type}")
216
+ return SimpleProfile()
217
+
218
+ # Create and configure profile
219
+ profile = profile_class()
220
+
221
+ # Apply configuration
222
+ if config.custom_filters:
223
+ profile.config.custom_filters = config.custom_filters
224
+
225
+ if hasattr(profile.config, 'show_reasoning'):
226
+ profile.config.show_reasoning = config.show_reasoning
227
+
228
+ # Handle user preferences
229
+ if config.user_preference == "simple" and template_type == TemplateType.REASONING:
230
+ profile.config.show_reasoning = False
231
+ elif config.user_preference == "full" and template_type == TemplateType.REASONING:
232
+ profile.config.show_reasoning = True
233
+
234
+ return profile
235
+
236
+ except ValueError as e:
237
+ # Invalid template type enum value
238
+ logger.error(f"Invalid template type '{config.detected_type}': {e}")
239
+ return SimpleProfile()
240
+ except AttributeError as e:
241
+ # Missing expected attributes
242
+ logger.error(f"Profile configuration error: {e}")
243
+ return SimpleProfile()
244
+ except TypeError as e:
245
+ # Type-related errors in profile instantiation
246
+ logger.error(f"Profile instantiation error: {e}")
247
+ return SimpleProfile()
248
+
249
+ def format_messages(
250
+ self,
251
+ model_name: str,
252
+ messages: List[Dict[str, str]],
253
+ add_generation_prompt: bool = True
254
+ ) -> str:
255
+ """Format messages for a specific model.
256
+
257
+ Args:
258
+ model_name: Name of the model
259
+ messages: List of message dictionaries
260
+ add_generation_prompt: Whether to add generation prompt
261
+
262
+ Returns:
263
+ Formatted prompt string
264
+ """
265
+ profile = self.get_template(model_name)
266
+ if not profile:
267
+ profile = self.setup_model(model_name, interactive=False)
268
+
269
+ return profile.format_messages(messages, add_generation_prompt)
270
+
271
+ def process_response(self, model_name: str, raw_output: str) -> str:
272
+ """Process model response using appropriate template.
273
+
274
+ Args:
275
+ model_name: Name of the model
276
+ raw_output: Raw model output
277
+
278
+ Returns:
279
+ Processed output string
280
+ """
281
+ profile = self.get_template(model_name)
282
+ if not profile:
283
+ profile = SimpleProfile()
284
+
285
+ return profile.process_response(raw_output)
286
+
287
+ def list_templates(self) -> List[Dict[str, Any]]:
288
+ """List all available template types.
289
+
290
+ Returns:
291
+ List of template information
292
+ """
293
+ templates = []
294
+ for template_type, profile_class in self._profile_types.items():
295
+ profile = profile_class()
296
+ templates.append({
297
+ 'type': template_type.value,
298
+ 'name': profile.config.name,
299
+ 'description': profile.config.description,
300
+ 'supports_system': profile.config.supports_system_prompt,
301
+ 'supports_multi_turn': profile.config.supports_multi_turn
302
+ })
303
+ return templates
304
+
305
+ def list_configured_models(self) -> List[str]:
306
+ """List all models with saved configurations.
307
+
308
+ Returns:
309
+ List of model names
310
+ """
311
+ return self.config_manager.list_configured_models()
312
+
313
+ def reset_model_config(self, model_name: str) -> bool:
314
+ """Reset model configuration to defaults.
315
+
316
+ Args:
317
+ model_name: Name of the model
318
+
319
+ Returns:
320
+ True if reset successful
321
+ """
322
+ # Remove from cache
323
+ if model_name in self._profile_cache:
324
+ del self._profile_cache[model_name]
325
+
326
+ # Remove saved config
327
+ return self.config_manager.remove_model_config(model_name)
328
+
329
+ def get_status(self) -> Dict[str, Any]:
330
+ """Get registry status information.
331
+
332
+ Returns:
333
+ Status dictionary
334
+ """
335
+ global_settings = self.config_manager.get_global_settings()
336
+
337
+ return {
338
+ 'configured_models': len(self.list_configured_models()),
339
+ 'cached_profiles': len(self._profile_cache),
340
+ 'available_templates': len(self._profile_types),
341
+ 'global_settings': {
342
+ 'auto_detect': global_settings.auto_detect,
343
+ 'prompt_on_unknown': global_settings.prompt_on_unknown,
344
+ 'cache_templates': global_settings.cache_templates,
345
+ 'default_fallback': global_settings.default_fallback
346
+ }
347
+ }
@@ -0,0 +1,5 @@
1
+ """Template profiles for different model formats."""
2
+
3
+ from cortex.template_registry.template_profiles.base import BaseTemplateProfile, TemplateType, TemplateConfig
4
+
5
+ __all__ = ['BaseTemplateProfile', 'TemplateType', 'TemplateConfig']
@@ -0,0 +1,142 @@
1
+ """Base template profile for all template implementations."""
2
+
3
+ from abc import ABC, abstractmethod
4
+ from typing import List, Dict, Any, Optional, Tuple
5
+ from dataclasses import dataclass
6
+ from enum import Enum
7
+
8
+
9
+ class TemplateType(Enum):
10
+ """Types of template formats."""
11
+ CHATML = "chatml"
12
+ LLAMA = "llama"
13
+ ALPACA = "alpaca"
14
+ VICUNA = "vicuna"
15
+ OPENAI = "openai"
16
+ REASONING = "reasoning"
17
+ GEMMA = "gemma"
18
+ CUSTOM = "custom"
19
+ SIMPLE = "simple"
20
+ UNKNOWN = "unknown"
21
+
22
+
23
+ @dataclass
24
+ class TemplateConfig:
25
+ """Configuration for a template profile."""
26
+ name: str
27
+ description: str
28
+ template_type: TemplateType
29
+ supports_system_prompt: bool = True
30
+ supports_multi_turn: bool = True
31
+ strip_special_tokens: bool = False
32
+ show_reasoning: bool = False
33
+ custom_filters: List[str] = None
34
+ stop_sequences: List[str] = None
35
+
36
+ def __post_init__(self):
37
+ if self.custom_filters is None:
38
+ self.custom_filters = []
39
+ if self.stop_sequences is None:
40
+ self.stop_sequences = []
41
+
42
+
43
+ class BaseTemplateProfile(ABC):
44
+ """Base class for all template profiles."""
45
+
46
+ def __init__(self, config: Optional[TemplateConfig] = None):
47
+ """Initialize the template profile."""
48
+ self.config = config or self.get_default_config()
49
+ self._tokenizer = None
50
+
51
+ @abstractmethod
52
+ def get_default_config(self) -> TemplateConfig:
53
+ """Return the default configuration for this template."""
54
+ pass
55
+
56
+ @abstractmethod
57
+ def format_messages(self, messages: List[Dict[str, str]], add_generation_prompt: bool = True) -> str:
58
+ """Format a list of messages into the model's expected format.
59
+
60
+ Args:
61
+ messages: List of message dictionaries with 'role' and 'content'
62
+ add_generation_prompt: Whether to add the assistant prompt at the end
63
+
64
+ Returns:
65
+ Formatted prompt string
66
+ """
67
+ pass
68
+
69
+ @abstractmethod
70
+ def process_response(self, raw_output: str) -> str:
71
+ """Process the model's raw output to clean it up.
72
+
73
+ Args:
74
+ raw_output: Raw text from the model
75
+
76
+ Returns:
77
+ Cleaned output text
78
+ """
79
+ pass
80
+
81
+ def supports_streaming(self) -> bool:
82
+ """Check if this profile supports streaming response processing.
83
+
84
+ Returns:
85
+ True if the profile has streaming capabilities, False otherwise
86
+ """
87
+ return hasattr(self, 'process_streaming_response')
88
+
89
+ def can_handle(self, model_name: str, tokenizer: Any = None) -> Tuple[bool, float]:
90
+ """Check if this profile can handle the given model.
91
+
92
+ Args:
93
+ model_name: Name of the model
94
+ tokenizer: Optional tokenizer object
95
+
96
+ Returns:
97
+ Tuple of (can_handle, confidence_score)
98
+ confidence_score is between 0.0 and 1.0
99
+ """
100
+ return False, 0.0
101
+
102
+ def set_tokenizer(self, tokenizer: Any) -> None:
103
+ """Set the tokenizer for this profile."""
104
+ self._tokenizer = tokenizer
105
+
106
+ def get_stop_sequences(self) -> List[str]:
107
+ """Get the stop sequences for this template."""
108
+ return self.config.stop_sequences.copy()
109
+
110
+ def update_config(self, **kwargs) -> None:
111
+ """Update configuration values."""
112
+ for key, value in kwargs.items():
113
+ if hasattr(self.config, key):
114
+ setattr(self.config, key, value)
115
+
116
+ def to_dict(self) -> Dict[str, Any]:
117
+ """Convert profile to dictionary representation."""
118
+ return {
119
+ 'name': self.config.name,
120
+ 'type': self.config.template_type.value,
121
+ 'description': self.config.description,
122
+ 'supports_system': self.config.supports_system_prompt,
123
+ 'supports_multi_turn': self.config.supports_multi_turn,
124
+ 'strip_special_tokens': self.config.strip_special_tokens,
125
+ 'custom_filters': self.config.custom_filters,
126
+ 'stop_sequences': self.config.stop_sequences
127
+ }
128
+
129
+ @classmethod
130
+ def from_dict(cls, data: Dict[str, Any]) -> 'BaseTemplateProfile':
131
+ """Create profile from dictionary representation."""
132
+ config = TemplateConfig(
133
+ name=data.get('name', 'Unknown'),
134
+ description=data.get('description', ''),
135
+ template_type=TemplateType(data.get('type', 'unknown')),
136
+ supports_system_prompt=data.get('supports_system', True),
137
+ supports_multi_turn=data.get('supports_multi_turn', True),
138
+ strip_special_tokens=data.get('strip_special_tokens', False),
139
+ custom_filters=data.get('custom_filters', []),
140
+ stop_sequences=data.get('stop_sequences', [])
141
+ )
142
+ return cls(config)
@@ -0,0 +1,5 @@
1
+ """Complex template profiles for advanced model formats."""
2
+
3
+ from cortex.template_registry.template_profiles.complex.reasoning import ReasoningProfile
4
+
5
+ __all__ = ['ReasoningProfile']