diff --git a/autogen/a2a/agent_executor.py b/autogen/a2a/agent_executor.py index 36745ab8065..2b9fbfcd3c0 100644 --- a/autogen/a2a/agent_executor.py +++ b/autogen/a2a/agent_executor.py @@ -3,11 +3,11 @@ # SPDX-License-Identifier: Apache-2.0 from datetime import datetime, timezone +from uuid import uuid4 from a2a.server.agent_execution import AgentExecutor, RequestContext from a2a.server.events import EventQueue -from a2a.types import TaskArtifactUpdateEvent, TaskState, TaskStatus, TaskStatusUpdateEvent -from a2a.utils import new_task +from a2a.types import Task, TaskArtifactUpdateEvent, TaskState, TaskStatus, TaskStatusUpdateEvent from a2a.utils.message import new_agent_text_message from autogen import ConversableAgent @@ -33,8 +33,17 @@ async def execute(self, context: RequestContext, event_queue: EventQueue) -> Non task = context.current_task if not task: - task = new_task(context.message) - task.status.timestamp = datetime.now(timezone.utc).isoformat() + request = context.message + # build task object manually to allow empty messages + task = Task( + status=TaskStatus( + state=TaskState.submitted, + timestamp=datetime.now(timezone.utc).isoformat(), + ), + id=request.task_id or str(uuid4()), + context_id=request.context_id or str(uuid4()), + history=[request], + ) # publish the task status submitted event await event_queue.enqueue_event(task) diff --git a/autogen/a2a/utils.py b/autogen/a2a/utils.py index c638ae1e6ee..c883ccb4663 100644 --- a/autogen/a2a/utils.py +++ b/autogen/a2a/utils.py @@ -10,8 +10,9 @@ from autogen.remote.protocol import RequestMessage, ResponseMessage -CLIENT_TOOLS_KEY = "ag2_client_tools" -CONTEXT_KEY = "ag2_context_update" +AG2_METADATA_KEY_PREFIX = "ag2_" +CLIENT_TOOLS_KEY = f"{AG2_METADATA_KEY_PREFIX}client_tools" +CONTEXT_KEY = f"{AG2_METADATA_KEY_PREFIX}context_update" def request_message_to_a2a( diff --git a/autogen/oai/client_utils.py b/autogen/oai/client_utils.py index 1b6aa37cca3..e8e51d445e5 100644 --- a/autogen/oai/client_utils.py +++ b/autogen/oai/client_utils.py @@ -129,13 +129,10 @@ def merge_config_with_tools(config: dict[str, Any], client_config: dict[str, Any full_config = {**config, **client_config} # Add tools if tools contains something AND are not using deprecated functions - config_tools = config.get("tools", []) - client_tools = client_config.get("tools", []) - - if config_tools or client_tools: + tools = config.get("tools", []) + client_config.get("tools", []) + if tools and "functions" not in full_config: # Don't add tools if functions parameter is present (deprecated API) - if "functions" not in full_config: - full_config["tools"] = config_tools + client_tools + full_config["tools"] = tools return full_config diff --git a/test/a2a/chats/test_chat.py b/test/a2a/chats/test_chat.py index 1fcce913d20..6705e309bb1 100644 --- a/test/a2a/chats/test_chat.py +++ b/test/a2a/chats/test_chat.py @@ -44,6 +44,25 @@ async def test_simple_messaging(remote_agent: ConversableAgent, a2a_client: Http } +@pytest.mark.asyncio() +async def test_empty_message_send(remote_agent: ConversableAgent, a2a_client: HttpxClientFactory) -> None: + # arrange + remote_agent_mirror = A2aRemoteAgent(url="http://memory", name="remote-mirror", client=a2a_client) + + with TestAgent(remote_agent, ["Hi, I am remote agent!"]): + # act + _, message = await remote_agent_mirror.a_generate_remote_reply([ + {"content": ""}, + ]) + + # assert + assert message == { + "content": "Hi, I am remote agent!", + "name": "remote", + "role": "assistant", + } + + @pytest.mark.asyncio() async def test_conversation(remote_agent: ConversableAgent, a2a_client: HttpxClientFactory) -> None: # arrange