179 lines
5.4 KiB
Python
179 lines
5.4 KiB
Python
# -*- 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()
|
|
}
|