From af2b5faeef866156036a2c41d2d2346df4e72436 Mon Sep 17 00:00:00 2001 From: Jack Gerrits Date: Mon, 30 Sep 2024 19:43:05 -0400 Subject: [PATCH] Update package versions --- .../packages/autogen-agentchat/pyproject.toml | 6 +- .../packages/autogen-core/docs/src/index.md | 16 +- .../autogen-core/docs/src/packages/index.md | 12 +- python/packages/autogen-core/pyproject.toml | 2 +- python/packages/autogen-ext/pyproject.toml | 4 +- .../autogen_ext/tools/langchain/__init__.py | 6 +- .../tools/langchain/_langchain_adapter.py | 150 +++++++------- .../packages/autogen-ext/tests/test_tools.py | 194 +++++++++--------- python/packages/team-one/pyproject.toml | 2 +- python/uv.lock | 8 +- 10 files changed, 196 insertions(+), 204 deletions(-) diff --git a/python/packages/autogen-agentchat/pyproject.toml b/python/packages/autogen-agentchat/pyproject.toml index 6b9346f8ae..3ec66b104e 100644 --- a/python/packages/autogen-agentchat/pyproject.toml +++ b/python/packages/autogen-agentchat/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "hatchling.build" [project] name = "autogen-agentchat" -version = "0.3.0dev0" +version = "0.4.0dev0" description = "AutoGen agent and group chat library" readme = "README.md" requires-python = ">=3.10" @@ -13,8 +13,8 @@ classifiers = [ "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", ] -dependencies = ["autogen-core", - +dependencies = [ + "autogen-core==0.4.0dev0", ] [tool.uv] diff --git a/python/packages/autogen-core/docs/src/index.md b/python/packages/autogen-core/docs/src/index.md index d94e596dc9..1d3c0552b7 100644 --- a/python/packages/autogen-core/docs/src/index.md +++ b/python/packages/autogen-core/docs/src/index.md @@ -46,11 +46,9 @@ Task driven, high level APIs for building multi-agent systems. Including group c Built with core.

-

-
+```sh +pip install autogen-agentchat==0.4.0dev0 +``` @@ -71,11 +69,9 @@ Built with core. Primitive building blocks for creating asynchronous, event driven multi-agent systems.

-

-
+```sh +pip install autogen-core==0.4.0dev0 +``` diff --git a/python/packages/autogen-core/docs/src/packages/index.md b/python/packages/autogen-core/docs/src/packages/index.md index 85613a35da..96c440f5c2 100644 --- a/python/packages/autogen-core/docs/src/packages/index.md +++ b/python/packages/autogen-core/docs/src/packages/index.md @@ -28,9 +28,7 @@ myst: Library that is at a similar level of abstraction as AutoGen 0.2, including default agents and group chat. ```sh -pip install git+https://github.com/microsoft/autogen.git#subdirectory=python/packages/autogen-core -pip install git+https://github.com/microsoft/autogen.git#subdirectory=python/packages/autogen-ext -pip install git+https://github.com/microsoft/autogen.git#subdirectory=python/packages/autogen-agentchat +pip install autogen-agentchat==0.4.0dev0 ``` ```{note} @@ -48,7 +46,7 @@ This package is a work in progress, it will be available on PyPI when it is read Implements the core functionality of the AutoGen framework, providing basic building blocks for creating multi-agent systems. ```sh -pip install git+https://github.com/microsoft/autogen.git#subdirectory=python/packages/autogen-core +pip install autogen-core==0.4.0dev0 ``` ```{note} @@ -65,8 +63,7 @@ This package is a work in progress, it will be available on PyPI when it is read Implementations of core components that interface with external services, or use extra dependencies. For example, Docker based code execution. ```sh -pip install git+https://github.com/microsoft/autogen.git#subdirectory=python/packages/autogen-core -pip install git+https://github.com/microsoft/autogen.git#subdirectory=python/packages/autogen-ext +pip install autogen-ext==0.4.0dev0 ``` ```{note} @@ -84,8 +81,7 @@ This package is a work in progress, it will be available on PyPI when it is read A generalist multi-agent softbot utilizing five agents to tackle intricate tasks involving multi-step planning and real-world actions. ```sh -pip install git+https://github.com/microsoft/autogen.git#subdirectory=python/packages/autogen-core -pip install git+https://github.com/microsoft/autogen.git#subdirectory=python/packages/autogen-team-one +pip install autogen-team-one==0.1.0dev0 ``` ```{note} diff --git a/python/packages/autogen-core/pyproject.toml b/python/packages/autogen-core/pyproject.toml index cb7f9a9e22..d3416b697a 100644 --- a/python/packages/autogen-core/pyproject.toml +++ b/python/packages/autogen-core/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "hatchling.build" [project] name = "autogen-core" -version = "0.3.dev0" +version = "0.4.0dev0" description = "Foundational interfaces and agent runtime implementation for AutoGen" readme = "README.md" requires-python = ">=3.10" diff --git a/python/packages/autogen-ext/pyproject.toml b/python/packages/autogen-ext/pyproject.toml index 78a8db209c..38e6a37b9e 100644 --- a/python/packages/autogen-ext/pyproject.toml +++ b/python/packages/autogen-ext/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "hatchling.build" [project] name = "autogen-ext" -version = "0.3.0dev0" +version = "0.4.0dev0" description = "AutoGen extensions library" readme = "README.md" requires-python = ">=3.10" @@ -14,7 +14,7 @@ classifiers = [ "Operating System :: OS Independent", ] dependencies = [ - "autogen-core", + "autogen-core==0.4.0dev0", ] diff --git a/python/packages/autogen-ext/src/autogen_ext/tools/langchain/__init__.py b/python/packages/autogen-ext/src/autogen_ext/tools/langchain/__init__.py index 039c41f3a6..03af9585bf 100644 --- a/python/packages/autogen-ext/src/autogen_ext/tools/langchain/__init__.py +++ b/python/packages/autogen-ext/src/autogen_ext/tools/langchain/__init__.py @@ -1,3 +1,3 @@ -from ._langchain_adapter import LangChainToolAdapter - -__all__ = ["LangChainToolAdapter"] +from ._langchain_adapter import LangChainToolAdapter + +__all__ = ["LangChainToolAdapter"] diff --git a/python/packages/autogen-ext/src/autogen_ext/tools/langchain/_langchain_adapter.py b/python/packages/autogen-ext/src/autogen_ext/tools/langchain/_langchain_adapter.py index 7ce90caf0c..1d7faf19cb 100644 --- a/python/packages/autogen-ext/src/autogen_ext/tools/langchain/_langchain_adapter.py +++ b/python/packages/autogen-ext/src/autogen_ext/tools/langchain/_langchain_adapter.py @@ -1,75 +1,75 @@ -import asyncio -import inspect -from typing import Any, Callable, Dict, Tuple, Type, cast - -from autogen_core.base import CancellationToken -from autogen_core.components.tools import BaseTool -from pydantic import BaseModel, Field, create_model -from pydantic.fields import FieldInfo - -from langchain.tools import Tool as LangChainTool - -FieldDefinition = Tuple[Type[Any], FieldInfo] -FieldsDict = Dict[str, FieldDefinition] - - -class LangChainToolAdapter(BaseTool[BaseModel, Any]): - langchain_tool: LangChainTool - _callable: Callable[..., Any] - - def __init__(self, langchain_tool: LangChainTool): - self.langchain_tool = langchain_tool - - # Extract name and description - name = langchain_tool.name - description = langchain_tool.description or "" - - # Determine the callable method - if hasattr(langchain_tool, "func") and callable(langchain_tool.func): - assert langchain_tool.func is not None - self._callable = langchain_tool.func - elif hasattr(langchain_tool, "_run") and callable(langchain_tool._run): # pyright: ignore - self._callable = langchain_tool._run # type: ignore - else: - raise AttributeError( - f"The provided LangChain tool '{name}' does not have a callable 'func' or '_run' method." - ) - - # Determine args_type - if langchain_tool.args_schema: # pyright: ignore - args_type = langchain_tool.args_schema # pyright: ignore - else: - # Infer args_type from the callable's signature - sig = inspect.signature(cast(Callable[..., Any], self._callable)) - fields = { - k: (v.annotation, Field(...)) - for k, v in sig.parameters.items() - if k != "self" and v.kind not in (inspect.Parameter.VAR_POSITIONAL, inspect.Parameter.VAR_KEYWORD) - } - args_type = create_model(f"{name}Args", **fields) # type: ignore - # Note: type ignore is used due to a LangChain typing limitation - - # Ensure args_type is a subclass of BaseModel - if not issubclass(args_type, BaseModel): - raise ValueError(f"Failed to create a valid Pydantic v2 model for {name}") - - # Assume return_type as Any if not specified - return_type: Type[Any] = object - - super().__init__(args_type, return_type, name, description) - - async def run(self, args: BaseModel, cancellation_token: CancellationToken) -> Any: - # Prepare arguments - kwargs = args.model_dump() - - # Determine if the callable is asynchronous - if inspect.iscoroutinefunction(self._callable): - result = await self._callable(**kwargs) - else: - # Run in a thread to avoid blocking the event loop - result = await asyncio.to_thread(self._call_sync, kwargs) - - return result - - def _call_sync(self, kwargs: Dict[str, Any]) -> Any: - return self._callable(**kwargs) +import asyncio +import inspect +from typing import Any, Callable, Dict, Tuple, Type, cast + +from autogen_core.base import CancellationToken +from autogen_core.components.tools import BaseTool +from pydantic import BaseModel, Field, create_model +from pydantic.fields import FieldInfo + +from langchain.tools import Tool as LangChainTool + +FieldDefinition = Tuple[Type[Any], FieldInfo] +FieldsDict = Dict[str, FieldDefinition] + + +class LangChainToolAdapter(BaseTool[BaseModel, Any]): + langchain_tool: LangChainTool + _callable: Callable[..., Any] + + def __init__(self, langchain_tool: LangChainTool): + self.langchain_tool = langchain_tool + + # Extract name and description + name = langchain_tool.name + description = langchain_tool.description or "" + + # Determine the callable method + if hasattr(langchain_tool, "func") and callable(langchain_tool.func): + assert langchain_tool.func is not None + self._callable = langchain_tool.func + elif hasattr(langchain_tool, "_run") and callable(langchain_tool._run): # pyright: ignore + self._callable = langchain_tool._run # type: ignore + else: + raise AttributeError( + f"The provided LangChain tool '{name}' does not have a callable 'func' or '_run' method." + ) + + # Determine args_type + if langchain_tool.args_schema: # pyright: ignore + args_type = langchain_tool.args_schema # pyright: ignore + else: + # Infer args_type from the callable's signature + sig = inspect.signature(cast(Callable[..., Any], self._callable)) + fields = { + k: (v.annotation, Field(...)) + for k, v in sig.parameters.items() + if k != "self" and v.kind not in (inspect.Parameter.VAR_POSITIONAL, inspect.Parameter.VAR_KEYWORD) + } + args_type = create_model(f"{name}Args", **fields) # type: ignore + # Note: type ignore is used due to a LangChain typing limitation + + # Ensure args_type is a subclass of BaseModel + if not issubclass(args_type, BaseModel): + raise ValueError(f"Failed to create a valid Pydantic v2 model for {name}") + + # Assume return_type as Any if not specified + return_type: Type[Any] = object + + super().__init__(args_type, return_type, name, description) + + async def run(self, args: BaseModel, cancellation_token: CancellationToken) -> Any: + # Prepare arguments + kwargs = args.model_dump() + + # Determine if the callable is asynchronous + if inspect.iscoroutinefunction(self._callable): + result = await self._callable(**kwargs) + else: + # Run in a thread to avoid blocking the event loop + result = await asyncio.to_thread(self._call_sync, kwargs) + + return result + + def _call_sync(self, kwargs: Dict[str, Any]) -> Any: + return self._callable(**kwargs) diff --git a/python/packages/autogen-ext/tests/test_tools.py b/python/packages/autogen-ext/tests/test_tools.py index 2cf1670402..e824576b41 100644 --- a/python/packages/autogen-ext/tests/test_tools.py +++ b/python/packages/autogen-ext/tests/test_tools.py @@ -1,97 +1,97 @@ -from typing import Optional, Type - -import pytest -from autogen_core.base import CancellationToken -from autogen_ext.tools.langchain import LangChainToolAdapter # type: ignore -from langchain.tools import BaseTool as LangChainTool # type: ignore -from langchain.tools import tool # pyright: ignore -from langchain_core.callbacks.manager import AsyncCallbackManagerForToolRun, CallbackManagerForToolRun -from pydantic import BaseModel, Field - - -@tool # type: ignore -def add(a: int, b: int) -> int: - """Add two numbers""" - return a + b - - -class CalculatorInput(BaseModel): - a: int = Field(description="first number") - b: int = Field(description="second number") - - -class CustomCalculatorTool(LangChainTool): - name: str = "Calculator" - description: str = "useful for when you need to answer questions about math" - args_schema: Type[BaseModel] = CalculatorInput - return_direct: bool = True - - def _run(self, a: int, b: int, run_manager: Optional[CallbackManagerForToolRun] = None) -> int: - """Use the tool.""" - return a * b - - async def _arun( - self, - a: int, - b: int, - run_manager: Optional[AsyncCallbackManagerForToolRun] = None, - ) -> int: - """Use the tool asynchronously.""" - return self._run(a, b, run_manager=run_manager.get_sync() if run_manager else None) - - -@pytest.mark.asyncio -async def test_langchain_tool_adapter() -> None: - # Create a LangChain tool - langchain_tool = add # type: ignore - - # Create an adapter - adapter = LangChainToolAdapter(langchain_tool) # pyright: ignore - - # Test schema generation - schema = adapter.schema - - assert schema["name"] == "add" - assert "description" in schema - assert schema["description"] == "Add two numbers" - assert "parameters" in schema - assert schema["parameters"]["type"] == "object" - assert "properties" in schema["parameters"] - assert "a" in schema["parameters"]["properties"] - assert "b" in schema["parameters"]["properties"] - assert schema["parameters"]["properties"]["a"]["type"] == "integer" - assert schema["parameters"]["properties"]["b"]["type"] == "integer" - assert "required" in schema["parameters"] - assert set(schema["parameters"]["required"]) == {"a", "b"} - assert len(schema["parameters"]["properties"]) == 2 - - # Test run method - result = await adapter.run_json({"a": 2, "b": 3}, CancellationToken()) - assert result == 5 - - # Test that the adapter's run method can be called multiple times - result = await adapter.run_json({"a": 5, "b": 7}, CancellationToken()) - assert result == 12 - - # Test CustomCalculatorTool - custom_langchain_tool = CustomCalculatorTool() - custom_adapter = LangChainToolAdapter(custom_langchain_tool) # pyright: ignore - - # Test schema generation for CustomCalculatorTool - custom_schema = custom_adapter.schema - - assert custom_schema["name"] == "Calculator" - assert custom_schema["description"] == "useful for when you need to answer questions about math" # type: ignore - assert "parameters" in custom_schema - assert custom_schema["parameters"]["type"] == "object" - assert "properties" in custom_schema["parameters"] - assert "a" in custom_schema["parameters"]["properties"] - assert "b" in custom_schema["parameters"]["properties"] - assert custom_schema["parameters"]["properties"]["a"]["type"] == "integer" - assert custom_schema["parameters"]["properties"]["b"]["type"] == "integer" - assert "required" in custom_schema["parameters"] - assert set(custom_schema["parameters"]["required"]) == {"a", "b"} - - # Test run method for CustomCalculatorTool - custom_result = await custom_adapter.run_json({"a": 3, "b": 4}, CancellationToken()) - assert custom_result == 12 +from typing import Optional, Type + +import pytest +from autogen_core.base import CancellationToken +from autogen_ext.tools.langchain import LangChainToolAdapter # type: ignore +from langchain.tools import BaseTool as LangChainTool # type: ignore +from langchain.tools import tool # pyright: ignore +from langchain_core.callbacks.manager import AsyncCallbackManagerForToolRun, CallbackManagerForToolRun +from pydantic import BaseModel, Field + + +@tool # type: ignore +def add(a: int, b: int) -> int: + """Add two numbers""" + return a + b + + +class CalculatorInput(BaseModel): + a: int = Field(description="first number") + b: int = Field(description="second number") + + +class CustomCalculatorTool(LangChainTool): + name: str = "Calculator" + description: str = "useful for when you need to answer questions about math" + args_schema: Type[BaseModel] = CalculatorInput + return_direct: bool = True + + def _run(self, a: int, b: int, run_manager: Optional[CallbackManagerForToolRun] = None) -> int: + """Use the tool.""" + return a * b + + async def _arun( + self, + a: int, + b: int, + run_manager: Optional[AsyncCallbackManagerForToolRun] = None, + ) -> int: + """Use the tool asynchronously.""" + return self._run(a, b, run_manager=run_manager.get_sync() if run_manager else None) + + +@pytest.mark.asyncio +async def test_langchain_tool_adapter() -> None: + # Create a LangChain tool + langchain_tool = add # type: ignore + + # Create an adapter + adapter = LangChainToolAdapter(langchain_tool) # pyright: ignore + + # Test schema generation + schema = adapter.schema + + assert schema["name"] == "add" + assert "description" in schema + assert schema["description"] == "Add two numbers" + assert "parameters" in schema + assert schema["parameters"]["type"] == "object" + assert "properties" in schema["parameters"] + assert "a" in schema["parameters"]["properties"] + assert "b" in schema["parameters"]["properties"] + assert schema["parameters"]["properties"]["a"]["type"] == "integer" + assert schema["parameters"]["properties"]["b"]["type"] == "integer" + assert "required" in schema["parameters"] + assert set(schema["parameters"]["required"]) == {"a", "b"} + assert len(schema["parameters"]["properties"]) == 2 + + # Test run method + result = await adapter.run_json({"a": 2, "b": 3}, CancellationToken()) + assert result == 5 + + # Test that the adapter's run method can be called multiple times + result = await adapter.run_json({"a": 5, "b": 7}, CancellationToken()) + assert result == 12 + + # Test CustomCalculatorTool + custom_langchain_tool = CustomCalculatorTool() + custom_adapter = LangChainToolAdapter(custom_langchain_tool) # pyright: ignore + + # Test schema generation for CustomCalculatorTool + custom_schema = custom_adapter.schema + + assert custom_schema["name"] == "Calculator" + assert custom_schema["description"] == "useful for when you need to answer questions about math" # type: ignore + assert "parameters" in custom_schema + assert custom_schema["parameters"]["type"] == "object" + assert "properties" in custom_schema["parameters"] + assert "a" in custom_schema["parameters"]["properties"] + assert "b" in custom_schema["parameters"]["properties"] + assert custom_schema["parameters"]["properties"]["a"]["type"] == "integer" + assert custom_schema["parameters"]["properties"]["b"]["type"] == "integer" + assert "required" in custom_schema["parameters"] + assert set(custom_schema["parameters"]["required"]) == {"a", "b"} + + # Test run method for CustomCalculatorTool + custom_result = await custom_adapter.run_json({"a": 3, "b": 4}, CancellationToken()) + assert custom_result == 12 diff --git a/python/packages/team-one/pyproject.toml b/python/packages/team-one/pyproject.toml index d1218840b2..57f74ca8ec 100644 --- a/python/packages/team-one/pyproject.toml +++ b/python/packages/team-one/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "hatchling.build" [project] name = "team-one" -version = "0.0.1" +version = "0.1.0dev0" description = '' readme = "readme.md" requires-python = ">=3.10" diff --git a/python/uv.lock b/python/uv.lock index 322382644d..6faa996047 100644 --- a/python/uv.lock +++ b/python/uv.lock @@ -335,7 +335,7 @@ wheels = [ [[package]] name = "autogen-agentchat" -version = "0.3.0.dev0" +version = "0.4.0.dev0" source = { editable = "packages/autogen-agentchat" } dependencies = [ { name = "autogen-core" }, @@ -346,7 +346,7 @@ requires-dist = [{ name = "autogen-core", editable = "packages/autogen-core" }] [[package]] name = "autogen-core" -version = "0.3.dev0" +version = "0.4.0.dev0" source = { editable = "packages/autogen-core" } dependencies = [ { name = "aiohttp" }, @@ -461,7 +461,7 @@ dev = [ [[package]] name = "autogen-ext" -version = "0.3.0.dev0" +version = "0.4.0.dev0" source = { editable = "packages/autogen-ext" } dependencies = [ { name = "autogen-core" }, @@ -4534,7 +4534,7 @@ wheels = [ [[package]] name = "team-one" -version = "0.0.1" +version = "0.1.0.dev0" source = { editable = "packages/team-one" } dependencies = [ { name = "aiofiles" },