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.
- cortex/__init__.py +73 -0
- cortex/__main__.py +83 -0
- cortex/config.py +329 -0
- cortex/conversation_manager.py +468 -0
- cortex/fine_tuning/__init__.py +8 -0
- cortex/fine_tuning/dataset.py +332 -0
- cortex/fine_tuning/mlx_lora_trainer.py +502 -0
- cortex/fine_tuning/trainer.py +957 -0
- cortex/fine_tuning/wizard.py +707 -0
- cortex/gpu_validator.py +467 -0
- cortex/inference_engine.py +727 -0
- cortex/metal/__init__.py +275 -0
- cortex/metal/gpu_validator.py +177 -0
- cortex/metal/memory_pool.py +886 -0
- cortex/metal/mlx_accelerator.py +678 -0
- cortex/metal/mlx_converter.py +638 -0
- cortex/metal/mps_optimizer.py +417 -0
- cortex/metal/optimizer.py +665 -0
- cortex/metal/performance_profiler.py +364 -0
- cortex/model_downloader.py +130 -0
- cortex/model_manager.py +2187 -0
- cortex/quantization/__init__.py +5 -0
- cortex/quantization/dynamic_quantizer.py +736 -0
- cortex/template_registry/__init__.py +15 -0
- cortex/template_registry/auto_detector.py +144 -0
- cortex/template_registry/config_manager.py +234 -0
- cortex/template_registry/interactive.py +260 -0
- cortex/template_registry/registry.py +347 -0
- cortex/template_registry/template_profiles/__init__.py +5 -0
- cortex/template_registry/template_profiles/base.py +142 -0
- cortex/template_registry/template_profiles/complex/__init__.py +5 -0
- cortex/template_registry/template_profiles/complex/reasoning.py +263 -0
- cortex/template_registry/template_profiles/standard/__init__.py +9 -0
- cortex/template_registry/template_profiles/standard/alpaca.py +73 -0
- cortex/template_registry/template_profiles/standard/chatml.py +82 -0
- cortex/template_registry/template_profiles/standard/gemma.py +103 -0
- cortex/template_registry/template_profiles/standard/llama.py +87 -0
- cortex/template_registry/template_profiles/standard/simple.py +65 -0
- cortex/ui/__init__.py +120 -0
- cortex/ui/cli.py +1685 -0
- cortex/ui/markdown_render.py +185 -0
- cortex/ui/terminal_app.py +534 -0
- cortex_llm-1.0.0.dist-info/METADATA +275 -0
- cortex_llm-1.0.0.dist-info/RECORD +48 -0
- cortex_llm-1.0.0.dist-info/WHEEL +5 -0
- cortex_llm-1.0.0.dist-info/entry_points.txt +2 -0
- cortex_llm-1.0.0.dist-info/licenses/LICENSE +21 -0
- cortex_llm-1.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"""Template Registry System for Cortex.
|
|
2
|
+
|
|
3
|
+
This module provides intelligent template management for different model types,
|
|
4
|
+
handling various chat formats and output processing requirements.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from cortex.template_registry.registry import TemplateRegistry
|
|
8
|
+
from cortex.template_registry.auto_detector import TemplateDetector
|
|
9
|
+
from cortex.template_registry.template_profiles.base import BaseTemplateProfile
|
|
10
|
+
|
|
11
|
+
__all__ = [
|
|
12
|
+
'TemplateRegistry',
|
|
13
|
+
'TemplateDetector',
|
|
14
|
+
'BaseTemplateProfile'
|
|
15
|
+
]
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
"""Automatic template detection for models."""
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
from typing import List, Dict, Any, Optional, Tuple
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
from cortex.template_registry.template_profiles.base import BaseTemplateProfile, TemplateType
|
|
8
|
+
from cortex.template_registry.template_profiles.standard import (
|
|
9
|
+
ChatMLProfile, LlamaProfile, AlpacaProfile, SimpleProfile, GemmaProfile
|
|
10
|
+
)
|
|
11
|
+
from cortex.template_registry.template_profiles.complex import ReasoningProfile
|
|
12
|
+
|
|
13
|
+
logger = logging.getLogger(__name__)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class TemplateDetector:
|
|
17
|
+
"""Automatically detect the best template for a model."""
|
|
18
|
+
|
|
19
|
+
def __init__(self):
|
|
20
|
+
"""Initialize the detector with all available profiles."""
|
|
21
|
+
self.profiles = [
|
|
22
|
+
ReasoningProfile(),
|
|
23
|
+
GemmaProfile(), # Add Gemma profile with high priority
|
|
24
|
+
ChatMLProfile(),
|
|
25
|
+
LlamaProfile(),
|
|
26
|
+
AlpacaProfile(),
|
|
27
|
+
SimpleProfile() # Fallback
|
|
28
|
+
]
|
|
29
|
+
|
|
30
|
+
def detect_template(
|
|
31
|
+
self,
|
|
32
|
+
model_name: str,
|
|
33
|
+
model_path: Optional[Path] = None,
|
|
34
|
+
tokenizer: Any = None
|
|
35
|
+
) -> Tuple[BaseTemplateProfile, float]:
|
|
36
|
+
"""Detect the best template for a model.
|
|
37
|
+
|
|
38
|
+
Args:
|
|
39
|
+
model_name: Name of the model
|
|
40
|
+
model_path: Optional path to model files
|
|
41
|
+
tokenizer: Optional tokenizer object
|
|
42
|
+
|
|
43
|
+
Returns:
|
|
44
|
+
Tuple of (best_profile, confidence_score)
|
|
45
|
+
"""
|
|
46
|
+
candidates = []
|
|
47
|
+
|
|
48
|
+
# Check each profile
|
|
49
|
+
for profile in self.profiles:
|
|
50
|
+
can_handle, confidence = profile.can_handle(model_name, tokenizer)
|
|
51
|
+
if can_handle:
|
|
52
|
+
candidates.append((profile, confidence))
|
|
53
|
+
logger.debug(f"Profile {profile.config.name} can handle {model_name} with confidence {confidence}")
|
|
54
|
+
|
|
55
|
+
# Sort by confidence
|
|
56
|
+
candidates.sort(key=lambda x: x[1], reverse=True)
|
|
57
|
+
|
|
58
|
+
if candidates:
|
|
59
|
+
best_profile, confidence = candidates[0]
|
|
60
|
+
logger.info(f"Selected {best_profile.config.name} template for {model_name} (confidence: {confidence:.2f})")
|
|
61
|
+
return best_profile, confidence
|
|
62
|
+
|
|
63
|
+
# Fallback to simple profile
|
|
64
|
+
logger.warning(f"No specific template found for {model_name}, using simple fallback")
|
|
65
|
+
return SimpleProfile(), 0.1
|
|
66
|
+
|
|
67
|
+
def detect_from_tokenizer_config(self, tokenizer: Any) -> Optional[TemplateType]:
|
|
68
|
+
"""Try to detect template type from tokenizer configuration."""
|
|
69
|
+
if not tokenizer:
|
|
70
|
+
return None
|
|
71
|
+
|
|
72
|
+
try:
|
|
73
|
+
# Check for chat_template attribute
|
|
74
|
+
if hasattr(tokenizer, 'chat_template'):
|
|
75
|
+
template_str = str(tokenizer.chat_template)
|
|
76
|
+
|
|
77
|
+
# Check for known patterns
|
|
78
|
+
if '<|im_start|>' in template_str:
|
|
79
|
+
return TemplateType.CHATML
|
|
80
|
+
elif '[INST]' in template_str:
|
|
81
|
+
return TemplateType.LLAMA
|
|
82
|
+
elif '<|channel|>' in template_str:
|
|
83
|
+
return TemplateType.REASONING
|
|
84
|
+
elif '### Instruction' in template_str:
|
|
85
|
+
return TemplateType.ALPACA
|
|
86
|
+
except (AttributeError, TypeError) as e:
|
|
87
|
+
logger.debug(f"Error accessing tokenizer chat_template: {e}")
|
|
88
|
+
|
|
89
|
+
try:
|
|
90
|
+
# Check tokenizer config
|
|
91
|
+
if hasattr(tokenizer, 'tokenizer_config'):
|
|
92
|
+
config = tokenizer.tokenizer_config
|
|
93
|
+
if isinstance(config, dict):
|
|
94
|
+
# Check for model type hints
|
|
95
|
+
model_type = config.get('model_type', '').lower()
|
|
96
|
+
if 'llama' in model_type:
|
|
97
|
+
return TemplateType.LLAMA
|
|
98
|
+
elif 'gpt' in model_type:
|
|
99
|
+
return TemplateType.OPENAI
|
|
100
|
+
except (AttributeError, TypeError, KeyError) as e:
|
|
101
|
+
logger.debug(f"Error accessing tokenizer config: {e}")
|
|
102
|
+
|
|
103
|
+
return None
|
|
104
|
+
|
|
105
|
+
def test_template(
|
|
106
|
+
self,
|
|
107
|
+
profile: BaseTemplateProfile,
|
|
108
|
+
test_prompt: str = "Hello, how are you?"
|
|
109
|
+
) -> Dict[str, Any]:
|
|
110
|
+
"""Test a template profile with a sample prompt.
|
|
111
|
+
|
|
112
|
+
Args:
|
|
113
|
+
profile: Template profile to test
|
|
114
|
+
test_prompt: Test prompt to use
|
|
115
|
+
|
|
116
|
+
Returns:
|
|
117
|
+
Dictionary with test results
|
|
118
|
+
"""
|
|
119
|
+
messages = [
|
|
120
|
+
{"role": "user", "content": test_prompt}
|
|
121
|
+
]
|
|
122
|
+
|
|
123
|
+
formatted = profile.format_messages(messages)
|
|
124
|
+
|
|
125
|
+
# Simulate a response
|
|
126
|
+
sample_response = "I'm doing well, thank you for asking! How can I help you today?"
|
|
127
|
+
|
|
128
|
+
# Add reasoning tokens for reasoning profile
|
|
129
|
+
if profile.config.template_type == TemplateType.REASONING:
|
|
130
|
+
sample_response = (
|
|
131
|
+
"<|channel|>analysis<|message|>The user is greeting me and asking about my state. "
|
|
132
|
+
"I should respond politely.<|end|>"
|
|
133
|
+
f"<|channel|>final<|message|>{sample_response}<|end|>"
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
processed = profile.process_response(sample_response)
|
|
137
|
+
|
|
138
|
+
return {
|
|
139
|
+
'profile_name': profile.config.name,
|
|
140
|
+
'formatted_prompt': formatted,
|
|
141
|
+
'raw_response': sample_response,
|
|
142
|
+
'processed_response': processed,
|
|
143
|
+
'template_type': profile.config.template_type.value
|
|
144
|
+
}
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
"""Configuration manager for template registry."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import logging
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from typing import Dict, Any, Optional, List
|
|
7
|
+
from datetime import datetime
|
|
8
|
+
from dataclasses import dataclass, asdict
|
|
9
|
+
|
|
10
|
+
logger = logging.getLogger(__name__)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
# Configuration schema for validation
|
|
14
|
+
CONFIG_SCHEMA = {
|
|
15
|
+
"version": str,
|
|
16
|
+
"models": dict,
|
|
17
|
+
"global_settings": dict
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
MODEL_CONFIG_SCHEMA = {
|
|
21
|
+
"detected_type": str,
|
|
22
|
+
"user_preference": str,
|
|
23
|
+
"custom_filters": list,
|
|
24
|
+
"show_reasoning": bool,
|
|
25
|
+
"last_updated": str,
|
|
26
|
+
"confidence": float
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@dataclass
|
|
31
|
+
class ModelTemplateConfig:
|
|
32
|
+
"""Configuration for a specific model's template."""
|
|
33
|
+
detected_type: str
|
|
34
|
+
user_preference: str
|
|
35
|
+
custom_filters: List[str]
|
|
36
|
+
show_reasoning: bool = False
|
|
37
|
+
last_updated: str = ""
|
|
38
|
+
confidence: float = 0.0
|
|
39
|
+
|
|
40
|
+
def __post_init__(self):
|
|
41
|
+
if not self.last_updated:
|
|
42
|
+
self.last_updated = datetime.now().isoformat()
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
@dataclass
|
|
46
|
+
class GlobalSettings:
|
|
47
|
+
"""Global template settings."""
|
|
48
|
+
auto_detect: bool = True
|
|
49
|
+
prompt_on_unknown: bool = True
|
|
50
|
+
verbose_mode: bool = False
|
|
51
|
+
cache_templates: bool = True
|
|
52
|
+
default_fallback: str = "simple"
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class TemplateConfigManager:
|
|
56
|
+
"""Manage template configurations and user preferences."""
|
|
57
|
+
|
|
58
|
+
def __init__(self, config_path: Optional[Path] = None):
|
|
59
|
+
"""Initialize the configuration manager."""
|
|
60
|
+
self.config_path = config_path or (Path.home() / ".cortex" / "template_config.json")
|
|
61
|
+
self.config_path.parent.mkdir(parents=True, exist_ok=True)
|
|
62
|
+
self.config = self._load_config()
|
|
63
|
+
|
|
64
|
+
def _validate_config_structure(self, config: Dict[str, Any]) -> bool:
|
|
65
|
+
"""Validate configuration structure matches expected schema.
|
|
66
|
+
|
|
67
|
+
Args:
|
|
68
|
+
config: Configuration dictionary to validate
|
|
69
|
+
|
|
70
|
+
Returns:
|
|
71
|
+
True if valid, False otherwise
|
|
72
|
+
"""
|
|
73
|
+
# Check top-level keys
|
|
74
|
+
for key, expected_type in CONFIG_SCHEMA.items():
|
|
75
|
+
if key not in config:
|
|
76
|
+
logger.warning(f"Missing required config key: {key}")
|
|
77
|
+
return False
|
|
78
|
+
if not isinstance(config[key], expected_type):
|
|
79
|
+
logger.warning(f"Invalid type for {key}: expected {expected_type}, got {type(config[key])}")
|
|
80
|
+
return False
|
|
81
|
+
|
|
82
|
+
# Validate model configs
|
|
83
|
+
if "models" in config:
|
|
84
|
+
for model_name, model_config in config["models"].items():
|
|
85
|
+
if not isinstance(model_config, dict):
|
|
86
|
+
logger.warning(f"Invalid model config for {model_name}")
|
|
87
|
+
return False
|
|
88
|
+
# Validate model config structure
|
|
89
|
+
for key, expected_type in MODEL_CONFIG_SCHEMA.items():
|
|
90
|
+
if key in model_config and not isinstance(model_config[key], expected_type):
|
|
91
|
+
logger.warning(f"Invalid type for {model_name}.{key}")
|
|
92
|
+
return False
|
|
93
|
+
|
|
94
|
+
return True
|
|
95
|
+
|
|
96
|
+
def _load_config(self) -> Dict[str, Any]:
|
|
97
|
+
"""Load configuration from file."""
|
|
98
|
+
if self.config_path.exists():
|
|
99
|
+
try:
|
|
100
|
+
with open(self.config_path, 'r') as f:
|
|
101
|
+
config = json.load(f)
|
|
102
|
+
|
|
103
|
+
# Validate loaded configuration
|
|
104
|
+
if self._validate_config_structure(config):
|
|
105
|
+
return config
|
|
106
|
+
else:
|
|
107
|
+
logger.warning("Invalid config structure, using defaults")
|
|
108
|
+
return self._get_default_config()
|
|
109
|
+
|
|
110
|
+
except json.JSONDecodeError as e:
|
|
111
|
+
logger.error(f"Invalid JSON in template config: {e}")
|
|
112
|
+
except IOError as e:
|
|
113
|
+
logger.error(f"Error reading template config file: {e}")
|
|
114
|
+
except PermissionError as e:
|
|
115
|
+
logger.error(f"Permission denied reading template config: {e}")
|
|
116
|
+
|
|
117
|
+
# Return default config
|
|
118
|
+
return self._get_default_config()
|
|
119
|
+
|
|
120
|
+
def _get_default_config(self) -> Dict[str, Any]:
|
|
121
|
+
"""Get default configuration."""
|
|
122
|
+
return {
|
|
123
|
+
"version": "1.0",
|
|
124
|
+
"models": {},
|
|
125
|
+
"global_settings": asdict(GlobalSettings())
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
def _save_config(self) -> None:
|
|
129
|
+
"""Save configuration to file."""
|
|
130
|
+
try:
|
|
131
|
+
# Ensure parent directory exists
|
|
132
|
+
self.config_path.parent.mkdir(parents=True, exist_ok=True)
|
|
133
|
+
|
|
134
|
+
# Write to temporary file first for atomicity
|
|
135
|
+
temp_path = self.config_path.with_suffix('.tmp')
|
|
136
|
+
with open(temp_path, 'w') as f:
|
|
137
|
+
json.dump(self.config, f, indent=2)
|
|
138
|
+
|
|
139
|
+
# Atomic rename
|
|
140
|
+
temp_path.replace(self.config_path)
|
|
141
|
+
logger.debug(f"Saved template config to {self.config_path}")
|
|
142
|
+
|
|
143
|
+
except IOError as e:
|
|
144
|
+
logger.error(f"IO error saving template config: {e}")
|
|
145
|
+
except PermissionError as e:
|
|
146
|
+
logger.error(f"Permission denied saving template config: {e}")
|
|
147
|
+
except OSError as e:
|
|
148
|
+
logger.error(f"OS error saving template config: {e}")
|
|
149
|
+
|
|
150
|
+
def get_model_config(self, model_name: str) -> Optional[ModelTemplateConfig]:
|
|
151
|
+
"""Get configuration for a specific model."""
|
|
152
|
+
if model_name in self.config.get("models", {}):
|
|
153
|
+
data = self.config["models"][model_name]
|
|
154
|
+
return ModelTemplateConfig(
|
|
155
|
+
detected_type=data.get("detected_type", "unknown"),
|
|
156
|
+
user_preference=data.get("user_preference", "auto"),
|
|
157
|
+
custom_filters=data.get("custom_filters", []),
|
|
158
|
+
show_reasoning=data.get("show_reasoning", False),
|
|
159
|
+
last_updated=data.get("last_updated", ""),
|
|
160
|
+
confidence=data.get("confidence", 0.0)
|
|
161
|
+
)
|
|
162
|
+
return None
|
|
163
|
+
|
|
164
|
+
def save_model_config(self, model_name: str, config: ModelTemplateConfig) -> None:
|
|
165
|
+
"""Save configuration for a specific model."""
|
|
166
|
+
if "models" not in self.config:
|
|
167
|
+
self.config["models"] = {}
|
|
168
|
+
|
|
169
|
+
self.config["models"][model_name] = {
|
|
170
|
+
"detected_type": config.detected_type,
|
|
171
|
+
"user_preference": config.user_preference,
|
|
172
|
+
"custom_filters": config.custom_filters,
|
|
173
|
+
"show_reasoning": config.show_reasoning,
|
|
174
|
+
"last_updated": datetime.now().isoformat(),
|
|
175
|
+
"confidence": config.confidence
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
self._save_config()
|
|
179
|
+
logger.info(f"Saved template configuration for {model_name}")
|
|
180
|
+
|
|
181
|
+
def get_global_settings(self) -> GlobalSettings:
|
|
182
|
+
"""Get global settings."""
|
|
183
|
+
data = self.config.get("global_settings", {})
|
|
184
|
+
return GlobalSettings(
|
|
185
|
+
auto_detect=data.get("auto_detect", True),
|
|
186
|
+
prompt_on_unknown=data.get("prompt_on_unknown", True),
|
|
187
|
+
verbose_mode=data.get("verbose_mode", False),
|
|
188
|
+
cache_templates=data.get("cache_templates", True),
|
|
189
|
+
default_fallback=data.get("default_fallback", "simple")
|
|
190
|
+
)
|
|
191
|
+
|
|
192
|
+
def update_global_settings(self, **kwargs) -> None:
|
|
193
|
+
"""Update global settings."""
|
|
194
|
+
settings = self.get_global_settings()
|
|
195
|
+
|
|
196
|
+
for key, value in kwargs.items():
|
|
197
|
+
if hasattr(settings, key):
|
|
198
|
+
setattr(settings, key, value)
|
|
199
|
+
|
|
200
|
+
self.config["global_settings"] = asdict(settings)
|
|
201
|
+
self._save_config()
|
|
202
|
+
|
|
203
|
+
def list_configured_models(self) -> List[str]:
|
|
204
|
+
"""List all models with saved configurations."""
|
|
205
|
+
return list(self.config.get("models", {}).keys())
|
|
206
|
+
|
|
207
|
+
def remove_model_config(self, model_name: str) -> bool:
|
|
208
|
+
"""Remove configuration for a model."""
|
|
209
|
+
if model_name in self.config.get("models", {}):
|
|
210
|
+
del self.config["models"][model_name]
|
|
211
|
+
self._save_config()
|
|
212
|
+
logger.info(f"Removed template configuration for {model_name}")
|
|
213
|
+
return True
|
|
214
|
+
return False
|
|
215
|
+
|
|
216
|
+
def export_config(self) -> Dict[str, Any]:
|
|
217
|
+
"""Export the entire configuration."""
|
|
218
|
+
return self.config.copy()
|
|
219
|
+
|
|
220
|
+
def import_config(self, config: Dict[str, Any]) -> None:
|
|
221
|
+
"""Import a configuration.
|
|
222
|
+
|
|
223
|
+
Args:
|
|
224
|
+
config: Configuration dictionary to import
|
|
225
|
+
|
|
226
|
+
Raises:
|
|
227
|
+
ValueError: If configuration is invalid
|
|
228
|
+
"""
|
|
229
|
+
if not self._validate_config_structure(config):
|
|
230
|
+
raise ValueError("Invalid configuration structure")
|
|
231
|
+
|
|
232
|
+
self.config = config
|
|
233
|
+
self._save_config()
|
|
234
|
+
logger.info("Imported template configuration")
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
"""Interactive template setup for user-friendly configuration."""
|
|
2
|
+
|
|
3
|
+
from typing import Optional, Dict, Any, List
|
|
4
|
+
from rich.console import Console
|
|
5
|
+
from rich.table import Table
|
|
6
|
+
from rich.prompt import Prompt, Confirm
|
|
7
|
+
|
|
8
|
+
from cortex.template_registry.template_profiles.base import BaseTemplateProfile, TemplateType
|
|
9
|
+
from cortex.template_registry.auto_detector import TemplateDetector
|
|
10
|
+
from cortex.template_registry.config_manager import ModelTemplateConfig
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class InteractiveTemplateSetup:
|
|
14
|
+
"""Interactive template configuration wizard."""
|
|
15
|
+
|
|
16
|
+
def __init__(self, console: Optional[Console] = None):
|
|
17
|
+
"""Initialize the interactive setup."""
|
|
18
|
+
self.console = console or Console()
|
|
19
|
+
self.detector = TemplateDetector()
|
|
20
|
+
|
|
21
|
+
def setup_model_template(
|
|
22
|
+
self,
|
|
23
|
+
model_name: str,
|
|
24
|
+
tokenizer: Any = None,
|
|
25
|
+
current_config: Optional[ModelTemplateConfig] = None
|
|
26
|
+
) -> ModelTemplateConfig:
|
|
27
|
+
"""Interactive setup for model template.
|
|
28
|
+
|
|
29
|
+
Args:
|
|
30
|
+
model_name: Name of the model
|
|
31
|
+
tokenizer: Optional tokenizer object
|
|
32
|
+
current_config: Existing configuration if any
|
|
33
|
+
|
|
34
|
+
Returns:
|
|
35
|
+
Updated model template configuration
|
|
36
|
+
"""
|
|
37
|
+
self.console.print(f"\n✓ Model loaded: [bold cyan]{model_name}[/bold cyan]")
|
|
38
|
+
|
|
39
|
+
# Detect template
|
|
40
|
+
profile, confidence = self.detector.detect_template(model_name, tokenizer=tokenizer)
|
|
41
|
+
|
|
42
|
+
if confidence < 0.5:
|
|
43
|
+
self.console.print("\n⚠️ [yellow]Template configuration needed for optimal performance[/yellow]")
|
|
44
|
+
|
|
45
|
+
self.console.print(f"\nDetecting template format...")
|
|
46
|
+
self.console.print(f"✓ Found: [green]{profile.config.description}[/green] (confidence: {confidence:.0%})")
|
|
47
|
+
|
|
48
|
+
# Check if this is a reasoning model
|
|
49
|
+
is_reasoning = profile.config.template_type == TemplateType.REASONING
|
|
50
|
+
|
|
51
|
+
if is_reasoning:
|
|
52
|
+
self.console.print("\n[yellow]Note: This model includes internal reasoning/analysis in its output.[/yellow]")
|
|
53
|
+
|
|
54
|
+
# Show options
|
|
55
|
+
self.console.print("\nHow would you like to handle this model's output?\n")
|
|
56
|
+
|
|
57
|
+
options = []
|
|
58
|
+
if is_reasoning:
|
|
59
|
+
options = [
|
|
60
|
+
("simple", "Simple mode - Hide internal reasoning (recommended)", True),
|
|
61
|
+
("full", "Full mode - Show all model outputs", False),
|
|
62
|
+
("custom", "Custom - Configure manually", False),
|
|
63
|
+
("test", "Test - See examples of each mode", False)
|
|
64
|
+
]
|
|
65
|
+
else:
|
|
66
|
+
options = [
|
|
67
|
+
("auto", "Automatic - Use detected template", True),
|
|
68
|
+
("custom", "Custom - Configure manually", False),
|
|
69
|
+
("test", "Test - See examples with different templates", False)
|
|
70
|
+
]
|
|
71
|
+
|
|
72
|
+
# Display options
|
|
73
|
+
for i, (key, desc, recommended) in enumerate(options, 1):
|
|
74
|
+
marker = " [green](recommended)[/green]" if recommended else ""
|
|
75
|
+
self.console.print(f"[{i}] {desc}{marker}")
|
|
76
|
+
|
|
77
|
+
# Get user choice
|
|
78
|
+
choice = Prompt.ask(
|
|
79
|
+
f"\nSelect option (1-{len(options)})",
|
|
80
|
+
default="1",
|
|
81
|
+
choices=[str(i) for i in range(1, len(options) + 1)]
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
selected_key = options[int(choice) - 1][0]
|
|
85
|
+
|
|
86
|
+
# Handle selection
|
|
87
|
+
if selected_key == "test":
|
|
88
|
+
self._show_template_tests(model_name, profile)
|
|
89
|
+
# Recurse to get actual selection
|
|
90
|
+
return self.setup_model_template(model_name, tokenizer, current_config)
|
|
91
|
+
|
|
92
|
+
elif selected_key == "custom":
|
|
93
|
+
return self._custom_setup(model_name, profile)
|
|
94
|
+
|
|
95
|
+
else:
|
|
96
|
+
# Create configuration
|
|
97
|
+
config = ModelTemplateConfig(
|
|
98
|
+
detected_type=profile.config.template_type.value,
|
|
99
|
+
user_preference=selected_key,
|
|
100
|
+
custom_filters=profile.config.custom_filters,
|
|
101
|
+
show_reasoning=(selected_key == "full") if is_reasoning else False,
|
|
102
|
+
confidence=confidence
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
self.console.print(f"\n✓ Template configured: [green]{selected_key} mode[/green]")
|
|
106
|
+
self.console.print("✓ Configuration saved for future use")
|
|
107
|
+
self.console.print("\n[dim]Tip: Use /template to adjust settings anytime[/dim]")
|
|
108
|
+
|
|
109
|
+
return config
|
|
110
|
+
|
|
111
|
+
def _show_template_tests(self, model_name: str, detected_profile: BaseTemplateProfile) -> None:
|
|
112
|
+
"""Show examples of different template modes."""
|
|
113
|
+
self.console.print("\n[bold]Testing different template modes:[/bold]\n")
|
|
114
|
+
|
|
115
|
+
test_prompt = "What is 2+2?"
|
|
116
|
+
|
|
117
|
+
# Test different profiles
|
|
118
|
+
profiles_to_test = []
|
|
119
|
+
|
|
120
|
+
if detected_profile.config.template_type == TemplateType.REASONING:
|
|
121
|
+
# Test with and without reasoning
|
|
122
|
+
simple_profile = detected_profile.__class__()
|
|
123
|
+
simple_profile.config.show_reasoning = False
|
|
124
|
+
|
|
125
|
+
full_profile = detected_profile.__class__()
|
|
126
|
+
full_profile.config.show_reasoning = True
|
|
127
|
+
|
|
128
|
+
profiles_to_test = [
|
|
129
|
+
("Simple Mode", simple_profile),
|
|
130
|
+
("Full Mode", full_profile)
|
|
131
|
+
]
|
|
132
|
+
else:
|
|
133
|
+
# Test different template types
|
|
134
|
+
from cortex.template_registry.template_profiles.standard import (
|
|
135
|
+
ChatMLProfile, LlamaProfile, SimpleProfile
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
profiles_to_test = [
|
|
139
|
+
("Detected", detected_profile),
|
|
140
|
+
("ChatML", ChatMLProfile()),
|
|
141
|
+
("Llama", LlamaProfile()),
|
|
142
|
+
("Simple", SimpleProfile())
|
|
143
|
+
]
|
|
144
|
+
|
|
145
|
+
for name, profile in profiles_to_test:
|
|
146
|
+
result = self.detector.test_template(profile, test_prompt)
|
|
147
|
+
|
|
148
|
+
self.console.print(f"[bold cyan]{name}:[/bold cyan]")
|
|
149
|
+
self.console.print("─" * 40)
|
|
150
|
+
|
|
151
|
+
# Show formatted prompt
|
|
152
|
+
self.console.print("[dim]Formatted prompt:[/dim]")
|
|
153
|
+
self.console.print(f" {result['formatted_prompt'][:100]}..." if len(result['formatted_prompt']) > 100 else f" {result['formatted_prompt']}")
|
|
154
|
+
|
|
155
|
+
# Show processed response
|
|
156
|
+
self.console.print("[dim]Output:[/dim]")
|
|
157
|
+
self.console.print(f" {result['processed_response']}")
|
|
158
|
+
self.console.print()
|
|
159
|
+
|
|
160
|
+
def _custom_setup(self, model_name: str, detected_profile: BaseTemplateProfile) -> ModelTemplateConfig:
|
|
161
|
+
"""Custom template configuration."""
|
|
162
|
+
self.console.print("\n[bold]Custom Template Configuration[/bold]\n")
|
|
163
|
+
|
|
164
|
+
# Select template type
|
|
165
|
+
template_types = [
|
|
166
|
+
("chatml", "ChatML format"),
|
|
167
|
+
("llama", "Llama format"),
|
|
168
|
+
("alpaca", "Alpaca format"),
|
|
169
|
+
("reasoning", "Reasoning/CoT format"),
|
|
170
|
+
("simple", "Simple format")
|
|
171
|
+
]
|
|
172
|
+
|
|
173
|
+
self.console.print("Available template types:")
|
|
174
|
+
for i, (key, desc) in enumerate(template_types, 1):
|
|
175
|
+
self.console.print(f"[{i}] {desc}")
|
|
176
|
+
|
|
177
|
+
choice = Prompt.ask(
|
|
178
|
+
"Select template type",
|
|
179
|
+
default="1",
|
|
180
|
+
choices=[str(i) for i in range(1, len(template_types) + 1)]
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
selected_type = template_types[int(choice) - 1][0]
|
|
184
|
+
|
|
185
|
+
# Configure filters
|
|
186
|
+
custom_filters = []
|
|
187
|
+
if Confirm.ask("Configure custom output filters?", default=False):
|
|
188
|
+
filters_input = Prompt.ask("Enter tokens to filter (comma-separated)")
|
|
189
|
+
custom_filters = [f.strip() for f in filters_input.split(",") if f.strip()]
|
|
190
|
+
|
|
191
|
+
# Show reasoning option
|
|
192
|
+
show_reasoning = False
|
|
193
|
+
if selected_type == "reasoning":
|
|
194
|
+
show_reasoning = Confirm.ask("Show internal reasoning/analysis?", default=False)
|
|
195
|
+
|
|
196
|
+
config = ModelTemplateConfig(
|
|
197
|
+
detected_type=selected_type,
|
|
198
|
+
user_preference="custom",
|
|
199
|
+
custom_filters=custom_filters,
|
|
200
|
+
show_reasoning=show_reasoning,
|
|
201
|
+
confidence=1.0 # User manually configured
|
|
202
|
+
)
|
|
203
|
+
|
|
204
|
+
self.console.print("\n✓ Custom template configured")
|
|
205
|
+
return config
|
|
206
|
+
|
|
207
|
+
def show_current_config(self, model_name: str, config: ModelTemplateConfig) -> None:
|
|
208
|
+
"""Display current configuration for a model."""
|
|
209
|
+
table = Table(title=f"Template Configuration for {model_name}")
|
|
210
|
+
table.add_column("Setting", style="cyan")
|
|
211
|
+
table.add_column("Value", style="green")
|
|
212
|
+
|
|
213
|
+
table.add_row("Template Type", config.detected_type)
|
|
214
|
+
table.add_row("User Preference", config.user_preference)
|
|
215
|
+
table.add_row("Show Reasoning", str(config.show_reasoning))
|
|
216
|
+
table.add_row("Custom Filters", ", ".join(config.custom_filters) if config.custom_filters else "None")
|
|
217
|
+
table.add_row("Confidence", f"{config.confidence:.0%}")
|
|
218
|
+
table.add_row("Last Updated", config.last_updated)
|
|
219
|
+
|
|
220
|
+
self.console.print(table)
|
|
221
|
+
|
|
222
|
+
def quick_adjust_template(self, model_name: str, config: ModelTemplateConfig) -> ModelTemplateConfig:
|
|
223
|
+
"""Quick adjustment interface for template settings."""
|
|
224
|
+
self.console.print(f"\n[bold]Adjust template for {model_name}[/bold]\n")
|
|
225
|
+
|
|
226
|
+
self.show_current_config(model_name, config)
|
|
227
|
+
|
|
228
|
+
self.console.print("\n[1] Toggle reasoning display")
|
|
229
|
+
self.console.print("[2] Change template type")
|
|
230
|
+
self.console.print("[3] Edit filters")
|
|
231
|
+
self.console.print("[4] Reset to defaults")
|
|
232
|
+
self.console.print("[0] Cancel")
|
|
233
|
+
|
|
234
|
+
choice = Prompt.ask("Select option", choices=["0", "1", "2", "3", "4"])
|
|
235
|
+
|
|
236
|
+
if choice == "1":
|
|
237
|
+
config.show_reasoning = not config.show_reasoning
|
|
238
|
+
self.console.print(f"✓ Reasoning display: [green]{'enabled' if config.show_reasoning else 'disabled'}[/green]")
|
|
239
|
+
|
|
240
|
+
elif choice == "2":
|
|
241
|
+
return self._custom_setup(model_name, None)
|
|
242
|
+
|
|
243
|
+
elif choice == "3":
|
|
244
|
+
filters_input = Prompt.ask("Enter tokens to filter (comma-separated)", default=",".join(config.custom_filters))
|
|
245
|
+
config.custom_filters = [f.strip() for f in filters_input.split(",") if f.strip()]
|
|
246
|
+
self.console.print("✓ Filters updated")
|
|
247
|
+
|
|
248
|
+
elif choice == "4":
|
|
249
|
+
# Reset to detected defaults
|
|
250
|
+
profile, confidence = self.detector.detect_template(model_name)
|
|
251
|
+
config = ModelTemplateConfig(
|
|
252
|
+
detected_type=profile.config.template_type.value,
|
|
253
|
+
user_preference="auto",
|
|
254
|
+
custom_filters=profile.config.custom_filters,
|
|
255
|
+
show_reasoning=False,
|
|
256
|
+
confidence=confidence
|
|
257
|
+
)
|
|
258
|
+
self.console.print("✓ Reset to defaults")
|
|
259
|
+
|
|
260
|
+
return config
|