Ctrl K
ComunidadGuías…

test/tools/test_agent_tools.py

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
# Copyright (c) 2026, AG2ai, Inc., AG2ai open-source projects maintainers and core contributors
#
# SPDX-License-Identifier: Apache-2.0

import asyncio
from dataclasses import asdict
from unittest.mock import MagicMock

import pytest
from dirty_equals import IsPartialDict
from pydantic import BaseModel

from ag2 import Agent, ToolResult, tool
from ag2.events import ModelResponse, ToolCallEvent, ToolCallsEvent
from ag2.testing import TestConfig

DEFAULT_SCHEMA = {
    "function": {
        "description": "Tool description.",
        "name": "my_tool",
        "parameters": {
            "properties": {
                "a": {
                    "title": "A",
                    "type": "string",
                },
                "b": {
                    "title": "B",
                    "type": "integer",
                },
            },
            "required": [
                "a",
                "b",
            ],
            "type": "object",
        },
    },
    "type": "function",
    "defer_loading": False,
}


def test_agent_with_function(mock: MagicMock) -> None:
    def my_tool(a: str, b: int) -> str:
        """Tool description."""
        return ""

    agent = Agent("", config=mock, tools=[my_tool])

    assert asdict(list(agent.tools)[0].schema) == DEFAULT_SCHEMA


def test_agent_with_tool(mock: MagicMock) -> None:
    @tool
    def my_tool(a: str, b: int) -> str:
        """Tool description."""
        return ""

    agent = Agent("", config=mock, tools=[my_tool])

    assert asdict(list(agent.tools)[0].schema) == DEFAULT_SCHEMA


def test_agent_with_tool_decorator(mock: MagicMock) -> None:
    agent = Agent("", config=mock)

    @agent.tool
    def my_tool(a: str, b: int) -> str:
        """Tool description."""
        return ""

    assert asdict(list(agent.tools)[0].schema) == DEFAULT_SCHEMA


def test_agent_with_tool_decorator_options_override(mock: MagicMock) -> None:
    agent = Agent("", config=mock)

    @agent.tool(name="another_name", description="another_description")
    def my_tool(a: str, b: int) -> str:
        """Tool description."""
        return ""

    assert asdict(list(agent.tools)[0].schema) == {
        "function": IsPartialDict({
            "description": "another_description",
            "name": "another_name",
        }),
        "type": "function",
        "defer_loading": False,
    }


@pytest.mark.asyncio()
async def test_final_tool() -> None:
    class DataModel(BaseModel):
        data: str

    def my_tool() -> ToolResult:
        return ToolResult({"data": "result"}, final=True)

    agent = Agent(
        "",
        tools=[my_tool],
        config=TestConfig(ToolCallEvent(name="my_tool")),
    )

    result = await agent.ask("Hi!")
    assert DataModel.model_validate_json(result.body) == DataModel(data="result")


@pytest.mark.asyncio()
async def test_concurrent_tool_execution() -> None:
    """Test that multiple tools are executed concurrently, not sequentially.

    Each tool blocks on a shared barrier that only releases once all three
    have started. Concurrent execution releases it immediately; sequential
    execution leaves the earlier tools waiting, so their bounded wait expires
    and they never record completion — failing the ``finished`` assertion
    fast instead of hanging.
    """
    started = 0
    all_started = asyncio.Event()
    finished: list[str] = []

    async def _run(name: str) -> str:
        nonlocal started
        started += 1
        if started == 3:
            all_started.set()
        try:
            await asyncio.wait_for(all_started.wait(), timeout=2.0)
        except TimeoutError:
            return "not-concurrent"
        finished.append(name)
        return f"result_{name}"

    async def slow_tool_a() -> str:
        return await _run("a")

    async def slow_tool_b() -> str:
        return await _run("b")

    async def slow_tool_c() -> str:
        return await _run("c")

    agent = Agent(
        "test_agent",
        tools=[slow_tool_a, slow_tool_b, slow_tool_c],
        config=TestConfig(
            ModelResponse(
                tool_calls=ToolCallsEvent(
                    calls=[
                        ToolCallEvent(name="slow_tool_a"),
                        ToolCallEvent(name="slow_tool_b"),
                        ToolCallEvent(name="slow_tool_c"),
                    ]
                )
            ),
            "result",
        ),
    )

    result = await agent.ask("Execute all tools")
    assert result.body == "result"
    assert started == 3
    assert sorted(finished) == ["a", "b", "c"]