Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
106 changes: 67 additions & 39 deletions src/google/adk/a2a/converters/part_converter.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,23 +60,30 @@ def convert_a2a_part_to_genai_part(
) -> Optional[genai_types.Part]:
"""Convert an A2A Part to a Google GenAI Part."""
part = a2a_part.root

thought = None
if part.metadata:
thought = part.metadata.get(_get_adk_metadata_key('thought'))

if isinstance(part, a2a_types.TextPart):
return genai_types.Part(text=part.text)
return genai_types.Part(text=part.text, thought=thought)

if isinstance(part, a2a_types.FilePart):
if isinstance(part.file, a2a_types.FileWithUri):
return genai_types.Part(
file_data=genai_types.FileData(
file_uri=part.file.uri, mime_type=part.file.mime_type
)
),
thought=thought,
)

elif isinstance(part.file, a2a_types.FileWithBytes):
return genai_types.Part(
inline_data=genai_types.Blob(
data=base64.b64decode(part.file.bytes),
mime_type=part.file.mime_type,
)
),
thought=thought,
)
else:
logger.warning(
Expand Down Expand Up @@ -104,7 +111,8 @@ def convert_a2a_part_to_genai_part(
return genai_types.Part(
function_call=genai_types.FunctionCall.model_validate(
part.data, by_alias=True
)
),
thought=thought,
)
if (
part.metadata[_get_adk_metadata_key(A2A_DATA_PART_METADATA_TYPE_KEY)]
Expand All @@ -113,7 +121,8 @@ def convert_a2a_part_to_genai_part(
return genai_types.Part(
function_response=genai_types.FunctionResponse.model_validate(
part.data, by_alias=True
)
),
thought=thought,
)
if (
part.metadata[_get_adk_metadata_key(A2A_DATA_PART_METADATA_TYPE_KEY)]
Expand All @@ -122,7 +131,8 @@ def convert_a2a_part_to_genai_part(
return genai_types.Part(
code_execution_result=genai_types.CodeExecutionResult.model_validate(
part.data, by_alias=True
)
),
thought=thought,
)
if (
part.metadata[_get_adk_metadata_key(A2A_DATA_PART_METADATA_TYPE_KEY)]
Expand All @@ -131,7 +141,8 @@ def convert_a2a_part_to_genai_part(
return genai_types.Part(
executable_code=genai_types.ExecutableCode.model_validate(
part.data, by_alias=True
)
),
thought=thought,
)
return genai_types.Part(
inline_data=genai_types.Blob(
Expand All @@ -141,7 +152,8 @@ def convert_a2a_part_to_genai_part(
)
+ A2A_DATA_PART_END_TAG,
mime_type=A2A_DATA_PART_TEXT_MIME_TYPE,
)
),
thought=thought,
)

logger.warning(
Expand All @@ -165,14 +177,15 @@ def convert_genai_part_to_a2a_part(
return a2a_types.Part(root=a2a_part)

if part.file_data:
return a2a_types.Part(
root=a2a_types.FilePart(
file=a2a_types.FileWithUri(
uri=part.file_data.file_uri,
mime_type=part.file_data.mime_type,
)
a2a_part = a2a_types.FilePart(
file=a2a_types.FileWithUri(
uri=part.file_data.file_uri,
mime_type=part.file_data.mime_type,
)
)
if part.thought is not None:
a2a_part.metadata = {_get_adk_metadata_key('thought'): part.thought}
return a2a_types.Part(root=a2a_part)

if part.inline_data:
if (
Expand All @@ -196,12 +209,15 @@ def convert_genai_part_to_a2a_part(
)
)

metadata = {}
if part.video_metadata:
a2a_part.metadata = {
_get_adk_metadata_key(
'video_metadata'
): part.video_metadata.model_dump(by_alias=True, exclude_none=True)
}
metadata[_get_adk_metadata_key('video_metadata')] = (
part.video_metadata.model_dump(by_alias=True, exclude_none=True)
)
if part.thought is not None:
metadata[_get_adk_metadata_key('thought')] = part.thought
if metadata:
a2a_part.metadata = metadata

return a2a_types.Part(root=a2a_part)

Expand All @@ -211,58 +227,70 @@ def convert_genai_part_to_a2a_part(
# TODO once A2A defined how to service such information, migrate below
# logic accordingly
if part.function_call:
metadata = {
_get_adk_metadata_key(
A2A_DATA_PART_METADATA_TYPE_KEY
): A2A_DATA_PART_METADATA_TYPE_FUNCTION_CALL
}
if part.thought is not None:
metadata[_get_adk_metadata_key('thought')] = part.thought
return a2a_types.Part(
root=a2a_types.DataPart(
data=part.function_call.model_dump(
by_alias=True, exclude_none=True
),
metadata={
_get_adk_metadata_key(
A2A_DATA_PART_METADATA_TYPE_KEY
): A2A_DATA_PART_METADATA_TYPE_FUNCTION_CALL
},
metadata=metadata,
)
)

if part.function_response:
metadata = {
_get_adk_metadata_key(
A2A_DATA_PART_METADATA_TYPE_KEY
): A2A_DATA_PART_METADATA_TYPE_FUNCTION_RESPONSE
}
if part.thought is not None:
metadata[_get_adk_metadata_key('thought')] = part.thought
return a2a_types.Part(
root=a2a_types.DataPart(
data=part.function_response.model_dump(
by_alias=True, exclude_none=True
),
metadata={
_get_adk_metadata_key(
A2A_DATA_PART_METADATA_TYPE_KEY
): A2A_DATA_PART_METADATA_TYPE_FUNCTION_RESPONSE
},
metadata=metadata,
)
)

if part.code_execution_result:
metadata = {
_get_adk_metadata_key(
A2A_DATA_PART_METADATA_TYPE_KEY
): A2A_DATA_PART_METADATA_TYPE_CODE_EXECUTION_RESULT
}
if part.thought is not None:
metadata[_get_adk_metadata_key('thought')] = part.thought
return a2a_types.Part(
root=a2a_types.DataPart(
data=part.code_execution_result.model_dump(
by_alias=True, exclude_none=True
),
metadata={
_get_adk_metadata_key(
A2A_DATA_PART_METADATA_TYPE_KEY
): A2A_DATA_PART_METADATA_TYPE_CODE_EXECUTION_RESULT
},
metadata=metadata,
)
)

if part.executable_code:
metadata = {
_get_adk_metadata_key(
A2A_DATA_PART_METADATA_TYPE_KEY
): A2A_DATA_PART_METADATA_TYPE_EXECUTABLE_CODE
}
if part.thought is not None:
metadata[_get_adk_metadata_key('thought')] = part.thought
return a2a_types.Part(
root=a2a_types.DataPart(
data=part.executable_code.model_dump(
by_alias=True, exclude_none=True
),
metadata={
_get_adk_metadata_key(
A2A_DATA_PART_METADATA_TYPE_KEY
): A2A_DATA_PART_METADATA_TYPE_EXECUTABLE_CODE
},
metadata=metadata,
)
)
Comment on lines 229 to 295
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The logic for handling function_call, function_response, code_execution_result, and executable_code parts is very similar and has been repeated four times. This code duplication can make the code harder to read and maintain. To improve this, you could refactor this section to eliminate the repetition. A good approach would be to use a loop over a data structure (like a dictionary) that maps the part attributes to their corresponding metadata types. This would make the code more concise and easier to maintain.

  data_part_mapping = {
      'function_call': (
          part.function_call,
          A2A_DATA_PART_METADATA_TYPE_FUNCTION_CALL,
      ),
      'function_response': (
          part.function_response,
          A2A_DATA_PART_METADATA_TYPE_FUNCTION_RESPONSE,
      ),
      'code_execution_result': (
          part.code_execution_result,
          A2A_DATA_PART_METADATA_TYPE_CODE_EXECUTION_RESULT,
      ),
      'executable_code': (
          part.executable_code,
          A2A_DATA_PART_METADATA_TYPE_EXECUTABLE_CODE,
      ),
  }

  for data_obj, metadata_type in data_part_mapping.values():
    if data_obj:
      metadata = {
          _get_adk_metadata_key(A2A_DATA_PART_METADATA_TYPE_KEY): metadata_type
      }
      if part.thought is not None:
        metadata[_get_adk_metadata_key('thought')] = part.thought
      return a2a_types.Part(
          root=a2a_types.DataPart(
              data=data_obj.model_dump(by_alias=True, exclude_none=True),
              metadata=metadata,
          )
      )


Expand Down
Loading
Loading