import json from copy import deepcopy import pytest from agents import FunctionTool from agents.exceptions import ModelBehaviorError from pydantic import BaseModel from agency_swarm.tools.base_tool import BaseTool from agency_swarm.tools.tool_factory import ToolFactory class TestFromOpenapiSchema: def test_converts_simple_openapi_schema(self): schema = { "openapi": "3.1.0", "servers": [{"url": "https://api.test.com"}], "paths": { "/tickets": { "post": { "operationId": "create_ticket", "description": "Create a support ticket", "parameters": [ { "name": "priority", "in": "query", "schema": {"type": "string"}, "required": False, } ], } } }, } tools = ToolFactory.from_openapi_schema(schema, strict=False) assert len(tools) == 1 tool = tools[0] assert isinstance(tool, FunctionTool) assert tool.name == "create_ticket" assert "support ticket" in tool.description.lower() @pytest.mark.asyncio async def test_validation_errors_raise_model_behavior_error(self): schema = { "openapi": "3.1.0", "servers": [{"url": "https://api.test.com"}], "paths": { "/tickets": { "post": { "operationId": "create_ticket", "requestBody": { "required": True, "content": { "application/json": { "schema": { "type": "object", "properties": {"message": {"type": "string"}}, "required": ["message"], } } }, }, } } }, } tools = ToolFactory.from_openapi_schema(schema, strict=False) tool = tools[0] with pytest.raises(ModelBehaviorError, match="Invalid JSON input in request body"): await tool.on_invoke_tool(None, json.dumps({"requestBody": {}})) class TestGetOpenapiSchema: def test_generates_schema_for_base_tools(self): class TestTool(BaseTool): input_field: str def run(self): return self.input_field result_json = ToolFactory.get_openapi_schema([TestTool], "https://api.test.com") result = json.loads(result_json) assert result["info"]["title"] == "Agent Tools" assert "/tool/TestTool" in result["paths"] post_schema = result["paths"]["/tool/TestTool"]["post"] assert post_schema["operationId"] == "TestTool" assert post_schema["requestBody"]["required"] is True assert post_schema["responses"]["200"]["description"] == "Tool executed successfully" assert ( post_schema["responses"]["422"]["content"]["application/json"]["schema"]["$ref"] == "#/components/schemas/HTTPValidationError" ) assert post_schema["security"] == [{"HTTPBearer": []}] assert set(result["components"]["schemas"].keys()) == {"HTTPValidationError", "TestTool", "ValidationError"} def test_generates_schema_for_function_tool(self): async def dummy_tool(ctx, input_json: str): return "ok" function_tool = FunctionTool( name="dummy_tool", description="Dummy tool", params_json_schema={"type": "object", "properties": {}}, on_invoke_tool=dummy_tool, ) result_json = ToolFactory.get_openapi_schema([function_tool], "https://api.test.com") result = json.loads(result_json) assert "/tool/dummy_tool" in result["paths"] post_schema = result["paths"]["/tool/dummy_tool"]["post"] assert post_schema["operationId"] == "dummy_tool" assert post_schema["security"] == [{"HTTPBearer": []}] assert "requestBody" not in post_schema assert "422" not in post_schema["responses"] def test_get_openapi_schema_preserves_function_tool_defs(self): class Address(BaseModel): street: str zip_code: int class AddressListTool(BaseTool): addresses: list[Address] def run(self): return ",".join(addr.street for addr in self.addresses) function_tool = ToolFactory.adapt_base_tool(AddressListTool) original_schema = deepcopy(function_tool.params_json_schema) ToolFactory.get_openapi_schema([function_tool], "https://api.test.com") assert function_tool.params_json_schema == original_schema def test_union_function_tool_omits_request_body(self): class Contact(BaseModel): identifier: str | int class ContainerTool(BaseTool): contact: Contact def run(self): return str(self.contact.identifier) function_tool = ToolFactory.adapt_base_tool(ContainerTool) result = json.loads(ToolFactory.get_openapi_schema([function_tool], "https://api.test.com")) post_schema = result["paths"]["/tool/ContainerTool"]["post"] assert "requestBody" not in post_schema assert "422" not in post_schema["responses"] assert result["components"]["schemas"] == {}