autogen_core._component_config 源代码

from __future__ import annotations

import importlib
import warnings
from typing import Any, ClassVar, Dict, Generic, Literal, Type, TypeGuard, cast, overload

from pydantic import BaseModel
from typing_extensions import Self, TypeVar

ComponentType = Literal["model", "agent", "tool", "termination", "token_provider", "workbench"] | str
ConfigT = TypeVar("ConfigT", bound=BaseModel)
FromConfigT = TypeVar("FromConfigT", bound=BaseModel, contravariant=True)
ToConfigT = TypeVar("ToConfigT", bound=BaseModel, covariant=True)

T = TypeVar("T", bound=BaseModel, covariant=True)


[文档] class ComponentModel(BaseModel): """组件的模型类。包含实例化组件所需的全部信息。""" provider: str """描述组件如何被实例化。""" component_type: ComponentType | None = None """组件的逻辑类型。如果缺失,组件将采用提供者的默认类型。""" version: int | None = None """组件规范的版本号。如果缺失,组件将假定使用加载它的库的当前版本。这显然存在风险,应仅用于用户编写的临时配置。所有其他配置都应指定版本号。""" component_version: int | None = None """组件的版本号。如果缺失,组件将假定使用提供者的默认版本。""" description: str | None = None """组件的描述信息。""" label: str | None = None """组件的可读标签。如果缺失,组件将使用提供者的类名作为默认值。""" config: dict[str, Any] """经过模式验证的配置字段会被传递给给定类的 :py:meth:`autogen_core.ComponentConfigImpl._from_config` 方法实现,用于创建组件类的新实例。"""
def _type_to_provider_str(t: type) -> str: return f"{t.__module__}.{t.__qualname__}" WELL_KNOWN_PROVIDERS = { "azure_openai_chat_completion_client": "autogen_ext.models.openai.AzureOpenAIChatCompletionClient", "AzureOpenAIChatCompletionClient": "autogen_ext.models.openai.AzureOpenAIChatCompletionClient", "openai_chat_completion_client": "autogen_ext.models.openai.OpenAIChatCompletionClient", "OpenAIChatCompletionClient": "autogen_ext.models.openai.OpenAIChatCompletionClient", }
[文档] class ComponentFromConfig(Generic[FromConfigT]):
[文档] @classmethod def _from_config(cls, config: FromConfigT) -> Self: """从配置对象创建组件的新实例。 Args: config (T): 配置对象。 Returns: Self: 组件的新实例。 :meta public: """ raise NotImplementedError("This component does not support dumping to config")
[文档] @classmethod def _from_config_past_version(cls, config: Dict[str, Any], version: int) -> Self: """从旧版配置对象创建组件的新实例。 仅当配置对象版本低于当前版本时调用此方法,因为此时模式未知。 Args: config (Dict[str, Any]): 配置对象。 version (int): 配置对象的版本。 Returns: Self: 组件的新实例。 :meta public: """ raise NotImplementedError("This component does not support loading from past versions")
[文档] class ComponentToConfig(Generic[ToConfigT]): """类必须实现的两种方法才能成为组件。 Args: Protocol (ConfigT): 继承自 :py:class:`pydantic.BaseModel` 的类型。 """ component_type: ClassVar[ComponentType] """组件的逻辑类型。""" component_version: ClassVar[int] = 1 """组件的版本号,如果引入了不兼容的schema变更则应更新此版本号。""" component_provider_override: ClassVar[str | None] = None """覆盖组件的provider字符串。这应该用于防止内部模块名称成为模块名称的一部分。""" component_description: ClassVar[str | None] = None """组件的描述。如果未提供,将使用类的文档字符串。""" component_label: ClassVar[str | None] = None """组件的人类可读标签。如果未提供,将使用组件类名。"""
[文档] def _to_config(self) -> ToConfigT: """导出当前组件实例的配置,该配置可用于创建具有相同配置的新组件实例。 Returns: T: 组件的配置。 :meta public: """ raise NotImplementedError("This component does not support dumping to config")
[文档] def dump_component(self) -> ComponentModel: """将组件转储为可重新加载的模型。 Raises: TypeError: 如果组件是本地类。 Returns: ComponentModel: 表示组件的模型。 """ if self.component_provider_override is not None: provider = self.component_provider_override else: provider = _type_to_provider_str(self.__class__) # Warn if internal module name is used, if "._" in provider: warnings.warn( "Internal module name used in provider string. This is not recommended and may cause issues in the future. Silence this warning by setting component_provider_override to this value.", stacklevel=2, ) if "<locals>" in provider: raise TypeError("Cannot dump component with local class") if not hasattr(self, "component_type"): raise AttributeError("component_type not defined") description = self.component_description if description is None and self.__class__.__doc__: # use docstring as description docstring = self.__class__.__doc__.strip() for marker in ["\n\nArgs:", "\n\nParameters:", "\n\nAttributes:", "\n\n"]: docstring = docstring.split(marker)[0] description = docstring.strip() obj_config = self._to_config().model_dump(exclude_none=True) model = ComponentModel( provider=provider, component_type=self.component_type, version=self.component_version, component_version=self.component_version, description=description, label=self.component_label or self.__class__.__name__, config=obj_config, ) return model
ExpectedType = TypeVar("ExpectedType")
[文档] class ComponentLoader: @overload @classmethod def load_component(cls, model: ComponentModel | Dict[str, Any], expected: None = None) -> Self: ... @overload @classmethod def load_component(cls, model: ComponentModel | Dict[str, Any], expected: Type[ExpectedType]) -> ExpectedType: ...
[文档] @classmethod def load_component( cls, model: ComponentModel | Dict[str, Any], expected: Type[ExpectedType] | None = None ) -> Self | ExpectedType: """从模型加载组件。该方法设计用于与 :py:meth:`autogen_core.ComponentConfig.dump_component` 的返回类型配合使用。 Example: .. code-block:: python from autogen_core import ComponentModel from autogen_core.models import ChatCompletionClient component: ComponentModel = ... # type: ignore model_client = ChatCompletionClient.load_component(component) Args: model (ComponentModel): 用于加载组件的模型。 Returns: Self: 加载后的组件。 Args: model (ComponentModel): _description_ expected (Type[ExpectedType] | None, optional): 仅在直接用于 ComponentLoader 时需要显式类型。默认为 None。 Raises: ValueError: 如果提供者字符串无效。 TypeError: 提供者不是 ComponentConfigImpl 的子类,或期望类型不匹配。 Returns: Self | ExpectedType: 加载后的组件。 """ # Use global and add further type checks if isinstance(model, dict): loaded_model = ComponentModel(**model) else: loaded_model = model # First, do a look up in well known providers if loaded_model.provider in WELL_KNOWN_PROVIDERS: loaded_model.provider = WELL_KNOWN_PROVIDERS[loaded_model.provider] output = loaded_model.provider.rsplit(".", maxsplit=1) if len(output) != 2: raise ValueError("Invalid") module_path, class_name = output module = importlib.import_module(module_path) component_class = module.__getattribute__(class_name) if not is_component_class(component_class): raise TypeError("Invalid component class") # We need to check the schema is valid if not hasattr(component_class, "component_config_schema"): raise AttributeError("component_config_schema not defined") if not hasattr(component_class, "component_type"): raise AttributeError("component_type not defined") loaded_config_version = loaded_model.component_version or component_class.component_version if loaded_config_version < component_class.component_version: try: instance = component_class._from_config_past_version(loaded_model.config, loaded_config_version) # type: ignore except NotImplementedError as e: raise NotImplementedError( f"Tried to load component {component_class} which is on version {component_class.component_version} with a config on version {loaded_config_version} but _from_config_past_version is not implemented" ) from e else: schema = component_class.component_config_schema # type: ignore validated_config = schema.model_validate(loaded_model.config) # We're allowed to use the private method here instance = component_class._from_config(validated_config) # type: ignore if expected is None and not isinstance(instance, cls): raise TypeError("Expected type does not match") elif expected is None: return cast(Self, instance) elif not isinstance(instance, expected): raise TypeError("Expected type does not match") else: return cast(ExpectedType, instance)
[文档] class ComponentSchemaType(Generic[ConfigT]): # Ideally would be ClassVar[Type[ConfigT]], but this is disallowed https://github.com/python/typing/discussions/1424 (despite being valid in this context) component_config_schema: Type[ConfigT] """表示组件配置的Pydantic模型类。""" required_class_vars = ["component_config_schema", "component_type"] def __init_subclass__(cls, **kwargs: Any): super().__init_subclass__(**kwargs) if cls.__name__ != "Component" and not cls.__name__ == "_ConcreteComponent": # TODO: validate provider is loadable for var in cls.required_class_vars: if not hasattr(cls, var): warnings.warn( f"Class variable '{var}' must be defined in {cls.__name__} to be a valid component", stacklevel=2, )
[文档] class ComponentBase(ComponentToConfig[ConfigT], ComponentLoader, Generic[ConfigT]): ...
[文档] class Component( ComponentFromConfig[ConfigT], ComponentSchemaType[ConfigT], Generic[ConfigT], ): """要创建组件类,具体类需继承自本类,接口需继承自ComponentBase。然后实现两个类变量: - :py:attr:`component_config_schema` - 表示组件配置的Pydantic模型类。这也是Component的类型参数。 - :py:attr:`component_type` - 组件的逻辑类型。 示例: .. code-block:: python from __future__ import annotations from pydantic import BaseModel from autogen_core import Component class Config(BaseModel): value: str class MyComponent(Component[Config]): component_type = "custom" component_config_schema = Config def __init__(self, value: str): self.value = value def _to_config(self) -> Config: return Config(value=self.value) @classmethod def _from_config(cls, config: Config) -> MyComponent: return cls(value=config.value) """ def __init_subclass__(cls, **kwargs: Any): super().__init_subclass__(**kwargs) if not is_component_class(cls): warnings.warn( f"Component class '{cls.__name__}' must subclass the following: ComponentFromConfig, ComponentToConfig, ComponentSchemaType, ComponentLoader, individually or with ComponentBase and Component. Look at the component config documentation or how OpenAIChatCompletionClient does it.", stacklevel=2, )
# Should never be used directly, only for type checking class _ConcreteComponent( ComponentFromConfig[ConfigT], ComponentSchemaType[ConfigT], ComponentToConfig[ConfigT], ComponentLoader, Generic[ConfigT], ): ...
[文档] def is_component_instance(cls: Any) -> TypeGuard[_ConcreteComponent[BaseModel]]: return ( isinstance(cls, ComponentFromConfig) and isinstance(cls, ComponentToConfig) and isinstance(cls, ComponentSchemaType) and isinstance(cls, ComponentLoader) )
[文档] def is_component_class(cls: type) -> TypeGuard[Type[_ConcreteComponent[BaseModel]]]: return ( issubclass(cls, ComponentFromConfig) and issubclass(cls, ComponentToConfig) and issubclass(cls, ComponentSchemaType) and issubclass(cls, ComponentLoader) )