Skip to content

Commit ecf6f7f

Browse files
authored
Merge branch 'main' into main
2 parents bda1354 + 8fcba01 commit ecf6f7f

12 files changed

Lines changed: 820 additions & 7 deletions

File tree

python/packages/autogen-core/docs/src/user-guide/core-user-guide/cookbook/llamaindex-agent.ipynb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@
3838
"outputs": [],
3939
"source": [
4040
"import os\n",
41-
"from pydantic import BaseModel\n",
4241
"from typing import List, Optional\n",
4342
"\n",
4443
"from autogen_core import AgentId, MessageContext, RoutedAgent, SingleThreadedAgentRuntime, message_handler\n",
@@ -57,7 +56,8 @@
5756
"from llama_index.embeddings.openai import OpenAIEmbedding\n",
5857
"from llama_index.llms.azure_openai import AzureOpenAI\n",
5958
"from llama_index.llms.openai import OpenAI\n",
60-
"from llama_index.tools.wikipedia import WikipediaToolSpec"
59+
"from llama_index.tools.wikipedia import WikipediaToolSpec\n",
60+
"from pydantic import BaseModel"
6161
]
6262
},
6363
{

python/packages/autogen-core/src/autogen_core/_component_config.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
from pydantic import BaseModel
88
from typing_extensions import Self, TypeVar
99

10-
ComponentType = Literal["model", "agent", "tool", "termination", "token_provider"] | str
10+
ComponentType = Literal["model", "agent", "tool", "termination", "token_provider", "workbench"] | str
1111
ConfigT = TypeVar("ConfigT", bound=BaseModel)
1212
FromConfigT = TypeVar("FromConfigT", bound=BaseModel, contravariant=True)
1313
ToConfigT = TypeVar("ToConfigT", bound=BaseModel, covariant=True)

python/packages/autogen-core/src/autogen_core/tools/__init__.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
from ._base import BaseTool, BaseToolWithState, ParametersSchema, Tool, ToolSchema
22
from ._function_tool import FunctionTool
3+
from ._static_workbench import StaticWorkbench
4+
from ._workbench import ImageResultContent, TextResultContent, ToolResult, Workbench
35

46
__all__ = [
57
"Tool",
@@ -8,4 +10,9 @@
810
"BaseTool",
911
"BaseToolWithState",
1012
"FunctionTool",
13+
"Workbench",
14+
"ToolResult",
15+
"TextResultContent",
16+
"ImageResultContent",
17+
"StaticWorkbench",
1118
]

python/packages/autogen-core/src/autogen_core/tools/_function_tool.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -178,4 +178,4 @@ def _from_config(cls, config: FunctionToolConfig) -> Self:
178178
if not callable(func):
179179
raise TypeError(f"Expected function but got {type(func)}")
180180

181-
return cls(func, "", None)
181+
return cls(func, name=config.name, description=config.description, global_imports=config.global_imports)
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
import asyncio
2+
from typing import Any, Dict, List, Literal, Mapping
3+
4+
from pydantic import BaseModel
5+
from typing_extensions import Self
6+
7+
from .._cancellation_token import CancellationToken
8+
from .._component_config import Component, ComponentModel
9+
from ._base import BaseTool, ToolSchema
10+
from ._workbench import TextResultContent, ToolResult, Workbench
11+
12+
13+
class StaticWorkbenchConfig(BaseModel):
14+
tools: List[ComponentModel] = []
15+
16+
17+
class StateicWorkbenchState(BaseModel):
18+
type: Literal["StaticWorkbenchState"] = "StaticWorkbenchState"
19+
tools: Dict[str, Mapping[str, Any]] = {}
20+
21+
22+
class StaticWorkbench(Workbench, Component[StaticWorkbenchConfig]):
23+
"""
24+
A workbench that provides a static set of tools that do not change after
25+
each tool execution.
26+
27+
Args:
28+
tools (List[BaseTool[Any, Any]]): A list of tools to be included in the workbench.
29+
The tools should be subclasses of :class:`~autogen_core.tools.BaseTool`.
30+
"""
31+
32+
component_provider_override = "autogen_core.tools.StaticWorkbench"
33+
component_config_schema = StaticWorkbenchConfig
34+
35+
def __init__(self, tools: List[BaseTool[Any, Any]]) -> None:
36+
self._tools = tools
37+
38+
async def list_tools(self) -> List[ToolSchema]:
39+
return [tool.schema for tool in self._tools]
40+
41+
async def call_tool(
42+
self, name: str, arguments: Mapping[str, Any] | None = None, cancellation_token: CancellationToken | None = None
43+
) -> ToolResult:
44+
tool = next((tool for tool in self._tools if tool.name == name), None)
45+
if tool is None:
46+
raise ValueError(f"Tool {name} not found in workbench.")
47+
if not cancellation_token:
48+
cancellation_token = CancellationToken()
49+
if not arguments:
50+
arguments = {}
51+
try:
52+
result_future = asyncio.ensure_future(tool.run_json(arguments, cancellation_token))
53+
cancellation_token.link_future(result_future)
54+
result = await result_future
55+
is_error = False
56+
except Exception as e:
57+
result = str(e)
58+
is_error = True
59+
result_str = tool.return_value_as_string(result)
60+
return ToolResult(name=tool.name, result=[TextResultContent(content=result_str)], is_error=is_error)
61+
62+
async def start(self) -> None:
63+
return None
64+
65+
async def stop(self) -> None:
66+
return None
67+
68+
async def reset(self) -> None:
69+
return None
70+
71+
async def save_state(self) -> Mapping[str, Any]:
72+
tool_states = StateicWorkbenchState()
73+
for tool in self._tools:
74+
tool_states.tools[tool.name] = await tool.save_state_json()
75+
return tool_states.model_dump()
76+
77+
async def load_state(self, state: Mapping[str, Any]) -> None:
78+
parsed_state = StateicWorkbenchState.model_validate(state)
79+
for tool in self._tools:
80+
if tool.name in parsed_state.tools:
81+
await tool.load_state_json(parsed_state.tools[tool.name])
82+
83+
def _to_config(self) -> StaticWorkbenchConfig:
84+
return StaticWorkbenchConfig(tools=[tool.dump_component() for tool in self._tools])
85+
86+
@classmethod
87+
def _from_config(cls, config: StaticWorkbenchConfig) -> Self:
88+
return cls(tools=[BaseTool.load_component(tool) for tool in config.tools])
Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
from abc import ABC, abstractmethod
2+
from types import TracebackType
3+
from typing import Any, List, Literal, Mapping, Optional, Type
4+
5+
from pydantic import BaseModel, Field
6+
from typing_extensions import Annotated, Self
7+
8+
from .._cancellation_token import CancellationToken
9+
from .._component_config import ComponentBase
10+
from .._image import Image
11+
from ._base import ToolSchema
12+
13+
14+
class TextResultContent(BaseModel):
15+
"""
16+
Text result content of a tool execution.
17+
"""
18+
19+
type: Literal["TextResultContent"] = "TextResultContent"
20+
21+
content: str
22+
"""The text content of the result."""
23+
24+
25+
class ImageResultContent(BaseModel):
26+
"""
27+
Image result content of a tool execution.
28+
"""
29+
30+
type: Literal["ImageResultContent"] = "ImageResultContent"
31+
32+
content: Image
33+
"""The image content of the result."""
34+
35+
36+
ResultContent = Annotated[TextResultContent | ImageResultContent, Field(discriminator="type")]
37+
38+
39+
class ToolResult(BaseModel):
40+
"""
41+
A result of a tool execution by a workbench.
42+
"""
43+
44+
type: Literal["ToolResult"] = "ToolResult"
45+
46+
name: str
47+
"""The name of the tool that was executed."""
48+
49+
result: List[ResultContent]
50+
"""The result of the tool execution."""
51+
52+
is_error: bool = False
53+
"""Whether the tool execution resulted in an error."""
54+
55+
56+
class Workbench(ABC, ComponentBase[BaseModel]):
57+
"""
58+
A workbench is a component that provides a set of tools that may share
59+
resources and state.
60+
61+
A workbench is responsible for managing the lifecycle of the tools and
62+
providing a single interface to call them. The tools provided by the workbench
63+
may be dynamic and their availabilities may change after each tool execution.
64+
65+
A workbench can be started by calling the :meth:`~autogen_core.tools.Workbench.start` method
66+
and stopped by calling the :meth:`~autogen_core.tools.Workbench.stop` method.
67+
It can also be used as an asynchronous context manager, which will automatically
68+
start and stop the workbench when entering and exiting the context.
69+
"""
70+
71+
component_type = "workbench"
72+
73+
@abstractmethod
74+
async def list_tools(self) -> List[ToolSchema]:
75+
"""
76+
List the currently available tools in the workbench as :class:`ToolSchema`
77+
objects.
78+
79+
The list of tools may be dynamic, and their content may change after
80+
tool execution.
81+
"""
82+
...
83+
84+
@abstractmethod
85+
async def call_tool(
86+
self, name: str, arguments: Mapping[str, Any] | None = None, cancellation_token: CancellationToken | None = None
87+
) -> ToolResult:
88+
"""
89+
Call a tool in the workbench.
90+
91+
Args:
92+
name (str): The name of the tool to call.
93+
arguments (Mapping[str, Any] | None): The arguments to pass to the tool.
94+
If None, the tool will be called with no arguments.
95+
cancellation_token (CancellationToken | None): An optional cancellation token
96+
to cancel the tool execution.
97+
Returns:
98+
ToolResult: The result of the tool execution.
99+
"""
100+
...
101+
102+
@abstractmethod
103+
async def start(self) -> None:
104+
"""
105+
Start the workbench and initialize any resources.
106+
107+
This method should be called before using the workbench.
108+
"""
109+
...
110+
111+
@abstractmethod
112+
async def stop(self) -> None:
113+
"""
114+
Stop the workbench and release any resources.
115+
116+
This method should be called when the workbench is no longer needed.
117+
"""
118+
...
119+
120+
@abstractmethod
121+
async def reset(self) -> None:
122+
"""
123+
Reset the workbench to its initialized, started state.
124+
"""
125+
...
126+
127+
@abstractmethod
128+
async def save_state(self) -> Mapping[str, Any]:
129+
"""
130+
Save the state of the workbench.
131+
132+
This method should be called to persist the state of the workbench.
133+
"""
134+
...
135+
136+
@abstractmethod
137+
async def load_state(self, state: Mapping[str, Any]) -> None:
138+
"""
139+
Load the state of the workbench.
140+
141+
Args:
142+
state (Mapping[str, Any]): The state to load into the workbench.
143+
"""
144+
...
145+
146+
async def __aenter__(self) -> Self:
147+
"""
148+
Enter the workbench context manager.
149+
150+
This method is called when the workbench is used in a `with` statement.
151+
It calls the :meth:`~autogen_core.tools.WorkBench.start` method to start the workbench.
152+
"""
153+
await self.start()
154+
return self
155+
156+
async def __aexit__(
157+
self, exc_type: Optional[Type[BaseException]], exc_val: Optional[BaseException], exc_tb: Optional[TracebackType]
158+
) -> None:
159+
"""
160+
Exit the workbench context manager.
161+
This method is called when the workbench is used in a `with` statement.
162+
It calls the :meth:`~autogen_core.tools.WorkBench.stop` method to stop the workbench.
163+
"""
164+
await self.stop()

0 commit comments

Comments
 (0)