# -*- coding: utf-8 -*- """ Pydantic models for Meta Tools configuration validation. This module defines the data models used for validating Meta_tool_config.json and ensuring type safety during configuration loading. """ from typing import Dict, List from pydantic import ( BaseModel, Field, field_validator, model_validator, RootModel, ) class CategoryConfig(BaseModel): """Configuration model for a single tool category. Attributes: description (`str`): A comprehensive description of what this category manages and its functional scope. tool_usage_notes (`str`): Special usage notes and considerations for tools in this category. Can be empty string if no special notes are needed. tools (`List[str]`): List of tool names that belong to this category. Each tool name must be a non-empty string. """ description: str = Field( ..., min_length=10, description=( "Comprehensive description of the category's purpose and scope" ), ) tool_usage_notes: str = Field( default="", description="Special usage notes for tools in this category", ) tools: List[str] = Field( ..., min_items=1, description="List of tool names belonging to this category", ) @field_validator("tools") @classmethod def validate_tools(cls, v): """Validate that all tool names are non-empty strings.""" if not v: raise ValueError("Category must have at least one tool") for tool_name in v: if not isinstance(tool_name, str) or not tool_name.strip(): raise ValueError( f"Tool name must be a non-empty string, got: {tool_name}", ) return v @field_validator("description") @classmethod def validate_description(cls, v): """Validate that description is meaningful.""" if not v.strip(): raise ValueError("Description cannot be empty") return v.strip() class MetaToolConfig(RootModel[Dict[str, CategoryConfig]]): """Root configuration model for Meta Tools system. This model represents the entire Meta_tool_config.json structure and validates that all categories are properly configured. The root structure is a dictionary mapping category names to their configurations. Category names must be non-empty strings. """ root: Dict[str, CategoryConfig] = Field( ..., description="Dictionary of category configurations", ) @model_validator(mode="after") def validate_not_empty(self) -> "MetaToolConfig": """Validate that configuration has at least one category.""" if not self.root: raise ValueError("Configuration must have at least one category") # Validate category names are non-empty for category_name in self.root.keys(): if not category_name or not category_name.strip(): raise ValueError( "Category name cannot be empty or whitespace", ) return self # Dictionary-like access methods for better user experience # These allow using MetaToolConfig as if it were a dict without conversion # Example: config["category_name"] instead of config.root["category_name"] def __iter__(self): """Iterate over category names: for name in config: ...""" return iter(self.root) def __getitem__(self, key: str) -> CategoryConfig: """Access category by name: config["category_name"]""" return self.root[key] def items(self): """Get (name, config) pairs: for name, cfg in config.items(): ...""" return self.root.items() def keys(self): """Get category names: list(config.keys())""" return self.root.keys() def values(self): """Get category configs: list(config.values())""" return self.root.values() def __len__(self) -> int: """Get number of categories: len(config)""" return len(self.root) @classmethod def from_json_file(cls, file_path: str) -> "MetaToolConfig": """Load and validate configuration from JSON file. Args: file_path (`str`): Path to the Meta_tool_config.json file. Returns: `MetaToolConfig`: Validated configuration instance. Raises: ValidationError: If the configuration is invalid. FileNotFoundError: If the configuration file doesn't exist. JSONDecodeError: If the JSON is malformed. """ import json with open(file_path, "r", encoding="utf-8") as f: config_data = json.load(f) return cls(config_data) @classmethod def from_dict(cls, config_dict: Dict) -> "MetaToolConfig": """Create configuration from dictionary. Args: config_dict (`Dict`): Configuration dictionary. Returns: `MetaToolConfig`: Validated configuration instance. """ return cls(config_dict) def to_dict(self) -> Dict[str, Dict]: """Convert to plain dictionary format. Returns: `Dict[str, Dict]`: Plain dictionary representation. """ return { category_name: category_config.model_dump() for category_name, category_config in self.root.items() }