File size: 7,468 Bytes
ca9b9a8
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
import os
import json
from pathlib import Path
from typing import List, Dict, Any, Optional
import logging
from datetime import datetime

def get_available_models(directory: str = "./models") -> Dict[str, Any]:
    """Get available GGUF models in the directory"""
    try:
        path = Path(directory)
        if not path.exists():
            return {"error": f"Directory {directory} does not exist"}
        
        models = []
        for file in path.glob("*.gguf"):
            try:
                stat = file.stat()
                models.append({
                    "name": file.name,
                    "path": str(file),
                    "size_mb": round(stat.st_size / (1024 * 1024), 2),
                    "modified": datetime.fromtimestamp(stat.st_mtime).isoformat()
                })
            except Exception as e:
                logging.warning(f"Error reading {file}: {e}")
        
        # Sort by name
        models.sort(key=lambda x: x["name"])
        
        return {
            "directory": directory,
            "exists": True,
            "model_count": len(models),
            "models": models
        }
        
    except Exception as e:
        return {"error": str(e)}

def format_chat_history(history: List[List[str]], system_prompt: str = "") -> str:
    """Format chat history for the model"""
    formatted = ""
    
    if system_prompt:
        formatted += f"<|system|>\n{system_prompt}\n<|end|>\n\n"
    
    for message in history:
        role, content = message
        if role == "user":
            formatted += f"<|user|>\n{content}\n<|end|>\n\n"
        elif role == "assistant":
            formatted += f"<|assistant|>\n{content}\n<|end|>\n\n"
    
    formatted += "<|assistant|>\n"
    return formatted

def format_chat_history_messages(history: List[Dict[str, str]], system_prompt: str = "") -> str:
    """Format chat history (message format) for the model"""
    formatted = ""
    
    if system_prompt:
        formatted += f"<|system|>\n{system_prompt}\n<|end|>\n\n"
    
    for message in history:
        role = message.get("role", "")
        content = message.get("content", "")
        
        if role == "user":
            formatted += f"<|user|>\n{content}\n<|end|>\n\n"
        elif role == "assistant":
            formatted += f"<|assistant|>\n{content}\n<|end|>\n\n"
    
    formatted += "<|assistant|>\n"
    return formatted

def parse_model_info(metadata: Dict[str, Any]) -> Dict[str, Any]:
    """Parse model metadata into a readable format"""
    parsed = {
        "architecture": "Unknown",
        "parameters": "Unknown",
        "context_length": "Unknown",
        "embedding_size": "Unknown",
        "layers": "Unknown",
        "heads": "Unknown"
    }
    
    # Try to extract common fields
    if "general.architecture" in metadata:
        parsed["architecture"] = metadata["general.architecture"]
    
    if "llama.block_count" in metadata:
        parsed["layers"] = metadata["llama.block_count"]
    
    if "llama.context_length" in metadata:
        parsed["context_length"] = metadata["llama.context_length"]
    
    if "llama.embedding_length" in metadata:
        parsed["embedding_size"] = metadata["llama.embedding_length"]
    
    if "llama.attention.head_count" in metadata:
        parsed["heads"] = metadata["llama.attention.head_count"]
    
    # Estimate parameters based on architecture
    if parsed["architecture"] == "llama":
        try:
            layers = int(parsed["layers"]) if parsed["layers"] != "Unknown" else 0
            embed_size = int(parsed["embedding_size"]) if parsed["embedding_size"] != "Unknown" else 0
            
            if layers > 0 and embed_size > 0:
                # Rough estimate for LLaMA parameters
                params = layers * (12 * embed_size * embed_size + 13 * embed_size)
                if params > 1e9:
                    parsed["parameters"] = f"{params / 1e9:.1f}B"
                elif params > 1e6:
                    parsed["parameters"] = f"{params / 1e6:.1f}M"
                else:
                    parsed["parameters"] = str(params)
        except:
            pass
    
    return parsed

def save_chat_history(history: List[Dict[str, str]], filename: str = None) -> str:
    """Save chat history to a JSON file"""
    if filename is None:
        filename = f"chat_history_{datetime.now().strftime('%Y%m%d_%H%M%S')}.json"
    
    try:
        with open(filename, 'w', encoding='utf-8') as f:
            json.dump(history, f, indent=2, ensure_ascii=False)
        return filename
    except Exception as e:
        logging.error(f"Failed to save chat history: {e}")
        return ""

def load_chat_history(filename: str) -> List[Dict[str, str]]:
    """Load chat history from a JSON file"""
    try:
        with open(filename, 'r', encoding='utf-8') as f:
            return json.load(f)
    except Exception as e:
        logging.error(f"Failed to load chat history: {e}")
        return []

def estimate_tokens(text: str) -> int:
    """Estimate token count (rough approximation)"""
    # Simple approximation: ~4 characters per token
    return len(text) // 4

def validate_model_file(model_path: str) -> Dict[str, Any]:
    """Validate a model file"""
    result = {
        "valid": False,
        "exists": False,
        "readable": False,
        "size_mb": 0,
        "file_type": None,
        "error": None
    }
    
    try:
        path = Path(model_path)
        result["exists"] = path.exists()
        
        if not result["exists"]:
            result["error"] = "File does not exist"
            return result
        
        result["size_mb"] = round(path.stat().st_size / (1024 * 1024), 2)
        result["file_type"] = path.suffix.lower()
        
        if result["file_type"] != ".gguf":
            result["error"] = "Not a GGUF file"
            return result
        
        # Try to read first few bytes
        try:
            with open(path, "rb") as f:
                header = f.read(4)
                result["readable"] = len(header) == 4
                result["valid"] = result["readable"]
        except Exception as e:
            result["error"] = f"Cannot read file: {str(e)}"
        
    except Exception as e:
        result["error"] = str(e)
    
    return result

def create_default_config() -> Dict[str, Any]:
    """Create default configuration"""
    return {
        "models_directory": "./models",
        "default_context_size": 2048,
        "default_gpu_layers": 0,
        "default_temperature": 0.7,
        "default_max_tokens": 512,
        "default_top_p": 0.9,
        "default_repeat_penalty": 1.1,
        "system_prompt": "You are a helpful assistant.",
        "chat_format": "chatml",
        "auto_save_chat": True,
        "theme": "soft"
    }

def load_config(config_path: str = "config.json") -> Dict[str, Any]:
    """Load configuration from file"""
    try:
        if Path(config_path).exists():
            with open(config_path, 'r') as f:
                return json.load(f)
    except Exception as e:
        logging.warning(f"Failed to load config: {e}")
    
    return create_default_config()

def save_config(config: Dict[str, Any], config_path: str = "config.json"):
    """Save configuration to file"""
    try:
        with open(config_path, 'w') as f:
            json.dump(config, f, indent=2)
    except Exception as e:
        logging.error(f"Failed to save config: {e}")