You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

validation_service.py 9.0 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. # validation/validation_service.py
  2. import importlib
  3. from typing import List, Optional
  4. from autogen_core import ComponentModel, is_component_class
  5. from pydantic import BaseModel
  6. class ValidationRequest(BaseModel):
  7. component: ComponentModel
  8. class ValidationError(BaseModel):
  9. field: str
  10. error: str
  11. suggestion: Optional[str] = None
  12. class ValidationResponse(BaseModel):
  13. is_valid: bool
  14. errors: List[ValidationError] = []
  15. warnings: List[ValidationError] = []
  16. class ValidationService:
  17. @staticmethod
  18. def validate_provider(provider: str) -> Optional[ValidationError]:
  19. """Validate that the provider exists and can be imported"""
  20. try:
  21. if provider in ["azure_openai_chat_completion_client", "AzureOpenAIChatCompletionClient"]:
  22. provider = "autogen_ext.models.openai.AzureOpenAIChatCompletionClient"
  23. elif provider in ["openai_chat_completion_client", "OpenAIChatCompletionClient"]:
  24. provider = "autogen_ext.models.openai.OpenAIChatCompletionClient"
  25. module_path, class_name = provider.rsplit(".", maxsplit=1)
  26. module = importlib.import_module(module_path)
  27. component_class = getattr(module, class_name)
  28. if not is_component_class(component_class):
  29. return ValidationError(
  30. field="provider",
  31. error=f"Class {provider} is not a valid component class",
  32. suggestion="Ensure the class inherits from Component and implements required methods",
  33. )
  34. return None
  35. except ImportError:
  36. return ValidationError(
  37. field="provider",
  38. error=f"Could not import provider {provider}",
  39. suggestion="Check that the provider module is installed and the path is correct",
  40. )
  41. except Exception as e:
  42. return ValidationError(
  43. field="provider",
  44. error=f"Error validating provider: {str(e)}",
  45. suggestion="Check the provider string format and class implementation",
  46. )
  47. @staticmethod
  48. def validate_component_type(component: ComponentModel) -> Optional[ValidationError]:
  49. """Validate the component type"""
  50. if not component.component_type:
  51. return ValidationError(
  52. field="component_type",
  53. error="Component type is missing",
  54. suggestion="Add a component_type field to the component configuration",
  55. )
  56. @staticmethod
  57. def validate_config_schema(component: ComponentModel) -> List[ValidationError]:
  58. """Validate the component configuration against its schema"""
  59. errors: List[ValidationError] = []
  60. try:
  61. # Convert to ComponentModel for initial validation
  62. model = component.model_copy(deep=True)
  63. # Get the component class
  64. provider = model.provider
  65. module_path, class_name = provider.rsplit(".", maxsplit=1)
  66. module = importlib.import_module(module_path)
  67. component_class = getattr(module, class_name)
  68. # Validate against component's schema
  69. if hasattr(component_class, "component_config_schema"):
  70. try:
  71. component_class.component_config_schema.model_validate(model.config)
  72. except Exception as e:
  73. errors.append(
  74. ValidationError(
  75. field="config",
  76. error=f"Config validation failed: {str(e)}",
  77. suggestion="Check that the config matches the component's schema",
  78. )
  79. )
  80. else:
  81. errors.append(
  82. ValidationError(
  83. field="config",
  84. error="Component class missing config schema",
  85. suggestion="Implement component_config_schema in the component class",
  86. )
  87. )
  88. except Exception as e:
  89. errors.append(
  90. ValidationError(
  91. field="config",
  92. error=f"Schema validation error: {str(e)}",
  93. suggestion="Check the component configuration format",
  94. )
  95. )
  96. return errors
  97. @staticmethod
  98. def validate_instantiation(component: ComponentModel) -> Optional[ValidationError]:
  99. """Validate that the component can be instantiated"""
  100. try:
  101. model = component.model_copy(deep=True)
  102. # Attempt to load the component
  103. module_path, class_name = model.provider.rsplit(".", maxsplit=1)
  104. module = importlib.import_module(module_path)
  105. component_class = getattr(module, class_name)
  106. component_class.load_component(model)
  107. return None
  108. except Exception as e:
  109. error_str = str(e)
  110. # Check for version compatibility issues
  111. if "component_version" in error_str and "_from_config_past_version is not implemented" in error_str:
  112. # Extract component information for a better error message
  113. try:
  114. # Get the current component version
  115. module_path, class_name = component.provider.rsplit(".", maxsplit=1)
  116. module = importlib.import_module(module_path)
  117. component_class = getattr(module, class_name)
  118. current_version = getattr(component_class, "component_version", None)
  119. config_version = component.component_version or component.version or 1
  120. return ValidationError(
  121. field="component_version",
  122. error=f"Component version mismatch: Your configuration uses version {config_version}, but the component requires version {current_version}",
  123. suggestion=f"Update your component configuration to use version {current_version}. Set 'component_version: {current_version}' in your configuration.",
  124. )
  125. except Exception:
  126. # Fallback to a more general version error message
  127. return ValidationError(
  128. field="component_version",
  129. error="Component version compatibility issue detected",
  130. suggestion="Your component configuration version is outdated. Update the 'component_version' field to match the latest component requirements.",
  131. )
  132. # Check for other common instantiation issues
  133. elif "Could not import provider" in error_str or "ImportError" in error_str:
  134. return ValidationError(
  135. field="provider",
  136. error=f"Provider import failed: {error_str}",
  137. suggestion="Ensure the provider module is installed and the import path is correct",
  138. )
  139. elif "component_config_schema" in error_str:
  140. return ValidationError(
  141. field="config",
  142. error="Component configuration schema validation failed",
  143. suggestion="Check that your configuration matches the component's expected schema",
  144. )
  145. else:
  146. return ValidationError(
  147. field="instantiation",
  148. error=f"Failed to instantiate component: {error_str}",
  149. suggestion="Check that the component can be properly instantiated with the given config",
  150. )
  151. @classmethod
  152. def validate(cls, component: ComponentModel) -> ValidationResponse:
  153. """Validate a component configuration"""
  154. errors: List[ValidationError] = []
  155. warnings: List[ValidationError] = []
  156. # Check provider
  157. if provider_error := cls.validate_provider(component.provider):
  158. errors.append(provider_error)
  159. # Check component type
  160. if type_error := cls.validate_component_type(component):
  161. errors.append(type_error)
  162. # Validate schema
  163. schema_errors = cls.validate_config_schema(component)
  164. errors.extend(schema_errors)
  165. # Only attempt instantiation if no errors so far
  166. if not errors:
  167. if inst_error := cls.validate_instantiation(component):
  168. errors.append(inst_error)
  169. # Check for version warnings
  170. if not component.version:
  171. warnings.append(
  172. ValidationError(
  173. field="version",
  174. error="Component version not specified",
  175. suggestion="Consider adding a version to ensure compatibility",
  176. )
  177. )
  178. return ValidationResponse(is_valid=len(errors) == 0, errors=errors, warnings=warnings)