Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add user proxy docs. Make user proxy's default impl cancellable #4459

Merged
merged 23 commits into from
Dec 6, 2024
Merged
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from inspect import iscoroutinefunction
from typing import Awaitable, Callable, List, Optional, Sequence, Union, cast

from aioconsole import ainput # type: ignore
from autogen_core import CancellationToken

from ..base import Response
Expand All @@ -14,6 +15,15 @@
InputFuncType = Union[SyncInputFunc, AsyncInputFunc]


# TODO: ainput doesn't seem to play nicely with jupyter.
# No input window appears in this case.
async def cancellable_input(prompt: str, cancellation_token: Optional[CancellationToken]) -> str:
task = asyncio.Task[str](asyncio.create_task(ainput(prompt))) # type: ignore
if cancellation_token is not None:
cancellation_token.link_future(task)
return await task


class UserProxyAgent(BaseChatAgent):
peterychang marked this conversation as resolved.
Show resolved Hide resolved
"""An agent that can represent a human user through an input function.

Expand All @@ -40,6 +50,63 @@ class UserProxyAgent(BaseChatAgent):

See `Pause for User Input <https://microsoft.github.io/autogen/dev/user-guide/agentchat-user-guide/tutorial/teams.html#pause-for-user-input>`_ for more information.

Example:
Simple usage case::

import asyncio
from autogen_core import CancellationToken
from autogen_agentchat.agents import UserProxyAgent
from autogen_agentchat.messages import TextMessage


async def simple_user_agent():
agent = UserProxyAgent("user_proxy")
response = await asyncio.create_task(
agent.on_messages(
[TextMessage(content="What is your name? ", source="user")],
cancellation_token=CancellationToken(),
)
)
print(f"Your name is {response.chat_message.content}")

Example:
Cancellable usage case::

import asyncio
from typing import Any
from autogen_core import CancellationToken
from autogen_agentchat.agents import UserProxyAgent
from autogen_agentchat.messages import TextMessage


token = CancellationToken()
agent = UserProxyAgent("user_proxy")


async def timeout(delay: float):
await asyncio.sleep(delay)


def cancellation_callback(task: asyncio.Task[Any]):
token.cancel()


async def cancellable_user_agent():
try:
timeout_task = asyncio.create_task(timeout(3))
timeout_task.add_done_callback(cancellation_callback)
agent_task = asyncio.create_task(
agent.on_messages(
[TextMessage(content="What is your name? ", source="user")],
cancellation_token=CancellationToken(),
)
)
response = await agent_task
print(f"Your name is {response.chat_message.content}")
except Exception as e:
print(f"Exception: {e}")
except BaseException as e:
print(f"BaseException: {e}")
"""

def __init__(
Expand All @@ -51,7 +118,7 @@ def __init__(
) -> None:
"""Initialize the UserProxyAgent."""
super().__init__(name=name, description=description)
self.input_func = input_func or input
self.input_func = input_func or cancellable_input
peterychang marked this conversation as resolved.
Show resolved Hide resolved
self._is_async = iscoroutinefunction(self.input_func)

@property
Expand Down
peterychang marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
},
{
"cell_type": "code",
"execution_count": 1,
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
Expand Down Expand Up @@ -101,6 +101,39 @@
"as well as a list of inner messages in the {py:attr}`~autogen_agentchat.base.Response.inner_messages` attribute,\n",
"which stores the agent's \"thought process\" that led to the final response.\n",
"\n",
"## User Proxy Agent\n",
"\n",
"{py:class}`~autogen_agentchat.agents.UserProxyAgent` is a built-in agent that\n",
provides one way for a user to intervene in the process. This agent will put the team in a temporary blocking state, and thus any exceptions or runtime failures while in the blocked state will result in a deadlock. It is strongly advised that this agent be coupled with a timeout mechanic and that all errors and exceptions emanating from it are handled.
peterychang marked this conversation as resolved.
Show resolved Hide resolved
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from autogen_agentchat.agents import UserProxyAgent\n",
"\n",
"\n",
"async def user_proxy_run() -> None:\n",
" user_proxy_agent = UserProxyAgent(\"user_proxy\")\n",
" response = await user_proxy_agent.on_messages(\n",
" [TextMessage(content=\"What is your name? \", source=\"user\")], cancellation_token=CancellationToken()\n",
" )\n",
" print(f\"Your name is {response.chat_message.content}\")\n",
"\n",
"\n",
"# Use asyncio.run(user_proxy_run()) when running in a script.\n",
"await user_proxy_run()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The User Proxy agent is ideally used for on-demand human-in-the-loop interactions for scenarios such as Just In Time approvals, human feedback, alerts, etc. For slower user interactions, consider terminating the session using a termination condition and start another one from run or run_stream with another message.\n",
"\n",
"### Stream Messages\n",
"\n",
"We can also stream each message as it is generated by the agent by using the\n",
Expand Down
1 change: 1 addition & 0 deletions python/packages/autogen-core/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ classifiers = [
dependencies = [
"openai>=1.3",
"pillow",
"aioconsole",
peterychang marked this conversation as resolved.
Show resolved Hide resolved
"aiohttp",
"typing-extensions",
"pydantic<3.0.0,>=2.0.0",
Expand Down
11 changes: 11 additions & 0 deletions python/uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading