8000 chore: Add A2A Part converter (WIP) · cloudbuilderspa/adk-python@e181279 · GitHub
[go: up one dir, main page]

Skip to content

Commit e181279

Browse files
seanzhougooglecopybara-github
authored andcommitted
chore: Add A2A Part converter (WIP)
PiperOrigin-RevId: 772282116
1 parent c755cf2 commit e181279

File tree

5 files changed

+648
-0
lines changed

5 files changed

+648
-0
lines changed
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# Copyright 2025 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
# Copyright 2025 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
"""
16+
module containing utilities for conversion betwen A2A Part and Google GenAI Part
17+
"""
18+
19+
from __future__ import annotations
20+
21+
import json
22+
import logging
23+
from typing import Optional
24+
25+
from a2a import types as a2a_types
26+
from google.genai import types as genai_types
27+
28+
from ...utils.feature_decorator import working_in_progress
29+
30+
logger = logging.getLogger('google_adk.' + __name__)
31+
32+
A2A_DATA_PART_METADATA_TYPE_KEY = 'type'
33+
A2A_DATA_PART_METADATA_TYPE_FUNCTION_CALL = 'function_call'
34+
A2A_DATA_PART_METADATA_TYPE_FUNCTION_RESPONSE = 'function_response'
35+
36+
37+
@working_in_progress
38+
def convert_a2a_part_to_genai_part(
39+
a2a_part: a2a_types.Part,
40+
) -> Optional[genai_types.Part]:
41+
"""Convert an A2A Part to a Google GenAI Part."""
42+
part = a2a_part.root
43+
if isinstance(part, a2a_types.TextPart):
44+
return genai_types.Part(text=part.text)
45+
46+
if isinstance(part, a2a_types.FilePart):
47+
if isinstance(part.file, a2a_types.FileWithUri):
48+
return genai_types.Part(
49+
file_data=genai_types.FileData(
50+
file_uri=part.file.uri, mime_type=part.file.mimeType
51+
)
52+
)
53+
54+
elif isinstance(part.file, a2a_types.FileWithBytes):
55+
return genai_types.Part(
56+
inline_data=genai_types.Blob(
57+
data=part.file.bytes.encode('utf-8'), mime_type=part.file.mimeType
58+
)
59+
)
60+
else:
61+
logger.warning(
62+
'Cannot convert unsupported file type: %s for A2A part: %s',
63+
type(part.file),
64+
a2a_part,
65+
)
66+
return None
67+
68+
if isinstance(part, a2a_types.DataPart):
69+
# Conver the Data Part to funcall and function reponse.
70+
# This is mainly for converting human in the loop and auth request and
71+
# response.
72+
# TODO once A2A defined how to suervice such information, migrate below
73+
# logic accordinlgy
74+
if part.metadata and A2A_DATA_PART_METADATA_TYPE_KEY in part.metadata:
75+
if (
76+
part.metadata[A2A_DATA_PART_METADATA_TYPE_KEY]
77+
== A2A_DATA_PART_METADATA_TYPE_FUNCTION_CALL
78+
):
79+
return genai_types.Part(
80+
function_call=genai_types.FunctionCall.model_validate(
81+
part.data, by_alias=True
82+
)
83+
)
84+
if (
85+
part.metadata[A2A_DATA_PART_METADATA_TYPE_KEY]
86+
== A2A_DATA_PART_METADATA_TYPE_FUNCTION_RESPONSE
87+
):
88+
return genai_types.Part(
89+
function_response=genai_types.FunctionResponse.model_validate(
90+
part.data, by_alias=True
28BE
91+
)
92+
)
93+
return genai_types.Part(text=json.dumps(part.data))
94+
95+
logger.warning(
96+
'Cannot convert unsupported part type: %s for A2A part: %s',
97+
type(part),
98+
a2a_part,
99+
)
100+
return None
101+
102+
103+
@working_in_progress
104+
def convert_genai_part_to_a2a_part(
105+
part: genai_types.Part,
106+
) -> Optional[a2a_types.Part]:
107+
"""Convert a Google GenAI Part to an A2A Part."""
108+
if part.text:
109+
return a2a_types.TextPart(text=part.text)
110+
111+
if part.file_data:
112+
return a2a_types.FilePart(
113+
file=a2a_types.FileWithUri(
114+
uri=part.file_data.file_uri,
115+
mimeType=part.file_data.mime_type,
116+
)
117+
)
118+
119+
if part.inline_data:
120+
return a2a_types.Part(
121+
root=a2a_types.FilePart(
122+
file=a2a_types.FileWithBytes(
123+
bytes=part.inline_data.data,
124+
mimeType=part.inline_data.mime_type,
125+
)
126+
)
127+
)
128+
129+
# Conver the funcall and function reponse to A2A DataPart.
130+
# This is mainly for converting human in the loop and auth request and
131+
# response.
132+
# TODO once A2A defined how to suervice such information, migrate below
133+
# logic accordinlgy
134+
if part.function_call:
135+
return a2a_types.Part(
136+
root=a2a_types.DataPart(
137+
data=part.function_call.model_dump(
138+
by_alias=True, exclude_none=True
139+
),
140+
metadata={
141+
A2A_DATA_PART_METADATA_TYPE_KEY: (
142+
A2A_DATA_PART_METADATA_TYPE_FUNCTION_CALL
143+
)
144+
},
145+
)
146+
)
147+
148+
if part.function_response:
149+
return a2a_types.Part(
150+
root=a2a_types.DataPart(
151+
data=part.function_response.model_dump(
152+
by_alias=True, exclude_none=True
153+
),
154+
metadata={
155+
A2A_DATA_PART_METADATA_TYPE_KEY: (
156+
A2A_DATA_PART_METADATA_TYPE_FUNCTION_RESPONSE
157+
)
158+
},
159+
)
160+
)
161+
162+
logger.warning(
163+
'Cannot convert unsupported part for Google GenAI part: %s',
164+
part,
165+
)
166+
return None

tests/unittests/a2a/__init__.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# Copyright 2025 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.

tests/unittests/a2a/converters/__init__.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# Copyright 2025 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.

0 commit comments

Comments
 (0)
0