-
Notifications
You must be signed in to change notification settings - Fork 5.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
4 changed files
with
317 additions
and
15 deletions.
There are no files selected for viewing
4 changes: 2 additions & 2 deletions
4
python/packages/autogen-ext/src/autogen_ext/tools/http/__init__.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,3 @@ | ||
from ._http_tool import HttpTool, HttpToolConfig | ||
from ._http_tool import HttpTool | ||
|
||
__all__ = ["HttpTool", "HttpToolConfig"] | ||
__all__ = ["HttpTool"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
import asyncio | ||
from typing import AsyncGenerator | ||
|
||
import pytest | ||
import pytest_asyncio | ||
import uvicorn | ||
from autogen_core import CancellationToken, ComponentModel | ||
from autogen_ext.tools.http import HttpTool | ||
from fastapi import Body, FastAPI | ||
from pydantic import BaseModel, Field | ||
|
||
|
||
class TestArgs(BaseModel): | ||
query: str = Field(description="The test query") | ||
value: int = Field(description="A test value") | ||
|
||
|
||
class TestResponse(BaseModel): | ||
result: str = Field(description="The test result") | ||
|
||
|
||
# Create a test FastAPI app | ||
app = FastAPI() | ||
|
||
|
||
@app.post("/test") | ||
async def test_endpoint(body: TestArgs = Body(...)) -> TestResponse: | ||
return TestResponse(result=f"Received: {body.query} with value {body.value}") | ||
|
||
|
||
@app.get("/test") | ||
async def test_get_endpoint(query: str, value: int) -> TestResponse: | ||
return TestResponse(result=f"Received: {query} with value {value}") | ||
|
||
|
||
@app.put("/test") | ||
async def test_put_endpoint(body: TestArgs = Body(...)) -> TestResponse: | ||
return TestResponse(result=f"Received: {body.query} with value {body.value}") | ||
|
||
|
||
@app.delete("/test") | ||
async def test_delete_endpoint(query: str, value: int) -> TestResponse: | ||
return TestResponse(result=f"Received: {query} with value {value}") | ||
|
||
|
||
@app.patch("/test") | ||
async def test_patch_endpoint(body: TestArgs = Body(...)) -> TestResponse: | ||
return TestResponse(result=f"Received: {body.query} with value {body.value}") | ||
|
||
|
||
@pytest.fixture | ||
def test_config() -> ComponentModel: | ||
return ComponentModel( | ||
provider="autogen_ext.tools.http.HttpTool", | ||
config={ | ||
"name": "TestHttpTool", | ||
"description": "A test HTTP tool", | ||
"url": "http://localhost:8000/test", | ||
"method": "POST", | ||
"headers": {"Content-Type": "application/json"}, | ||
"json_schema": { | ||
"type": "object", | ||
"properties": { | ||
"query": {"type": "string", "description": "The test query"}, | ||
"value": {"type": "integer", "description": "A test value"}, | ||
}, | ||
"required": ["query", "value"], | ||
}, | ||
}, | ||
) | ||
|
||
|
||
@pytest_asyncio.fixture | ||
async def test_server() -> AsyncGenerator[None, None]: | ||
# Start the test server | ||
config = uvicorn.Config(app, host="127.0.0.1", port=8000, log_level="error") | ||
server = uvicorn.Server(config) | ||
|
||
# Create a task for the server | ||
server_task = asyncio.create_task(server.serve()) | ||
|
||
# Wait a bit for server to start | ||
await asyncio.sleep(0.5) # Increased sleep time to ensure server is ready | ||
|
||
yield | ||
|
||
# Cleanup | ||
server.should_exit = True | ||
await server_task |
144 changes: 144 additions & 0 deletions
144
python/packages/autogen-ext/tests/tools/http/test_http_tool.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,144 @@ | ||
import pytest | ||
import httpx | ||
from pydantic import ValidationError | ||
from autogen_core import CancellationToken | ||
from autogen_ext.tools.http import HttpTool | ||
from autogen_core import Component, ComponentModel | ||
|
||
|
||
def test_tool_schema_generation(test_config: ComponentModel) -> None: | ||
tool = HttpTool.load_component(test_config) | ||
schema = tool.schema | ||
|
||
assert schema["name"] == "TestHttpTool" | ||
assert "description" in schema | ||
assert schema["description"] == "A test HTTP tool" | ||
assert "parameters" in schema | ||
assert schema["parameters"]["type"] == "object" | ||
assert "properties" in schema["parameters"] | ||
assert schema["parameters"]["properties"]["query"]["description"] == "The test query" | ||
assert schema["parameters"]["properties"]["query"]["type"] == "string" | ||
assert schema["parameters"]["properties"]["value"]["description"] == "A test value" | ||
assert schema["parameters"]["properties"]["value"]["type"] == "integer" | ||
assert "required" in schema["parameters"] | ||
assert set(schema["parameters"]["required"]) == {"query", "value"} | ||
|
||
|
||
def test_tool_properties(test_config: ComponentModel) -> None: | ||
tool = HttpTool.load_component(test_config) | ||
|
||
assert tool.name == "TestHttpTool" | ||
assert tool.description == "A test HTTP tool" | ||
assert tool.server_params.url == "http://localhost:8000/test" | ||
assert tool.server_params.method == "POST" | ||
|
||
|
||
def test_component_base_class(test_config: ComponentModel) -> None: | ||
tool = HttpTool.load_component(test_config) | ||
assert tool.dump_component() is not None | ||
assert HttpTool.load_component(tool.dump_component(), HttpTool) is not None | ||
assert isinstance(tool, Component) | ||
|
||
|
||
@pytest.mark.asyncio | ||
async def test_post_request(test_config: ComponentModel, test_server: None) -> None: | ||
tool = HttpTool.load_component(test_config) | ||
result = await tool.run_json({"query": "test query", "value": 42}, CancellationToken()) | ||
|
||
assert isinstance(result, dict) | ||
assert result["result"] == "Received: test query with value 42" | ||
|
||
|
||
@pytest.mark.asyncio | ||
async def test_get_request(test_config: ComponentModel, test_server: None) -> None: | ||
# Modify config for GET request | ||
config = test_config.model_copy() | ||
config.config["method"] = "GET" | ||
tool = HttpTool.load_component(config) | ||
|
||
result = await tool.run_json({"query": "test query", "value": 42}, CancellationToken()) | ||
|
||
assert isinstance(result, dict) | ||
assert result["result"] == "Received: test query with value 42" | ||
|
||
|
||
@pytest.mark.asyncio | ||
async def test_put_request(test_config: ComponentModel, test_server: None) -> None: | ||
# Modify config for PUT request | ||
config = test_config.model_copy() | ||
config.config["method"] = "PUT" | ||
tool = HttpTool.load_component(config) | ||
|
||
result = await tool.run_json({"query": "test query", "value": 42}, CancellationToken()) | ||
|
||
assert isinstance(result, dict) | ||
assert result["result"] == "Received: test query with value 42" | ||
|
||
|
||
@pytest.mark.asyncio | ||
async def test_delete_request(test_config: ComponentModel, test_server: None) -> None: | ||
# Modify config for DELETE request | ||
config = test_config.model_copy() | ||
config.config["method"] = "DELETE" | ||
tool = HttpTool.load_component(config) | ||
|
||
result = await tool.run_json({"query": "test query", "value": 42}, CancellationToken()) | ||
|
||
assert isinstance(result, dict) | ||
assert result["result"] == "Received: test query with value 42" | ||
|
||
|
||
@pytest.mark.asyncio | ||
async def test_patch_request(test_config: ComponentModel, test_server: None) -> None: | ||
# Modify config for PATCH request | ||
config = test_config.model_copy() | ||
config.config["method"] = "PATCH" | ||
tool = HttpTool.load_component(config) | ||
|
||
result = await tool.run_json({"query": "test query", "value": 42}, CancellationToken()) | ||
|
||
assert isinstance(result, dict) | ||
assert result["result"] == "Received: test query with value 42" | ||
|
||
|
||
@pytest.mark.asyncio | ||
async def test_invalid_schema(test_config: ComponentModel, test_server: None) -> None: | ||
# Create an invalid schema missing required properties | ||
config: ComponentModel = test_config.model_copy() | ||
config.config["url"] = True # Incorrect type | ||
|
||
with pytest.raises(ValidationError): | ||
# Should fail when trying to create model from invalid schema | ||
HttpTool.load_component(config) | ||
|
||
|
||
@pytest.mark.asyncio | ||
async def test_invalid_request(test_config: ComponentModel, test_server: None) -> None: | ||
# Use an invalid URL | ||
config = test_config.model_copy() | ||
config.config["url"] = "http://fake:8000/nonexistent" | ||
tool = HttpTool.load_component(config) | ||
|
||
with pytest.raises(httpx.ConnectError): | ||
await tool.run_json({"query": "test query", "value": 42}, CancellationToken()) | ||
|
||
|
||
def test_config_serialization(test_config: ComponentModel) -> None: | ||
tool = HttpTool.load_component(test_config) | ||
config = tool._to_config() | ||
|
||
assert config.name == test_config.config["name"] | ||
assert config.description == test_config.config["description"] | ||
assert config.url == test_config.config["url"] | ||
assert config.method == test_config.config["method"] | ||
assert config.headers == test_config.config["headers"] | ||
|
||
|
||
def test_config_deserialization(test_config: ComponentModel) -> None: | ||
tool = HttpTool.load_component(test_config) | ||
|
||
assert tool.name == test_config.config["name"] | ||
assert tool.description == test_config.config["description"] | ||
assert tool.server_params.url == test_config.config["url"] | ||
assert tool.server_params.method == test_config.config["method"] | ||
assert tool.server_params.headers == test_config.config["headers"] |