Skip to content

Commit 122a23f

Browse files
authored
feat(adapters): add nested trajectory support for AgentStack (#1273)
Ref: #1081 Signed-off-by: Lukáš Janeček <xjacka@gmail.com>
1 parent 942b423 commit 122a23f

File tree

4 files changed

+214
-185
lines changed

4 files changed

+214
-185
lines changed

python/beeai_framework/adapters/a2a/agents/_utils.py

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,18 @@
11
# Copyright 2025 © BeeAI a Series of LF Projects, LLC
22
# SPDX-License-Identifier: Apache-2.0
3+
from typing import Any
4+
from uuid import uuid4
5+
36
from beeai_framework.backend.message import (
47
AnyMessage,
58
AssistantMessage,
69
CustomMessageContent,
10+
Message,
711
MessageTextContent,
12+
Role,
813
UserMessage,
914
)
15+
from beeai_framework.logger import Logger
1016
from beeai_framework.utils.strings import to_json
1117

1218
try:
@@ -16,6 +22,8 @@
1622
"Optional module [a2a] not found.\nRun 'pip install \"beeai-framework[a2a]\"' to install."
1723
) from e
1824

25+
logger = Logger(__name__)
26+
1927

2028
def convert_a2a_to_framework_message(input: a2a_types.Message | a2a_types.Artifact) -> AnyMessage:
2129
msg = (
@@ -47,3 +55,49 @@ def convert_a2a_to_framework_message(input: a2a_types.Message | a2a_types.Artifa
4755
)
4856
)
4957
return msg
58+
59+
60+
def convert_to_a2a_message(
61+
input: str | list[AnyMessage] | AnyMessage | a2a_types.Message,
62+
*,
63+
context_id: str | None = None,
64+
task_id: str | None = None,
65+
reference_task_ids: list[str] | None = None,
66+
metadata: dict[str, Any] | None = None,
67+
) -> a2a_types.Message:
68+
if isinstance(input, list) and input and isinstance(input[-1], Message):
69+
if len(input) == 0:
70+
raise ValueError("Input cannot be empty")
71+
elif len(input) > 1:
72+
logger.warn("Input contains more than one message, only the last one will be used.")
73+
return convert_to_a2a_message(
74+
input[-1], context_id=context_id, task_id=task_id, reference_task_ids=reference_task_ids, metadata=metadata
75+
)
76+
elif isinstance(input, str):
77+
return a2a_types.Message(
78+
role=a2a_types.Role.user,
79+
parts=[a2a_types.Part(root=a2a_types.TextPart(text=input))],
80+
message_id=uuid4().hex,
81+
context_id=context_id,
82+
task_id=task_id,
83+
reference_task_ids=reference_task_ids,
84+
metadata=metadata,
85+
)
86+
elif isinstance(input, Message):
87+
return a2a_types.Message(
88+
role=a2a_types.Role.agent if input.role == Role.ASSISTANT else a2a_types.Role.user,
89+
parts=[a2a_types.Part(root=a2a_types.TextPart(text=input.text))],
90+
message_id=uuid4().hex,
91+
context_id=context_id,
92+
task_id=task_id,
93+
reference_task_ids=reference_task_ids,
94+
metadata=(metadata or {}) | input.meta or None,
95+
)
96+
elif isinstance(input, a2a_types.Message):
97+
input.metadata = (input.metadata or {}) | (metadata or {})
98+
input.context_id = context_id or input.context_id
99+
input.task_id = task_id or input.task_id
100+
input.reference_task_ids = reference_task_ids or input.reference_task_ids
101+
return input
102+
else:
103+
raise ValueError("Unsupported message type. Can not convert to a2a message.")

python/beeai_framework/adapters/a2a/agents/agent.py

Lines changed: 10 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
import ssl
55
from collections.abc import Callable, Mapping, Sequence
66
from typing import Any, Literal, Unpack
7-
from uuid import uuid4
87

98
import httpx
109
import httpx._types as httpx_types
@@ -15,7 +14,7 @@
1514
from httpx._urls import URL, QueryParams # noqa: F401
1615
from pydantic import BaseModel, ConfigDict
1716

18-
from beeai_framework.adapters.a2a.agents._utils import convert_a2a_to_framework_message
17+
from beeai_framework.adapters.a2a.agents._utils import convert_a2a_to_framework_message, convert_to_a2a_message
1918
from beeai_framework.adapters.a2a.agents.events import (
2019
A2AAgentErrorEvent,
2120
A2AAgentUpdateEvent,
@@ -42,7 +41,6 @@
4241
AnyMessage,
4342
AssistantMessage,
4443
Message,
45-
Role,
4644
)
4745
from beeai_framework.context import RunContext
4846
from beeai_framework.emitter import Emitter
@@ -353,34 +351,15 @@ def convert_to_a2a_message(
353351
context_id: str | None = None,
354352
metadata: dict[str, Any] | None = None,
355353
) -> a2a_types.Message:
356-
if isinstance(input, str):
357-
return a2a_types.Message(
358-
role=a2a_types.Role.user,
359-
parts=[a2a_types.Part(root=a2a_types.TextPart(text=input))],
360-
message_id=uuid4().hex,
361-
context_id=context_id or self._context_id,
362-
task_id=self._task_id if not context_id else None,
363-
reference_task_ids=self._reference_task_ids if not context_id else None,
364-
metadata=metadata,
365-
)
366-
elif isinstance(input, Message):
367-
return a2a_types.Message(
368-
role=a2a_types.Role.agent if input.role == Role.ASSISTANT else a2a_types.Role.user,
369-
parts=[a2a_types.Part(root=a2a_types.TextPart(text=input.text))],
370-
message_id=uuid4().hex,
371-
context_id=context_id or self._context_id,
372-
task_id=self._task_id if not context_id else None,
373-
reference_task_ids=self._reference_task_ids if not context_id else None,
374-
metadata=(metadata or {}) | input.meta or None,
375-
)
376-
elif isinstance(input, list) and input and isinstance(input[-1], Message):
377-
return self.convert_to_a2a_message(input[-1], context_id=context_id, metadata=metadata)
378-
elif isinstance(input, a2a_types.Message):
379-
input.metadata = (input.metadata or {}) | (metadata or {})
380-
input.context_id = context_id or input.context_id or self._context_id
381-
return input
382-
else:
383-
raise ValueError("Unsupported input type")
354+
return convert_to_a2a_message(
355+
input,
356+
metadata=metadata,
357+
context_id=context_id
358+
or (input.context_id if isinstance(input, a2a_types.Message) else None)
359+
or self._context_id,
360+
task_id=self._task_id if not context_id else None,
361+
reference_task_ids=self._reference_task_ids if not context_id else None,
362+
)
384363

385364

386365
def _convert_to_framework_message(

0 commit comments

Comments
 (0)