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}")