|
- # validation/validation_service.py
- import importlib
- from typing import List, Optional
-
- from autogen_core import ComponentModel, is_component_class
- from pydantic import BaseModel
-
-
- class ValidationRequest(BaseModel):
- component: ComponentModel
-
-
- class ValidationError(BaseModel):
- field: str
- error: str
- suggestion: Optional[str] = None
-
-
- class ValidationResponse(BaseModel):
- is_valid: bool
- errors: List[ValidationError] = []
- warnings: List[ValidationError] = []
-
-
- class ValidationService:
- @staticmethod
- def validate_provider(provider: str) -> Optional[ValidationError]:
- """Validate that the provider exists and can be imported"""
- try:
- if provider in ["azure_openai_chat_completion_client", "AzureOpenAIChatCompletionClient"]:
- provider = "autogen_ext.models.openai.AzureOpenAIChatCompletionClient"
- elif provider in ["openai_chat_completion_client", "OpenAIChatCompletionClient"]:
- provider = "autogen_ext.models.openai.OpenAIChatCompletionClient"
-
- module_path, class_name = provider.rsplit(".", maxsplit=1)
- module = importlib.import_module(module_path)
- component_class = getattr(module, class_name)
-
- if not is_component_class(component_class):
- return ValidationError(
- field="provider",
- error=f"Class {provider} is not a valid component class",
- suggestion="Ensure the class inherits from Component and implements required methods",
- )
- return None
- except ImportError:
- return ValidationError(
- field="provider",
- error=f"Could not import provider {provider}",
- suggestion="Check that the provider module is installed and the path is correct",
- )
- except Exception as e:
- return ValidationError(
- field="provider",
- error=f"Error validating provider: {str(e)}",
- suggestion="Check the provider string format and class implementation",
- )
-
- @staticmethod
- def validate_component_type(component: ComponentModel) -> Optional[ValidationError]:
- """Validate the component type"""
- if not component.component_type:
- return ValidationError(
- field="component_type",
- error="Component type is missing",
- suggestion="Add a component_type field to the component configuration",
- )
-
- @staticmethod
- def validate_config_schema(component: ComponentModel) -> List[ValidationError]:
- """Validate the component configuration against its schema"""
- errors: List[ValidationError] = []
- try:
- # Convert to ComponentModel for initial validation
- model = component.model_copy(deep=True)
-
- # Get the component class
- provider = model.provider
- module_path, class_name = provider.rsplit(".", maxsplit=1)
- module = importlib.import_module(module_path)
- component_class = getattr(module, class_name)
-
- # Validate against component's schema
- if hasattr(component_class, "component_config_schema"):
- try:
- component_class.component_config_schema.model_validate(model.config)
- except Exception as e:
- errors.append(
- ValidationError(
- field="config",
- error=f"Config validation failed: {str(e)}",
- suggestion="Check that the config matches the component's schema",
- )
- )
- else:
- errors.append(
- ValidationError(
- field="config",
- error="Component class missing config schema",
- suggestion="Implement component_config_schema in the component class",
- )
- )
- except Exception as e:
- errors.append(
- ValidationError(
- field="config",
- error=f"Schema validation error: {str(e)}",
- suggestion="Check the component configuration format",
- )
- )
- return errors
-
- @staticmethod
- def validate_instantiation(component: ComponentModel) -> Optional[ValidationError]:
- """Validate that the component can be instantiated"""
- try:
- model = component.model_copy(deep=True)
- # Attempt to load the component
- module_path, class_name = model.provider.rsplit(".", maxsplit=1)
- module = importlib.import_module(module_path)
- component_class = getattr(module, class_name)
- component_class.load_component(model)
- return None
- except Exception as e:
- error_str = str(e)
-
- # Check for version compatibility issues
- if "component_version" in error_str and "_from_config_past_version is not implemented" in error_str:
- # Extract component information for a better error message
- try:
- # Get the current component version
- module_path, class_name = component.provider.rsplit(".", maxsplit=1)
- module = importlib.import_module(module_path)
- component_class = getattr(module, class_name)
- current_version = getattr(component_class, "component_version", None)
- config_version = component.component_version or component.version or 1
-
- return ValidationError(
- field="component_version",
- error=f"Component version mismatch: Your configuration uses version {config_version}, but the component requires version {current_version}",
- suggestion=f"Update your component configuration to use version {current_version}. Set 'component_version: {current_version}' in your configuration.",
- )
- except Exception:
- # Fallback to a more general version error message
- return ValidationError(
- field="component_version",
- error="Component version compatibility issue detected",
- suggestion="Your component configuration version is outdated. Update the 'component_version' field to match the latest component requirements.",
- )
-
- # Check for other common instantiation issues
- elif "Could not import provider" in error_str or "ImportError" in error_str:
- return ValidationError(
- field="provider",
- error=f"Provider import failed: {error_str}",
- suggestion="Ensure the provider module is installed and the import path is correct",
- )
- elif "component_config_schema" in error_str:
- return ValidationError(
- field="config",
- error="Component configuration schema validation failed",
- suggestion="Check that your configuration matches the component's expected schema",
- )
- else:
- return ValidationError(
- field="instantiation",
- error=f"Failed to instantiate component: {error_str}",
- suggestion="Check that the component can be properly instantiated with the given config",
- )
-
- @classmethod
- def validate(cls, component: ComponentModel) -> ValidationResponse:
- """Validate a component configuration"""
- errors: List[ValidationError] = []
- warnings: List[ValidationError] = []
-
- # Check provider
- if provider_error := cls.validate_provider(component.provider):
- errors.append(provider_error)
-
- # Check component type
- if type_error := cls.validate_component_type(component):
- errors.append(type_error)
-
- # Validate schema
- schema_errors = cls.validate_config_schema(component)
- errors.extend(schema_errors)
-
- # Only attempt instantiation if no errors so far
- if not errors:
- if inst_error := cls.validate_instantiation(component):
- errors.append(inst_error)
-
- # Check for version warnings
- if not component.version:
- warnings.append(
- ValidationError(
- field="version",
- error="Component version not specified",
- suggestion="Consider adding a version to ensure compatibility",
- )
- )
-
- return ValidationResponse(is_valid=len(errors) == 0, errors=errors, warnings=warnings)
|