google-adk 1.5.0__py3-none-any.whl → 1.6.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- google/adk/a2a/converters/event_converter.py +257 -36
- google/adk/a2a/converters/part_converter.py +93 -25
- google/adk/a2a/converters/request_converter.py +12 -32
- google/adk/a2a/converters/utils.py +22 -4
- google/adk/a2a/executor/__init__.py +13 -0
- google/adk/a2a/executor/a2a_agent_executor.py +260 -0
- google/adk/a2a/executor/task_result_aggregator.py +71 -0
- google/adk/a2a/logs/__init__.py +13 -0
- google/adk/a2a/logs/log_utils.py +349 -0
- google/adk/agents/base_agent.py +54 -0
- google/adk/agents/llm_agent.py +15 -0
- google/adk/agents/remote_a2a_agent.py +532 -0
- google/adk/artifacts/in_memory_artifact_service.py +6 -3
- google/adk/cli/browser/chunk-EQDQRRRY.js +1 -0
- google/adk/cli/browser/chunk-TXJFAAIW.js +2 -0
- google/adk/cli/browser/index.html +4 -3
- google/adk/cli/browser/main-RXDVX3K6.js +3914 -0
- google/adk/cli/browser/polyfills-FFHMD2TL.js +17 -0
- google/adk/cli/cli_deploy.py +4 -1
- google/adk/cli/cli_eval.py +8 -6
- google/adk/cli/cli_tools_click.py +30 -10
- google/adk/cli/fast_api.py +120 -5
- google/adk/cli/utils/agent_loader.py +12 -0
- google/adk/evaluation/agent_evaluator.py +107 -10
- google/adk/evaluation/base_eval_service.py +157 -0
- google/adk/evaluation/constants.py +20 -0
- google/adk/evaluation/eval_case.py +3 -3
- google/adk/evaluation/eval_metrics.py +39 -0
- google/adk/evaluation/evaluation_generator.py +1 -1
- google/adk/evaluation/final_response_match_v2.py +230 -0
- google/adk/evaluation/llm_as_judge.py +141 -0
- google/adk/evaluation/llm_as_judge_utils.py +48 -0
- google/adk/evaluation/metric_evaluator_registry.py +89 -0
- google/adk/evaluation/response_evaluator.py +38 -211
- google/adk/evaluation/safety_evaluator.py +54 -0
- google/adk/evaluation/trajectory_evaluator.py +16 -2
- google/adk/evaluation/vertex_ai_eval_facade.py +147 -0
- google/adk/events/event.py +2 -4
- google/adk/flows/llm_flows/base_llm_flow.py +2 -0
- google/adk/memory/in_memory_memory_service.py +3 -2
- google/adk/models/lite_llm.py +50 -10
- google/adk/runners.py +27 -10
- google/adk/sessions/database_session_service.py +25 -7
- google/adk/sessions/in_memory_session_service.py +5 -1
- google/adk/sessions/vertex_ai_session_service.py +67 -42
- google/adk/tools/bigquery/config.py +11 -1
- google/adk/tools/bigquery/query_tool.py +306 -12
- google/adk/tools/enterprise_search_tool.py +2 -2
- google/adk/tools/function_tool.py +7 -1
- google/adk/tools/google_search_tool.py +1 -1
- google/adk/tools/mcp_tool/mcp_session_manager.py +44 -30
- google/adk/tools/mcp_tool/mcp_tool.py +44 -7
- google/adk/version.py +1 -1
- {google_adk-1.5.0.dist-info → google_adk-1.6.1.dist-info}/METADATA +6 -4
- {google_adk-1.5.0.dist-info → google_adk-1.6.1.dist-info}/RECORD +58 -42
- google/adk/cli/browser/main-JAAWEV7F.js +0 -92
- google/adk/cli/browser/polyfills-B6TNHZQ6.js +0 -17
- {google_adk-1.5.0.dist-info → google_adk-1.6.1.dist-info}/WHEEL +0 -0
- {google_adk-1.5.0.dist-info → google_adk-1.6.1.dist-info}/entry_points.txt +0 -0
- {google_adk-1.5.0.dist-info → google_adk-1.6.1.dist-info}/licenses/LICENSE +0 -0
@@ -31,42 +31,24 @@ except ImportError as e:
|
|
31
31
|
from google.genai import types as genai_types
|
32
32
|
|
33
33
|
from ...runners import RunConfig
|
34
|
-
from ...utils.feature_decorator import
|
34
|
+
from ...utils.feature_decorator import experimental
|
35
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
36
|
|
39
37
|
|
40
|
-
def _get_user_id(request: RequestContext
|
38
|
+
def _get_user_id(request: RequestContext) -> str:
|
41
39
|
# Get user from call context if available (auth is enabled on a2a server)
|
42
|
-
if
|
40
|
+
if (
|
41
|
+
request.call_context
|
42
|
+
and request.call_context.user
|
43
|
+
and request.call_context.user.user_name
|
44
|
+
):
|
43
45
|
return request.call_context.user.user_name
|
44
46
|
|
45
|
-
# Get user from context id
|
46
|
-
|
47
|
-
return user_id_from_context
|
47
|
+
# Get user from context id
|
48
|
+
return f'A2A_USER_{request.context_id}'
|
48
49
|
|
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
50
|
|
55
|
-
|
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
|
51
|
+
@experimental
|
70
52
|
def convert_a2a_request_to_adk_run_args(
|
71
53
|
request: RequestContext,
|
72
54
|
) -> dict[str, Any]:
|
@@ -74,11 +56,9 @@ def convert_a2a_request_to_adk_run_args(
|
|
74
56
|
if not request.message:
|
75
57
|
raise ValueError('Request message cannot be None')
|
76
58
|
|
77
|
-
_, user_id, session_id = _from_a2a_context_id(request.context_id)
|
78
|
-
|
79
59
|
return {
|
80
|
-
'user_id': _get_user_id(request
|
81
|
-
'session_id':
|
60
|
+
'user_id': _get_user_id(request),
|
61
|
+
'session_id': request.context_id,
|
82
62
|
'new_message': genai_types.Content(
|
83
63
|
role='user',
|
84
64
|
parts=[
|
@@ -16,6 +16,7 @@ from __future__ import annotations
|
|
16
16
|
|
17
17
|
ADK_METADATA_KEY_PREFIX = "adk_"
|
18
18
|
ADK_CONTEXT_ID_PREFIX = "ADK"
|
19
|
+
ADK_CONTEXT_ID_SEPARATOR = "/"
|
19
20
|
|
20
21
|
|
21
22
|
def _get_adk_metadata_key(key: str) -> str:
|
@@ -45,8 +46,17 @@ def _to_a2a_context_id(app_name: str, user_id: str, session_id: str) -> str:
|
|
45
46
|
|
46
47
|
Returns:
|
47
48
|
The A2A context id.
|
49
|
+
|
50
|
+
Raises:
|
51
|
+
ValueError: If any of the input parameters are empty or None.
|
48
52
|
"""
|
49
|
-
|
53
|
+
if not all([app_name, user_id, session_id]):
|
54
|
+
raise ValueError(
|
55
|
+
"All parameters (app_name, user_id, session_id) must be non-empty"
|
56
|
+
)
|
57
|
+
return ADK_CONTEXT_ID_SEPARATOR.join(
|
58
|
+
[ADK_CONTEXT_ID_PREFIX, app_name, user_id, session_id]
|
59
|
+
)
|
50
60
|
|
51
61
|
|
52
62
|
def _from_a2a_context_id(context_id: str) -> tuple[str, str, str]:
|
@@ -64,8 +74,16 @@ def _from_a2a_context_id(context_id: str) -> tuple[str, str, str]:
|
|
64
74
|
if not context_id:
|
65
75
|
return None, None, None
|
66
76
|
|
67
|
-
|
68
|
-
|
69
|
-
|
77
|
+
try:
|
78
|
+
parts = context_id.split(ADK_CONTEXT_ID_SEPARATOR)
|
79
|
+
if len(parts) != 4:
|
80
|
+
return None, None, None
|
81
|
+
|
82
|
+
prefix, app_name, user_id, session_id = parts
|
83
|
+
if prefix == ADK_CONTEXT_ID_PREFIX and app_name and user_id and session_id:
|
84
|
+
return app_name, user_id, session_id
|
85
|
+
except ValueError:
|
86
|
+
# Handle any split errors gracefully
|
87
|
+
pass
|
70
88
|
|
71
89
|
return None, None, None
|
@@ -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,0 +1,260 @@
|
|
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
|
+
from datetime import datetime
|
18
|
+
from datetime import timezone
|
19
|
+
import inspect
|
20
|
+
import logging
|
21
|
+
from typing import Any
|
22
|
+
from typing import Awaitable
|
23
|
+
from typing import Callable
|
24
|
+
from typing import Optional
|
25
|
+
import uuid
|
26
|
+
|
27
|
+
try:
|
28
|
+
from a2a.server.agent_execution import AgentExecutor
|
29
|
+
from a2a.server.agent_execution.context import RequestContext
|
30
|
+
from a2a.server.events.event_queue import EventQueue
|
31
|
+
from a2a.types import Message
|
32
|
+
from a2a.types import Role
|
33
|
+
from a2a.types import TaskState
|
34
|
+
from a2a.types import TaskStatus
|
35
|
+
from a2a.types import TaskStatusUpdateEvent
|
36
|
+
from a2a.types import TextPart
|
37
|
+
|
38
|
+
except ImportError as e:
|
39
|
+
import sys
|
40
|
+
|
41
|
+
if sys.version_info < (3, 10):
|
42
|
+
raise ImportError(
|
43
|
+
'A2A requires Python 3.10 or above. Please upgrade your Python version.'
|
44
|
+
) from e
|
45
|
+
else:
|
46
|
+
raise e
|
47
|
+
from google.adk.runners import Runner
|
48
|
+
from pydantic import BaseModel
|
49
|
+
from typing_extensions import override
|
50
|
+
|
51
|
+
from ...utils.feature_decorator import experimental
|
52
|
+
from ..converters.event_converter import convert_event_to_a2a_events
|
53
|
+
from ..converters.request_converter import convert_a2a_request_to_adk_run_args
|
54
|
+
from ..converters.utils import _get_adk_metadata_key
|
55
|
+
from .task_result_aggregator import TaskResultAggregator
|
56
|
+
|
57
|
+
logger = logging.getLogger('google_adk.' + __name__)
|
58
|
+
|
59
|
+
|
60
|
+
@experimental
|
61
|
+
class A2aAgentExecutorConfig(BaseModel):
|
62
|
+
"""Configuration for the A2aAgentExecutor."""
|
63
|
+
|
64
|
+
pass
|
65
|
+
|
66
|
+
|
67
|
+
@experimental
|
68
|
+
class A2aAgentExecutor(AgentExecutor):
|
69
|
+
"""An AgentExecutor that runs an ADK Agent against an A2A request and
|
70
|
+
publishes updates to an event queue.
|
71
|
+
"""
|
72
|
+
|
73
|
+
def __init__(
|
74
|
+
self,
|
75
|
+
*,
|
76
|
+
runner: Runner | Callable[..., Runner | Awaitable[Runner]],
|
77
|
+
config: Optional[A2aAgentExecutorConfig] = None,
|
78
|
+
):
|
79
|
+
super().__init__()
|
80
|
+
self._runner = runner
|
81
|
+
self._config = config
|
82
|
+
|
83
|
+
async def _resolve_runner(self) -> Runner:
|
84
|
+
"""Resolve the runner, handling cases where it's a callable that returns a Runner."""
|
85
|
+
# If already resolved and cached, return it
|
86
|
+
if isinstance(self._runner, Runner):
|
87
|
+
return self._runner
|
88
|
+
if callable(self._runner):
|
89
|
+
# Call the function to get the runner
|
90
|
+
result = self._runner()
|
91
|
+
|
92
|
+
# Handle async callables
|
93
|
+
if inspect.iscoroutine(result):
|
94
|
+
resolved_runner = await result
|
95
|
+
else:
|
96
|
+
resolved_runner = result
|
97
|
+
|
98
|
+
# Cache the resolved runner for future calls
|
99
|
+
self._runner = resolved_runner
|
100
|
+
return resolved_runner
|
101
|
+
|
102
|
+
raise TypeError(
|
103
|
+
'Runner must be a Runner instance or a callable that returns a'
|
104
|
+
f' Runner, got {type(self._runner)}'
|
105
|
+
)
|
106
|
+
|
107
|
+
@override
|
108
|
+
async def cancel(self, context: RequestContext, event_queue: EventQueue):
|
109
|
+
"""Cancel the execution."""
|
110
|
+
# TODO: Implement proper cancellation logic if needed
|
111
|
+
raise NotImplementedError('Cancellation is not supported')
|
112
|
+
|
113
|
+
@override
|
114
|
+
async def execute(
|
115
|
+
self,
|
116
|
+
context: RequestContext,
|
117
|
+
event_queue: EventQueue,
|
118
|
+
):
|
119
|
+
"""Executes an A2A request and publishes updates to the event queue
|
120
|
+
specified. It runs as following:
|
121
|
+
* Takes the input from the A2A request
|
122
|
+
* Convert the input to ADK input content, and runs the ADK agent
|
123
|
+
* Collects output events of the underlying ADK Agent
|
124
|
+
* Converts the ADK output events into A2A task updates
|
125
|
+
* Publishes the updates back to A2A server via event queue
|
126
|
+
"""
|
127
|
+
if not context.message:
|
128
|
+
raise ValueError('A2A request must have a message')
|
129
|
+
|
130
|
+
# for new task, create a task submitted event
|
131
|
+
if not context.current_task:
|
132
|
+
await event_queue.enqueue_event(
|
133
|
+
TaskStatusUpdateEvent(
|
134
|
+
taskId=context.task_id,
|
135
|
+
status=TaskStatus(
|
136
|
+
state=TaskState.submitted,
|
137
|
+
message=context.message,
|
138
|
+
timestamp=datetime.now(timezone.utc).isoformat(),
|
139
|
+
),
|
140
|
+
contextId=context.context_id,
|
141
|
+
final=False,
|
142
|
+
)
|
143
|
+
)
|
144
|
+
|
145
|
+
# Handle the request and publish updates to the event queue
|
146
|
+
try:
|
147
|
+
await self._handle_request(context, event_queue)
|
148
|
+
except Exception as e:
|
149
|
+
logger.error('Error handling A2A request: %s', e, exc_info=True)
|
150
|
+
# Publish failure event
|
151
|
+
try:
|
152
|
+
await event_queue.enqueue_event(
|
153
|
+
TaskStatusUpdateEvent(
|
154
|
+
taskId=context.task_id,
|
155
|
+
status=TaskStatus(
|
156
|
+
state=TaskState.failed,
|
157
|
+
timestamp=datetime.now(timezone.utc).isoformat(),
|
158
|
+
message=Message(
|
159
|
+
messageId=str(uuid.uuid4()),
|
160
|
+
role=Role.agent,
|
161
|
+
parts=[TextPart(text=str(e))],
|
162
|
+
),
|
163
|
+
),
|
164
|
+
contextId=context.context_id,
|
165
|
+
final=True,
|
166
|
+
)
|
167
|
+
)
|
168
|
+
except Exception as enqueue_error:
|
169
|
+
logger.error(
|
170
|
+
'Failed to publish failure event: %s', enqueue_error, exc_info=True
|
171
|
+
)
|
172
|
+
|
173
|
+
async def _handle_request(
|
174
|
+
self,
|
175
|
+
context: RequestContext,
|
176
|
+
event_queue: EventQueue,
|
177
|
+
):
|
178
|
+
# Resolve the runner instance
|
179
|
+
runner = await self._resolve_runner()
|
180
|
+
|
181
|
+
# Convert the a2a request to ADK run args
|
182
|
+
run_args = convert_a2a_request_to_adk_run_args(context)
|
183
|
+
|
184
|
+
# ensure the session exists
|
185
|
+
session = await self._prepare_session(context, run_args, runner)
|
186
|
+
|
187
|
+
# create invocation context
|
188
|
+
invocation_context = runner._new_invocation_context(
|
189
|
+
session=session,
|
190
|
+
new_message=run_args['new_message'],
|
191
|
+
run_config=run_args['run_config'],
|
192
|
+
)
|
193
|
+
|
194
|
+
# publish the task working event
|
195
|
+
await event_queue.enqueue_event(
|
196
|
+
TaskStatusUpdateEvent(
|
197
|
+
taskId=context.task_id,
|
198
|
+
status=TaskStatus(
|
199
|
+
state=TaskState.working,
|
200
|
+
timestamp=datetime.now(timezone.utc).isoformat(),
|
201
|
+
),
|
202
|
+
contextId=context.context_id,
|
203
|
+
final=False,
|
204
|
+
metadata={
|
205
|
+
_get_adk_metadata_key('app_name'): runner.app_name,
|
206
|
+
_get_adk_metadata_key('user_id'): run_args['user_id'],
|
207
|
+
_get_adk_metadata_key('session_id'): run_args['session_id'],
|
208
|
+
},
|
209
|
+
)
|
210
|
+
)
|
211
|
+
|
212
|
+
task_result_aggregator = TaskResultAggregator()
|
213
|
+
async for adk_event in runner.run_async(**run_args):
|
214
|
+
for a2a_event in convert_event_to_a2a_events(
|
215
|
+
adk_event, invocation_context, context.task_id, context.context_id
|
216
|
+
):
|
217
|
+
task_result_aggregator.process_event(a2a_event)
|
218
|
+
await event_queue.enqueue_event(a2a_event)
|
219
|
+
|
220
|
+
# publish the task result event - this is final
|
221
|
+
await event_queue.enqueue_event(
|
222
|
+
TaskStatusUpdateEvent(
|
223
|
+
taskId=context.task_id,
|
224
|
+
status=TaskStatus(
|
225
|
+
state=(
|
226
|
+
task_result_aggregator.task_state
|
227
|
+
if task_result_aggregator.task_state != TaskState.working
|
228
|
+
else TaskState.completed
|
229
|
+
),
|
230
|
+
timestamp=datetime.now(timezone.utc).isoformat(),
|
231
|
+
message=task_result_aggregator.task_status_message,
|
232
|
+
),
|
233
|
+
contextId=context.context_id,
|
234
|
+
final=True,
|
235
|
+
)
|
236
|
+
)
|
237
|
+
|
238
|
+
async def _prepare_session(
|
239
|
+
self, context: RequestContext, run_args: dict[str, Any], runner: Runner
|
240
|
+
):
|
241
|
+
|
242
|
+
session_id = run_args['session_id']
|
243
|
+
# create a new session if not exists
|
244
|
+
user_id = run_args['user_id']
|
245
|
+
session = await runner.session_service.get_session(
|
246
|
+
app_name=runner.app_name,
|
247
|
+
user_id=user_id,
|
248
|
+
session_id=session_id,
|
249
|
+
)
|
250
|
+
if session is None:
|
251
|
+
session = await runner.session_service.create_session(
|
252
|
+
app_name=runner.app_name,
|
253
|
+
user_id=user_id,
|
254
|
+
state={},
|
255
|
+
session_id=session_id,
|
256
|
+
)
|
257
|
+
# Update run_args with the new session_id
|
258
|
+
run_args['session_id'] = session.id
|
259
|
+
|
260
|
+
return session
|
@@ -0,0 +1,71 @@
|
|
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
|
+
from a2a.server.events import Event
|
18
|
+
from a2a.types import Message
|
19
|
+
from a2a.types import TaskState
|
20
|
+
from a2a.types import TaskStatusUpdateEvent
|
21
|
+
|
22
|
+
from ...utils.feature_decorator import experimental
|
23
|
+
|
24
|
+
|
25
|
+
@experimental
|
26
|
+
class TaskResultAggregator:
|
27
|
+
"""Aggregates the task status updates and provides the final task state."""
|
28
|
+
|
29
|
+
def __init__(self):
|
30
|
+
self._task_state = TaskState.working
|
31
|
+
self._task_status_message = None
|
32
|
+
|
33
|
+
def process_event(self, event: Event):
|
34
|
+
"""Process an event from the agent run and detect signals about the task status.
|
35
|
+
Priority of task state:
|
36
|
+
- failed
|
37
|
+
- auth_required
|
38
|
+
- input_required
|
39
|
+
- working
|
40
|
+
"""
|
41
|
+
if isinstance(event, TaskStatusUpdateEvent):
|
42
|
+
if event.status.state == TaskState.failed:
|
43
|
+
self._task_state = TaskState.failed
|
44
|
+
self._task_status_message = event.status.message
|
45
|
+
elif (
|
46
|
+
event.status.state == TaskState.auth_required
|
47
|
+
and self._task_state != TaskState.failed
|
48
|
+
):
|
49
|
+
self._task_state = TaskState.auth_required
|
50
|
+
self._task_status_message = event.status.message
|
51
|
+
elif (
|
52
|
+
event.status.state == TaskState.input_required
|
53
|
+
and self._task_state
|
54
|
+
not in (TaskState.failed, TaskState.auth_required)
|
55
|
+
):
|
56
|
+
self._task_state = TaskState.input_required
|
57
|
+
self._task_status_message = event.status.message
|
58
|
+
# final state is already recorded and make sure the intermediate state is
|
59
|
+
# always working because other state may terminate the event aggregation
|
60
|
+
# in a2a request handler
|
61
|
+
elif self._task_state == TaskState.working:
|
62
|
+
self._task_status_message = event.status.message
|
63
|
+
event.status.state = TaskState.working
|
64
|
+
|
65
|
+
@property
|
66
|
+
def task_state(self) -> TaskState:
|
67
|
+
return self._task_state
|
68
|
+
|
69
|
+
@property
|
70
|
+
def task_status_message(self) -> Message | None:
|
71
|
+
return self._task_status_message
|
@@ -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.
|