Muhammad Junaid 0b9a622b56
Fix: Auto-Convert Pydantic and Dataclass Arguments in AutoGen Tool Calls (#5737)
AutoGen was passing raw dictionaries to functions instead of
constructing Pydantic model or dataclass instances. If a tool function’s
parameter was a Pydantic BaseModel or a dataclass, the function would
receive a dict and likely throw an error or behave incorrectly (since it
expected an object of that type).

This PR addresses problem in AutoGen where tool functions expecting
structured inputs (Pydantic models or dataclasses) were receiving raw
dictionaries. It ensures that structured inputs are automatically
validated and instantiated before function calls. Complete details are
in Issue #5736

[Reproducible Example Code - Failing
Case](https://colab.research.google.com/drive/1hgoP-cGdSZ1-OqQLpwYmlmcExgftDqlO?usp=sharing)
 
<!-- Please give a short summary of the change and the problem this
solves. -->
## Changes Made:
- Inspect function signatures for Pydantic BaseModel and dataclass
annotations.
- Convert input dictionaries into properly instantiated objects using
BaseModel.model_validate() for Pydantic models or standard instantiation
for dataclasses.
  - Raise descriptive errors when validation or instantiation fails.
  - Unit tests have been added to cover all scenarios

Now structured inputs are automatically validated and instantiated
before function calls.

- **Updated Conversion Logic:**  
In the `run()` method, we now inspect the function’s signature and
convert input dictionaries to structured objects. For parameters
annotated with a Pydantic model, we use `model_validate()` to create an
instance; for those annotated with a dataclass, we instantiate the
object using the dataclass constructor. For example:

  ```python
  # Get the function signature.
  sig = inspect.signature(self._func)
  raw_kwargs = args.model_dump()
  kwargs = {}

  # Iterate over the parameters expected by the function.
  for name, param in sig.parameters.items():
      if name in raw_kwargs:
          expected_type = param.annotation
          value = raw_kwargs[name]
# If expected type is a subclass of BaseModel, perform conversion.
if inspect.isclass(expected_type) and issubclass(expected_type,
BaseModel):
              try:
                  kwargs[name] = expected_type.model_validate(value)
              except ValidationError as e:
                  raise ValueError(
f"Error validating parameter '{name}' for function
'{self._func.__name__}': {e}"
                  ) from e
          # If it's a dataclass, instantiate it.
          elif is_dataclass(expected_type):
              try:
cls = expected_type if isinstance(expected_type, type) else
type(expected_type)
                  kwargs[name] = cls(**value)
              except Exception as e:
                  raise ValueError(
f"Error instantiating dataclass parameter '{name}' for function
'{self._func.__name__}': {e}"
                  ) from e
          else:
              kwargs[name] = value
  ```

- **Error Handling Improvements:**  
Conversion steps are wrapped in try/except blocks to raise descriptive
errors when instantiation fails, aiding in debugging invalid inputs.

- **Testing:**  
Unit tests have been added to simulate tool calls (e.g., an `add` tool)
to ensure that with input like:
  ```json
  {"input": {"x": 2, "y": 3}}
  ```
The tool function receives an instance of the expected type and returns
the correct result.


## Related issue number
Closes #5736
 
## Checks
- [x] I've included any doc changes needed for
<https://microsoft.github.io/autogen/>. See
<https://github.com/microsoft/autogen/blob/main/CONTRIBUTING.md> to
build and test documentation locally.
- [x] I've added tests (if relevant) corresponding to the changes
introduced in this PR.
- [x] I've made sure all auto checks have passed.
2025-03-03 16:35:27 -08:00
..

AutoGen Python packages

0.4 Docs PyPi autogen-core PyPi autogen-agentchat PyPi autogen-ext

This directory works as a single uv workspace containing all project packages. See packages to discover all project packages.

Migrating from 0.2.x?

Please refer to the migration guide for how to migrate your code from 0.2.x to 0.4.x.

Development

TL;DR, run all checks with:

uv sync --all-extras
source .venv/bin/activate
poe check

Setup

uv is a package manager that assists in creating the necessary environment and installing packages to run AutoGen.

Note: To prevent incompatibilities between versions the same UV version as is running in CI should be used. Check the version in CI by looking the setup-uv action, here for example.

For example, to change your version to 0.5.18, run:

uv self update 0.5.18

Virtual Environment

During development, you may need to test changes made to any of the packages.
To do so, create a virtual environment where the AutoGen packages are installed based on the current state of the directory.
Run the following commands at the root level of the Python directory:

uv sync --all-extras
source .venv/bin/activate
  • uv sync --all-extras will create a .venv directory at the current level and install packages from the current directory along with any other dependencies. The all-extras flag adds optional dependencies.
  • source .venv/bin/activate activates the virtual environment.

Common Tasks

To create a pull request (PR), ensure the following checks are met. You can run each check individually:

  • Format: poe format
  • Lint: poe lint
  • Test: poe test
  • Mypy: poe mypy
  • Pyright: poe pyright
  • Build docs: poe --directory ./packages/autogen-core/ docs-build
  • Auto rebuild+serve docs: poe --directory ./packages/autogen-core/ docs-serve
  • Check samples in python/samples: poe samples-code-check Alternatively, you can run all the checks with:
  • poe check

Note

These need to be run in the virtual environment.

Syncing Dependencies

When you pull new changes, you may need to update the dependencies. To do so, first make sure you are in the virtual environment, and then in the python directory, run:

uv sync --all-extras

This will update the dependencies in the virtual environment.

Creating a New Package

To create a new package, similar to autogen-core or autogen-chat, use the following:

uv sync --python 3.12
source .venv/bin/activate
cookiecutter ./templates/new-package/