18
18
19
19
from __future__ import annotations
20
20
21
+ import base64
21
22
import json
22
23
import logging
23
24
import sys
43
44
logger = logging .getLogger ('google_adk.' + __name__ )
44
45
45
46
A2A_DATA_PART_METADATA_TYPE_KEY = 'type'
47
+ A2A_DATA_PART_METADATA_IS_LONG_RUNNING_KEY = 'is_long_running'
46
48
A2A_DATA_PART_METADATA_TYPE_FUNCTION_CALL = 'function_call'
47
49
A2A_DATA_PART_METADATA_TYPE_FUNCTION_RESPONSE = 'function_response'
50
+ A2A_DATA_PART_METADATA_TYPE_CODE_EXECUTION_RESULT = 'code_execution_result'
51
+ A2A_DATA_PART_METADATA_TYPE_EXECUTABLE_CODE = 'executable_code'
48
52
49
53
50
54
@working_in_progress
@@ -67,7 +71,8 @@ def convert_a2a_part_to_genai_part(
67
71
elif isinstance (part .file , a2a_types .FileWithBytes ):
68
72
return genai_types .Part (
69
73
inline_data = genai_types .Blob (
70
- data = part .file .bytes .encode ('utf-8' ), mime_type = part .file .mimeType
74
+ data = base64 .b64decode (part .file .bytes ),
75
+ mime_type = part .file .mimeType ,
71
76
)
72
77
)
73
78
else :
@@ -84,7 +89,11 @@ def convert_a2a_part_to_genai_part(
84
89
# response.
85
90
# TODO once A2A defined how to suervice such information, migrate below
86
91
# logic accordinlgy
87
- if part .metadata and A2A_DATA_PART_METADATA_TYPE_KEY in part .metadata :
92
+ if (
93
+ part .metadata
94
+ and _get_adk_metadata_key (A2A_DATA_PART_METADATA_TYPE_KEY )
95
+ in part .metadata
96
+ ):
88
97
if (
89
98
part .metadata [_get_adk_metadata_key (A2A_DATA_PART_METADATA_TYPE_KEY )]
90
99
== A2A_DATA_PART_METADATA_TYPE_FUNCTION_CALL
@@ -103,6 +112,24 @@ def convert_a2a_part_to_genai_part(
103
112
part .data , by_alias = True
104
113
)
105
114
)
115
+ if (
116
+ part .metadata [_get_adk_metadata_key (A2A_DATA_PART_METADATA_TYPE_KEY )]
117
+ == A2A_DATA_PART_METADATA_TYPE_CODE_EXECUTION_RESULT
118
+ ):
119
+ return genai_types .Part (
120
+ code_execution_result = genai_types .CodeExecutionResult .model_validate (
121
+ part .data , by_alias = True
122
+ )
123
+ )
124
+ if (
125
+ part .metadata [_get_adk_metadata_key (A2A_DATA_PART_METADATA_TYPE_KEY )]
126
+ == A2A_DATA_PART_METADATA_TYPE_EXECUTABLE_CODE
127
+ ):
128
+ return genai_types .Part (
129
+ executable_code = genai_types .ExecutableCode .model_validate (
130
+ part .data , by_alias = True
131
+ )
132
+ )
106
133
return genai_types .Part (text = json .dumps (part .data ))
107
134
108
135
logger .warning (
@@ -118,27 +145,40 @@ def convert_genai_part_to_a2a_part(
118
145
part : genai_types .Part ,
119
146
) -> Optional [a2a_types .Part ]:
120
147
"""Convert a Google GenAI Part to an A2A Part."""
148
+
121
149
if part .text :
122
- return a2a_types .TextPart (text = part .text )
150
+ a2a_part = a2a_types .TextPart (text = part .text )
151
+ if part .thought is not None :
152
+ a2a_part .metadata = {_get_adk_metadata_key ('thought' ): part .thought }
153
+ return a2a_types .Part (root = a2a_part )
123
154
124
155
if part .file_data :
125
- return a2a_types .FilePart (
126
- file = a2a_types .FileWithUri (
127
- uri = part .file_data .file_uri ,
128
- mimeType = part .file_data .mime_type ,
156
+ return a2a_types .Part (
157
+ root = a2a_types .FilePart (
158
+ file = a2a_types .FileWithUri (
159
+ uri = part .file_data .file_uri ,
160
+ mimeType = part .file_data .mime_type ,
161
+ )
129
162
)
130
163
)
131
164
132
165
if part .inline_data :
133
- return a2a_types .Part (
134
- root = a2a_types .FilePart (
135
- file = a2a_types .FileWithBytes (
136
- bytes = part .inline_data .data ,
137
- mimeType = part .inline_data .mime_type ,
138
- )
166
+ a2a_part = a2a_types .FilePart (
167
+ file = a2a_types .FileWithBytes (
168
+ bytes = base64 .b64encode (part .inline_data .data ).decode ('utf-8' ),
169
+ mimeType = part .inline_data .mime_type ,
139
170
)
140
171
)
141
172
173
+ if part .video_metadata :
174
+ a2a_part .metadata = {
175
+ _get_adk_metadata_key (
176
+ 'video_metadata'
177
+ ): part .video_metadata .model_dump (by_alias = True , exclude_none = True )
178
+ }
179
+
180
+ return a2a_types .Part (root = a2a_part )
181
+
142
182
# Conver the funcall and function reponse to A2A DataPart.
143
183
# This is mainly for converting human in the loop and auth request and
144
184
# response.
@@ -151,9 +191,9 @@ def convert_genai_part_to_a2a_part(
151
191
by_alias = True , exclude_none = True
152
192
),
153
193
metadata = {
154
- A2A_DATA_PART_METADATA_TYPE_KEY : (
155
- A2A_DATA_PART_METADATA_TYPE_FUNCTION_CALL
156
- )
194
+ _get_adk_metadata_key (
195
+ A2A_DATA_PART_METADATA_TYPE_KEY
196
+ ): A2A_DATA_PART_METADATA_TYPE_FUNCTION_CALL
157
197
},
158
198
)
159
199
)
@@ -165,9 +205,37 @@ def convert_genai_part_to_a2a_part(
165
205
by_alias = True , exclude_none = True
166
206
),
167
207
metadata = {
168
- A2A_DATA_PART_METADATA_TYPE_KEY : (
169
- A2A_DATA_PART_METADATA_TYPE_FUNCTION_RESPONSE
170
- )
208
+ _get_adk_metadata_key (
209
+ A2A_DATA_PART_METADATA_TYPE_KEY
210
+ ): A2A_DATA_PART_METADATA_TYPE_FUNCTION_RESPONSE
211
+ },
212
+ )
213
+ )
214
+
215
+ if part .code_execution_result :
216
+ return a2a_types .Part (
217
+ root = a2a_types .DataPart (
218
+ data = part .code_execution_result .model_dump (
219
+ by_alias = True , exclude_none = True
220
+ ),
221
+ metadata = {
222
+ _get_adk_metadata_key (
223
+ A2A_DATA_PART_METADATA_TYPE_KEY
224
+ ): A2A_DATA_PART_METADATA_TYPE_CODE_EXECUTION_RESULT
225
+ },
226
+ )
227
+ )
228
+
229
+ if part .executable_code :
230
+ return a2a_types .Part (
231
+ root = a2a_types .DataPart (
232
+ data = part .executable_code .model_dump (
233
+ by_alias = True , exclude_none = True
234
+ ),
235
+ metadata = {
236
+ _get_adk_metadata_key (
237
+ A2A_DATA_PART_METADATA_TYPE_KEY
238
+ ): A2A_DATA_PART_METADATA_TYPE_EXECUTABLE_CODE
171
239
},
172
240
)
173
241
)
0 commit comments