Files
microsoft--agent-lightning/agentlightning/trainer/init_utils.py
T
wehub-resource-sync 85742ab165
Deploy Documentation / deploy (push) Has been cancelled
CPU Test / Test (Utilities, legacy, Python 3.10) (push) Has been cancelled
CPU Test / Test (LLM proxy, stable, Python 3.11) (push) Has been cancelled
CPU Test / Test (Others, stable, Python 3.11) (push) Has been cancelled
CPU Test / Test (Store, stable, Python 3.11) (push) Has been cancelled
CPU Test / Test (Utilities, stable, Python 3.11) (push) Has been cancelled
CPU Test / Test (Weave, stable, Python 3.11) (push) Has been cancelled
CPU Test / Test (AgentOps, stable, Python 3.12) (push) Has been cancelled
CPU Test / Test (LLM proxy, stable, Python 3.12) (push) Has been cancelled
CPU Test / Test (Others, stable, Python 3.12) (push) Has been cancelled
CPU Test / Test (Weave, latest, Python 3.13) (push) Has been cancelled
Dashboard / Chromatic (push) Has been cancelled
CPU Test / Lint - fast (push) Has been cancelled
CPU Test / Lint - next (push) Has been cancelled
CPU Test / Lint - slow (push) Has been cancelled
CPU Test / Lint - JavaScript (push) Has been cancelled
CPU Test / Build documentation (push) Has been cancelled
CPU Test / Test (AgentOps, legacy, Python 3.10) (push) Has been cancelled
CPU Test / Test (LLM proxy, legacy, Python 3.10) (push) Has been cancelled
CPU Test / Test (Others, legacy, Python 3.10) (push) Has been cancelled
CPU Test / Test (Store, legacy, Python 3.10) (push) Has been cancelled
CPU Test / Test (Weave, legacy, Python 3.10) (push) Has been cancelled
CPU Test / Test (AgentOps, stable, Python 3.11) (push) Has been cancelled
CPU Test / Test (Store, stable, Python 3.12) (push) Has been cancelled
CPU Test / Test (Utilities, stable, Python 3.12) (push) Has been cancelled
CPU Test / Test (Weave, stable, Python 3.12) (push) Has been cancelled
CPU Test / Test (AgentOps, latest, Python 3.13) (push) Has been cancelled
CPU Test / Test (LLM proxy, latest, Python 3.13) (push) Has been cancelled
CPU Test / Test (Others, latest, Python 3.13) (push) Has been cancelled
CPU Test / Test (Store, latest, Python 3.13) (push) Has been cancelled
CPU Test / Test (Utilities, latest, Python 3.13) (push) Has been cancelled
CPU Test / Test (JavaScript) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:44:17 +08:00

264 lines
10 KiB
Python

# Copyright (c) Microsoft. All rights reserved.
"""Utility helpers for dynamic component initialization within the trainer."""
from __future__ import annotations
import importlib
import inspect
from typing import Any, Callable, Dict, Optional, TypeVar, Union, cast, overload
OptionalDefaults = Dict[str, Callable[[], Any] | Any]
T = TypeVar("T")
def load_class(path: str) -> type[Any]:
"""Load a class from its fully qualified import path."""
module_name, class_name = path.rsplit(".", 1)
module = importlib.import_module(module_name)
return getattr(module, class_name)
def instantiate_component(
cls: type[Any],
provided_kwargs: Optional[Dict[str, Any]] = None,
optional_defaults: Optional[OptionalDefaults] = None,
) -> Any:
"""Instantiate `cls`, filling optional kwargs when the constructor accepts them."""
kwargs = dict(provided_kwargs or {})
if optional_defaults:
signature = inspect.signature(cls.__init__)
for name, value in optional_defaults.items():
if name in kwargs or name not in signature.parameters:
continue
kwargs[name] = value() if callable(value) else value
return cls(**kwargs)
def instantiate_from_spec(
spec: Union[str, Dict[str, Any]],
*,
spec_name: str,
optional_defaults: Optional[OptionalDefaults] = None,
dict_requires_type: bool = True,
dict_default_cls: type[Any] | None = None,
registry: Optional[Dict[str, str]] = None,
) -> Any:
"""Instantiate a component from a string or dict spec."""
if isinstance(spec, str):
type_path = registry.get(spec, spec) if registry else spec
cls = load_class(type_path)
return instantiate_component(cls, optional_defaults=optional_defaults)
if isinstance(spec, dict): # pyright: ignore[reportUnnecessaryIsInstance]
spec_conf = dict(spec)
type_path = spec_conf.pop("type", None)
if type_path is None and registry and "name" in spec_conf:
type_path = registry.get(spec_conf.pop("name"))
elif registry and type_path is not None:
type_path = registry.get(type_path, type_path)
if type_path is None:
if dict_requires_type:
raise ValueError(f"{spec_name} dict must have a 'type' key with the class full name")
if dict_default_cls is None:
raise ValueError(f"{spec_name} dict missing 'type' and no default class provided")
cls = dict_default_cls
else:
cls = load_class(type_path)
return instantiate_component(cls, spec_conf, optional_defaults)
raise TypeError(f"{spec_name} spec must be a string or dict (got {type(spec)}).")
def _ensure_expected_type(
instance: Any,
expected_type: type[T],
spec_name: str,
type_error_fmt: str | None,
) -> T:
if not isinstance(instance, expected_type):
type_name = str(type(instance)) # type: ignore
if type_error_fmt:
raise TypeError(type_error_fmt.format(type_name=type_name, expected_type=expected_type.__name__))
raise TypeError(f"{spec_name} factory returned {type_name}, which is not a {expected_type.__name__} subclass.")
return instance
@overload
def build_component(
spec: Union[T, str, Dict[str, Any], type[T], Callable[[], T], None],
*,
expected_type: type[T],
spec_name: str,
default_factory: Callable[[], T],
allow_none: bool = ...,
optional_defaults: Optional[OptionalDefaults] = ...,
dict_requires_type: bool = ...,
dict_default_cls: type[T] | None = ...,
type_error_fmt: str | None = ...,
invalid_spec_error_fmt: str | None = ...,
registry: Optional[Dict[str, str]] = ...,
) -> T: ...
@overload
def build_component(
spec: Union[T, str, Dict[str, Any], type[T], Callable[[], T], None],
*,
expected_type: type[T],
spec_name: str,
default_factory: None = ...,
allow_none: bool,
optional_defaults: Optional[OptionalDefaults] = ...,
dict_requires_type: bool = ...,
dict_default_cls: type[T] | None = ...,
type_error_fmt: str | None = ...,
invalid_spec_error_fmt: str | None = ...,
registry: Optional[Dict[str, str]] = ...,
) -> T | None: ...
@overload
def build_component(
spec: Union[T, str, Dict[str, Any], type[T], Callable[[], T], None],
*,
expected_type: type[T],
spec_name: str,
default_factory: None = ...,
allow_none: bool = ...,
optional_defaults: Optional[OptionalDefaults] = ...,
dict_requires_type: bool = ...,
dict_default_cls: type[T] | None = ...,
type_error_fmt: str | None = ...,
invalid_spec_error_fmt: str | None = ...,
registry: Optional[Dict[str, str]] = ...,
) -> T | None: ...
def build_component(
spec: Union[T, str, Dict[str, Any], type[T], Callable[[], T], None],
*,
expected_type: type[T],
spec_name: str,
default_factory: Callable[[], T] | None = None,
allow_none: bool = False,
optional_defaults: Optional[OptionalDefaults] = None,
dict_requires_type: bool = True,
dict_default_cls: type[T] | None = None,
type_error_fmt: str | None = None,
invalid_spec_error_fmt: str | None = None,
registry: Optional[Dict[str, str]] = None,
) -> T | None:
"""Build and return a component instance from a flexible specification.
This function provides a flexible way to create component instances from various
input formats including direct instances, class types, factory functions, import
paths, or configuration dictionaries.
Args:
spec: The component specification. Can be:
- An instance of expected_type (returned as-is)
- A string import path (e.g., 'module.Class') or registry key
- A dict with 'type' key (import path or registry key) and constructor kwargs
- A class type (will be instantiated)
- A factory function (will be called)
- None (uses default_factory or returns None if allow_none=True)
expected_type: The type that the resulting instance must be or inherit from.
spec_name: Descriptive name for the spec, used in error messages.
default_factory: Optional factory function called when spec is None.
allow_none: If True, allows None to be returned when spec is None and
no default_factory is provided.
optional_defaults: Dict mapping parameter names to default values or factory
functions that will be injected if the constructor accepts them.
dict_requires_type: If True, dict specs must include a 'type' key.
dict_default_cls: Default class to use for dict specs without a 'type' key
(only used when dict_requires_type=False).
type_error_fmt: Custom format string for type validation errors. Should include
{type_name} and {expected_type} placeholders.
invalid_spec_error_fmt: Custom format string for invalid spec type errors.
Should include {actual_type} and {expected_type} placeholders.
registry: Optional mapping of short names to fully qualified import paths.
When provided, string specs or dict 'type'/'name' entries are first
resolved through this registry before attempting to import.
Returns:
An instance of expected_type, or None if allow_none=True and spec is None
without a default_factory.
Raises:
TypeError: If the instantiated object is not an instance of expected_type.
ValueError: If spec is None and neither default_factory nor allow_none is set,
or if spec type is invalid, or if dict spec is invalid.
Examples:
>>> # Direct instance
>>> optimizer = build_component(AdamW(), expected_type=Optimizer, spec_name='optimizer')
>>>
>>> # String import path
>>> optimizer = build_component('torch.optim.AdamW', expected_type=Optimizer, spec_name='optimizer')
>>>
>>> # Dict with type and kwargs
>>> spec = {'type': 'torch.optim.AdamW', 'lr': 0.001}
>>> optimizer = build_component(spec, expected_type=Optimizer, spec_name='optimizer')
>>>
>>> # Class type
>>> optimizer = build_component(AdamW, expected_type=Optimizer, spec_name='optimizer')
>>>
>>> # Factory function
>>> optimizer = build_component(lambda: AdamW(lr=0.001), expected_type=Optimizer,
... spec_name='optimizer')
"""
if isinstance(spec, expected_type):
return cast(T, spec)
if spec is None:
if default_factory is not None:
instance = default_factory()
return _ensure_expected_type(instance, expected_type, spec_name, type_error_fmt)
if allow_none:
return None
raise ValueError(
invalid_spec_error_fmt.format(actual_type=type(spec), expected_type=expected_type.__name__)
if invalid_spec_error_fmt
else f"{spec_name} cannot be None."
)
if isinstance(spec, type) and issubclass(spec, expected_type):
instance = instantiate_component(spec, optional_defaults=optional_defaults)
return _ensure_expected_type(instance, expected_type, spec_name, type_error_fmt)
if callable(spec) and not isinstance(spec, type): # type: ignore
instance = spec()
return _ensure_expected_type(instance, expected_type, spec_name, type_error_fmt)
if isinstance(spec, str):
instance = instantiate_from_spec(
spec,
spec_name=spec_name,
optional_defaults=optional_defaults,
dict_requires_type=dict_requires_type,
dict_default_cls=dict_default_cls,
registry=registry,
)
return _ensure_expected_type(instance, expected_type, spec_name, type_error_fmt)
if isinstance(spec, dict):
instance = instantiate_from_spec(
spec, # type: ignore
spec_name=spec_name,
optional_defaults=optional_defaults,
dict_requires_type=dict_requires_type,
dict_default_cls=dict_default_cls,
registry=registry,
)
return _ensure_expected_type(instance, expected_type, spec_name, type_error_fmt)
if invalid_spec_error_fmt:
raise ValueError(invalid_spec_error_fmt.format(actual_type=type(spec), expected_type=expected_type.__name__)) # type: ignore
type_name = str(type(spec)) # type: ignore
raise ValueError(f"Invalid {spec_name} type: {type_name}. Expected {expected_type.__name__}, str, dict, or None.")
__all__ = ["OptionalDefaults", "build_component", "instantiate_component", "instantiate_from_spec", "load_class"]