Skip to content

Commit fb13963

Browse files
seanzhougooglecopybara-github
authored andcommitted
chore: Add request converter to convert a2a request to ADK request
PiperOrigin-RevId: 773894462
1 parent 2fd8feb commit fb13963

File tree

3 files changed

+624
-0
lines changed

3 files changed

+624
-0
lines changed
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
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+
from __future__ import annotations
16+
17+
import sys
18+
from typing import Any
19+
20+
try:
21+
from a2a.server.agent_execution import RequestContext
22+
except ImportError as e:
23+
if sys.version_info < (3, 10):
24+
raise ImportError(
25+
'A2A Tool requires Python 3.10 or above. Please upgrade your Python'
26+
' version.'
27+
) from e
28+
else:
29+
raise e
30+
31+
from google.genai import types as genai_types
32+
33+
from ...runners import RunConfig
34+
from ...utils.feature_decorator import working_in_progress
35+
from .part_converter import convert_a2a_part_to_genai_part
36+
from .utils import _from_a2a_context_id
37+
from .utils import _get_adk_metadata_key
38+
39+
40+
def _get_user_id(request: RequestContext, user_id_from_context: str) -> str:
41+
# Get user from call context if available (auth is enabled on a2a server)
42+
if request.call_context and request.call_context.user:
43+
return request.call_context.user.user_name
44+
45+
# Get user from context id if available
46+
if user_id_from_context:
47+
return user_id_from_context
48+
49+
# Get user from message metadata if available (client is an ADK agent)
50+
if request.message.metadata:
51+
user_id = request.message.metadata.get(_get_adk_metadata_key('user_id'))
52+
if user_id:
53+
return f'ADK_USER_{user_id}'
54+
55+
# Get user from task if available (client is a an ADK agent)
56+
if request.current_task:
57+
user_id = request.current_task.metadata.get(
58+
_get_adk_metadata_key('user_id')
59+
)
60+
if user_id:
61+
return f'ADK_USER_{user_id}'
62+
return (
63+
f'temp_user_{request.task_id}'
64+
if request.task_id
65+
else f'TEMP_USER_{request.message.messageId}'
66+
)
67+
68+
69+
@working_in_progress
70+
def convert_a2a_request_to_adk_run_args(
71+
request: RequestContext,
72+
) -> dict[str, Any]:
73+
74+
if not request.message:
75+
raise ValueError('Request message cannot be None')
76+
77+
_, user_id, session_id = _from_a2a_context_id(request.context_id)
78+
79+
return {
80+
'user_id': _get_user_id(request, user_id),
81+
'session_id': session_id,
82+
'new_message': genai_types.Content(
83+
role='user',
84+
parts=[
85+
convert_a2a_part_to_genai_part(part)
86+
for part in request.message.parts
87+
],
88+
),
89+
'run_config': RunConfig(),
90+
}

src/google/adk/a2a/converters/utils.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
from __future__ import annotations
1616

1717
ADK_METADATA_KEY_PREFIX = "adk_"
18+
ADK_CONTEXT_ID_PREFIX = "ADK"
1819

1920

2021
def _get_adk_metadata_key(key: str) -> str:
@@ -32,3 +33,39 @@ def _get_adk_metadata_key(key: str) -> str:
3233
if not key:
3334
raise ValueError("Metadata key cannot be empty or None")
3435
return f"{ADK_METADATA_KEY_PREFIX}{key}"
36+
37+
38+
def _to_a2a_context_id(app_name: str, user_id: str, session_id: str) -> str:
39+
"""Converts app name, user id and session id to an A2A context id.
40+
41+
Args:
42+
app_name: The app name.
43+
user_id: The user id.
44+
session_id: The session id.
45+
46+
Returns:
47+
The A2A context id.
48+
"""
49+
return [ADK_CONTEXT_ID_PREFIX, app_name, user_id, session_id].join("$")
50+
51+
52+
def _from_a2a_context_id(context_id: str) -> tuple[str, str, str]:
53+
"""Converts an A2A context id to app name, user id and session id.
54+
if context_id is None, return None, None, None
55+
if context_id is not None, but not in the format of
56+
ADK$app_name$user_id$session_id, return None, None, None
57+
58+
Args:
59+
context_id: The A2A context id.
60+
61+
Returns:
62+
The app name, user id and session id.
63+
"""
64+
if not context_id:
65+
return None, None, None
66+
67+
prefix, app_name, user_id, session_id = context_id.split("$")
68+
if prefix == "ADK" and app_name and user_id and session_id:
69+
return app_name, user_id, session_id
70+
71+
return None, None, None

0 commit comments

Comments
 (0)