1818
1919from __future__ import annotations
2020
21+ import base64
2122import json
2223import logging
2324import sys
4344logger = logging .getLogger ('google_adk.' + __name__ )
4445
4546A2A_DATA_PART_METADATA_TYPE_KEY = 'type'
47+ A2A_DATA_PART_METADATA_IS_LONG_RUNNING_KEY = 'is_long_running'
4648A2A_DATA_PART_METADATA_TYPE_FUNCTION_CALL = 'function_call'
4749A2A_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'
4852
4953
5054@working_in_progress
@@ -67,7 +71,8 @@ def convert_a2a_part_to_genai_part(
6771 elif isinstance (part .file , a2a_types .FileWithBytes ):
6872 return genai_types .Part (
6973 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 ,
7176 )
7277 )
7378 else :
@@ -84,7 +89,11 @@ def convert_a2a_part_to_genai_part(
8489 # response.
8590 # TODO once A2A defined how to suervice such information, migrate below
8691 # 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+ ):
8897 if (
8998 part .metadata [_get_adk_metadata_key (A2A_DATA_PART_METADATA_TYPE_KEY )]
9099 == A2A_DATA_PART_METADATA_TYPE_FUNCTION_CALL
@@ -103,6 +112,24 @@ def convert_a2a_part_to_genai_part(
103112 part .data , by_alias = True
104113 )
105114 )
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+ )
106133 return genai_types .Part (text = json .dumps (part .data ))
107134
108135 logger .warning (
@@ -118,27 +145,40 @@ def convert_genai_part_to_a2a_part(
118145 part : genai_types .Part ,
119146) -> Optional [a2a_types .Part ]:
120147 """Convert a Google GenAI Part to an A2A Part."""
148+
121149 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 )
123154
124155 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+ )
129162 )
130163 )
131164
132165 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 ,
139170 )
140171 )
141172
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+
142182 # Conver the funcall and function reponse to A2A DataPart.
143183 # This is mainly for converting human in the loop and auth request and
144184 # response.
@@ -151,9 +191,9 @@ def convert_genai_part_to_a2a_part(
151191 by_alias = True , exclude_none = True
152192 ),
153193 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
157197 },
158198 )
159199 )
@@ -165,9 +205,37 @@ def convert_genai_part_to_a2a_part(
165205 by_alias = True , exclude_none = True
166206 ),
167207 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
171239 },
172240 )
173241 )
0 commit comments