google-adk 0.4.0__py3-none-any.whl → 1.0.0__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.
Files changed (129) hide show
  1. google/adk/agents/active_streaming_tool.py +1 -0
  2. google/adk/agents/base_agent.py +91 -47
  3. google/adk/agents/base_agent.py.orig +330 -0
  4. google/adk/agents/callback_context.py +4 -9
  5. google/adk/agents/invocation_context.py +1 -0
  6. google/adk/agents/langgraph_agent.py +1 -0
  7. google/adk/agents/live_request_queue.py +1 -0
  8. google/adk/agents/llm_agent.py +172 -35
  9. google/adk/agents/loop_agent.py +1 -1
  10. google/adk/agents/parallel_agent.py +7 -0
  11. google/adk/agents/readonly_context.py +7 -1
  12. google/adk/agents/run_config.py +5 -1
  13. google/adk/agents/sequential_agent.py +31 -0
  14. google/adk/agents/transcription_entry.py +5 -2
  15. google/adk/artifacts/base_artifact_service.py +5 -10
  16. google/adk/artifacts/gcs_artifact_service.py +9 -9
  17. google/adk/artifacts/in_memory_artifact_service.py +6 -6
  18. google/adk/auth/auth_credential.py +9 -5
  19. google/adk/auth/auth_preprocessor.py +7 -1
  20. google/adk/auth/auth_tool.py +3 -4
  21. google/adk/cli/agent_graph.py +5 -5
  22. google/adk/cli/browser/index.html +2 -2
  23. google/adk/cli/browser/{main-HWIBUY2R.js → main-QOEMUXM4.js} +58 -58
  24. google/adk/cli/cli.py +7 -7
  25. google/adk/cli/cli_deploy.py +7 -2
  26. google/adk/cli/cli_eval.py +181 -106
  27. google/adk/cli/cli_tools_click.py +147 -62
  28. google/adk/cli/fast_api.py +340 -158
  29. google/adk/cli/fast_api.py.orig +822 -0
  30. google/adk/cli/utils/common.py +23 -0
  31. google/adk/cli/utils/evals.py +83 -1
  32. google/adk/cli/utils/logs.py +13 -5
  33. google/adk/code_executors/__init__.py +3 -1
  34. google/adk/code_executors/built_in_code_executor.py +52 -0
  35. google/adk/evaluation/__init__.py +1 -1
  36. google/adk/evaluation/agent_evaluator.py +168 -128
  37. google/adk/evaluation/eval_case.py +102 -0
  38. google/adk/evaluation/eval_set.py +37 -0
  39. google/adk/evaluation/eval_sets_manager.py +42 -0
  40. google/adk/evaluation/evaluation_constants.py +1 -0
  41. google/adk/evaluation/evaluation_generator.py +89 -114
  42. google/adk/evaluation/evaluator.py +56 -0
  43. google/adk/evaluation/local_eval_sets_manager.py +264 -0
  44. google/adk/evaluation/response_evaluator.py +107 -3
  45. google/adk/evaluation/trajectory_evaluator.py +83 -2
  46. google/adk/events/event.py +7 -1
  47. google/adk/events/event_actions.py +7 -1
  48. google/adk/examples/example.py +1 -0
  49. google/adk/examples/example_util.py +3 -2
  50. google/adk/flows/__init__.py +0 -1
  51. google/adk/flows/llm_flows/_code_execution.py +19 -11
  52. google/adk/flows/llm_flows/audio_transcriber.py +4 -3
  53. google/adk/flows/llm_flows/base_llm_flow.py +86 -22
  54. google/adk/flows/llm_flows/basic.py +3 -0
  55. google/adk/flows/llm_flows/functions.py +10 -9
  56. google/adk/flows/llm_flows/instructions.py +28 -9
  57. google/adk/flows/llm_flows/single_flow.py +1 -1
  58. google/adk/memory/__init__.py +1 -1
  59. google/adk/memory/_utils.py +23 -0
  60. google/adk/memory/base_memory_service.py +25 -21
  61. google/adk/memory/base_memory_service.py.orig +76 -0
  62. google/adk/memory/in_memory_memory_service.py +59 -27
  63. google/adk/memory/memory_entry.py +37 -0
  64. google/adk/memory/vertex_ai_rag_memory_service.py +40 -17
  65. google/adk/models/anthropic_llm.py +36 -11
  66. google/adk/models/base_llm.py +45 -4
  67. google/adk/models/gemini_llm_connection.py +15 -2
  68. google/adk/models/google_llm.py +9 -44
  69. google/adk/models/google_llm.py.orig +305 -0
  70. google/adk/models/lite_llm.py +94 -38
  71. google/adk/models/llm_request.py +1 -1
  72. google/adk/models/llm_response.py +15 -3
  73. google/adk/models/registry.py +1 -1
  74. google/adk/runners.py +68 -44
  75. google/adk/sessions/__init__.py +1 -1
  76. google/adk/sessions/_session_util.py +14 -0
  77. google/adk/sessions/base_session_service.py +8 -32
  78. google/adk/sessions/database_session_service.py +58 -61
  79. google/adk/sessions/in_memory_session_service.py +108 -26
  80. google/adk/sessions/session.py +4 -0
  81. google/adk/sessions/vertex_ai_session_service.py +23 -45
  82. google/adk/telemetry.py +3 -0
  83. google/adk/tools/__init__.py +4 -7
  84. google/adk/tools/{built_in_code_execution_tool.py → _built_in_code_execution_tool.py} +11 -0
  85. google/adk/tools/_memory_entry_utils.py +30 -0
  86. google/adk/tools/agent_tool.py +16 -13
  87. google/adk/tools/apihub_tool/apihub_toolset.py +55 -74
  88. google/adk/tools/application_integration_tool/application_integration_toolset.py +107 -85
  89. google/adk/tools/application_integration_tool/clients/connections_client.py +29 -25
  90. google/adk/tools/application_integration_tool/clients/integration_client.py +6 -6
  91. google/adk/tools/application_integration_tool/integration_connector_tool.py +69 -26
  92. google/adk/tools/base_toolset.py +58 -0
  93. google/adk/tools/enterprise_search_tool.py +65 -0
  94. google/adk/tools/function_parameter_parse_util.py +2 -2
  95. google/adk/tools/google_api_tool/__init__.py +18 -70
  96. google/adk/tools/google_api_tool/google_api_tool.py +11 -5
  97. google/adk/tools/google_api_tool/google_api_toolset.py +126 -0
  98. google/adk/tools/google_api_tool/google_api_toolsets.py +102 -0
  99. google/adk/tools/google_api_tool/googleapi_to_openapi_converter.py +40 -42
  100. google/adk/tools/langchain_tool.py +96 -49
  101. google/adk/tools/load_artifacts_tool.py +4 -4
  102. google/adk/tools/load_memory_tool.py +16 -5
  103. google/adk/tools/mcp_tool/__init__.py +3 -2
  104. google/adk/tools/mcp_tool/conversion_utils.py +1 -1
  105. google/adk/tools/mcp_tool/mcp_session_manager.py +167 -16
  106. google/adk/tools/mcp_tool/mcp_session_manager.py.orig +322 -0
  107. google/adk/tools/mcp_tool/mcp_tool.py +12 -12
  108. google/adk/tools/mcp_tool/mcp_toolset.py +155 -195
  109. google/adk/tools/openapi_tool/common/common.py +2 -5
  110. google/adk/tools/openapi_tool/openapi_spec_parser/openapi_toolset.py +32 -7
  111. google/adk/tools/openapi_tool/openapi_spec_parser/operation_parser.py +43 -33
  112. google/adk/tools/openapi_tool/openapi_spec_parser/tool_auth_handler.py +1 -1
  113. google/adk/tools/preload_memory_tool.py +27 -18
  114. google/adk/tools/retrieval/__init__.py +1 -1
  115. google/adk/tools/retrieval/vertex_ai_rag_retrieval.py +1 -1
  116. google/adk/tools/tool_context.py +4 -4
  117. google/adk/tools/toolbox_toolset.py +79 -0
  118. google/adk/tools/transfer_to_agent_tool.py +0 -1
  119. google/adk/version.py +1 -1
  120. {google_adk-0.4.0.dist-info → google_adk-1.0.0.dist-info}/METADATA +7 -5
  121. google_adk-1.0.0.dist-info/RECORD +195 -0
  122. google/adk/agents/remote_agent.py +0 -50
  123. google/adk/tools/google_api_tool/google_api_tool_set.py +0 -110
  124. google/adk/tools/google_api_tool/google_api_tool_sets.py +0 -112
  125. google/adk/tools/toolbox_tool.py +0 -46
  126. google_adk-0.4.0.dist-info/RECORD +0 -179
  127. {google_adk-0.4.0.dist-info → google_adk-1.0.0.dist-info}/WHEEL +0 -0
  128. {google_adk-0.4.0.dist-info → google_adk-1.0.0.dist-info}/entry_points.txt +0 -0
  129. {google_adk-0.4.0.dist-info → google_adk-1.0.0.dist-info}/licenses/LICENSE +0 -0
@@ -30,6 +30,7 @@ class ActiveStreamingTool(BaseModel):
30
30
  arbitrary_types_allowed=True,
31
31
  extra='forbid',
32
32
  )
33
+ """The pydantic model config."""
33
34
 
34
35
  task: Optional[asyncio.Task] = None
35
36
  """The active task of this streaming tool."""
@@ -14,12 +14,15 @@
14
14
 
15
15
  from __future__ import annotations
16
16
 
17
+ import inspect
17
18
  from typing import Any
18
19
  from typing import AsyncGenerator
20
+ from typing import Awaitable
19
21
  from typing import Callable
20
22
  from typing import final
21
23
  from typing import Optional
22
24
  from typing import TYPE_CHECKING
25
+ from typing import Union
23
26
 
24
27
  from google.genai import types
25
28
  from opentelemetry import trace
@@ -28,6 +31,7 @@ from pydantic import ConfigDict
28
31
  from pydantic import Field
29
32
  from pydantic import field_validator
30
33
  from typing_extensions import override
34
+ from typing_extensions import TypeAlias
31
35
 
32
36
  from ..events.event import Event
33
37
  from .callback_context import CallbackContext
@@ -37,27 +41,20 @@ if TYPE_CHECKING:
37
41
 
38
42
  tracer = trace.get_tracer('gcp.vertex.agent')
39
43
 
40
- BeforeAgentCallback = Callable[[CallbackContext], Optional[types.Content]]
41
- """Callback signature that is invoked before the agent run.
44
+ _SingleAgentCallback: TypeAlias = Callable[
45
+ [CallbackContext],
46
+ Union[Awaitable[Optional[types.Content]], Optional[types.Content]],
47
+ ]
42
48
 
43
- Args:
44
- callback_context: MUST be named 'callback_context' (enforced).
49
+ BeforeAgentCallback: TypeAlias = Union[
50
+ _SingleAgentCallback,
51
+ list[_SingleAgentCallback],
52
+ ]
45
53
 
46
- Returns:
47
- The content to return to the user. When set, the agent run will be skipped and
48
- the provided content will be returned to user.
49
- """
50
-
51
- AfterAgentCallback = Callable[[CallbackContext], Optional[types.Content]]
52
- """Callback signature that is invoked after the agent run.
53
-
54
- Args:
55
- callback_context: MUST be named 'callback_context' (enforced).
56
-
57
- Returns:
58
- The content to return to the user. When set, the provided content will be
59
- appended to event history as agent response.
60
- """
54
+ AfterAgentCallback: TypeAlias = Union[
55
+ _SingleAgentCallback,
56
+ list[_SingleAgentCallback],
57
+ ]
61
58
 
62
59
 
63
60
  class BaseAgent(BaseModel):
@@ -67,6 +64,7 @@ class BaseAgent(BaseModel):
67
64
  arbitrary_types_allowed=True,
68
65
  extra='forbid',
69
66
  )
67
+ """The pydantic model config."""
70
68
 
71
69
  name: str
72
70
  """The agent's name.
@@ -95,24 +93,32 @@ class BaseAgent(BaseModel):
95
93
  """The sub-agents of this agent."""
96
94
 
97
95
  before_agent_callback: Optional[BeforeAgentCallback] = None
98
- """Callback signature that is invoked before the agent run.
96
+ """Callback or list of callbacks to be invoked before the agent run.
97
+
98
+ When a list of callbacks is provided, the callbacks will be called in the
99
+ order they are listed until a callback does not return None.
99
100
 
100
101
  Args:
101
102
  callback_context: MUST be named 'callback_context' (enforced).
102
103
 
103
104
  Returns:
104
- The content to return to the user. When set, the agent run will be skipped
105
- and the provided content will be returned to user.
105
+ Optional[types.Content]: The content to return to the user.
106
+ When the content is present, the agent run will be skipped and the
107
+ provided content will be returned to user.
106
108
  """
107
109
  after_agent_callback: Optional[AfterAgentCallback] = None
108
- """Callback signature that is invoked after the agent run.
110
+ """Callback or list of callbacks to be invoked after the agent run.
111
+
112
+ When a list of callbacks is provided, the callbacks will be called in the
113
+ order they are listed until a callback does not return None.
109
114
 
110
115
  Args:
111
116
  callback_context: MUST be named 'callback_context' (enforced).
112
117
 
113
118
  Returns:
114
- The content to return to the user. When set, the provided content will be
115
- appended to event history as agent response.
119
+ Optional[types.Content]: The content to return to the user.
120
+ When the content is present, the provided content will be used as agent
121
+ response and appended to event history as agent response.
116
122
  """
117
123
 
118
124
  @final
@@ -133,7 +139,7 @@ class BaseAgent(BaseModel):
133
139
  with tracer.start_as_current_span(f'agent_run [{self.name}]'):
134
140
  ctx = self._create_invocation_context(parent_context)
135
141
 
136
- if event := self.__handle_before_agent_callback(ctx):
142
+ if event := await self.__handle_before_agent_callback(ctx):
137
143
  yield event
138
144
  if ctx.end_invocation:
139
145
  return
@@ -144,7 +150,7 @@ class BaseAgent(BaseModel):
144
150
  if ctx.end_invocation:
145
151
  return
146
152
 
147
- if event := self.__handle_after_agent_callback(ctx):
153
+ if event := await self.__handle_after_agent_callback(ctx):
148
154
  yield event
149
155
 
150
156
  @final
@@ -244,7 +250,31 @@ class BaseAgent(BaseModel):
244
250
  invocation_context.branch = f'{parent_context.branch}.{self.name}'
245
251
  return invocation_context
246
252
 
247
- def __handle_before_agent_callback(
253
+ @property
254
+ def canonical_before_agent_callbacks(self) -> list[_SingleAgentCallback]:
255
+ """The resolved self.before_agent_callback field as a list of _SingleAgentCallback.
256
+
257
+ This method is only for use by Agent Development Kit.
258
+ """
259
+ if not self.before_agent_callback:
260
+ return []
261
+ if isinstance(self.before_agent_callback, list):
262
+ return self.before_agent_callback
263
+ return [self.before_agent_callback]
264
+
265
+ @property
266
+ def canonical_after_agent_callbacks(self) -> list[_SingleAgentCallback]:
267
+ """The resolved self.after_agent_callback field as a list of _SingleAgentCallback.
268
+
269
+ This method is only for use by Agent Development Kit.
270
+ """
271
+ if not self.after_agent_callback:
272
+ return []
273
+ if isinstance(self.after_agent_callback, list):
274
+ return self.after_agent_callback
275
+ return [self.after_agent_callback]
276
+
277
+ async def __handle_before_agent_callback(
248
278
  self, ctx: InvocationContext
249
279
  ) -> Optional[Event]:
250
280
  """Runs the before_agent_callback if it exists.
@@ -254,24 +284,27 @@ class BaseAgent(BaseModel):
254
284
  """
255
285
  ret_event = None
256
286
 
257
- if not isinstance(self.before_agent_callback, Callable):
287
+ if not self.canonical_before_agent_callbacks:
258
288
  return ret_event
259
289
 
260
290
  callback_context = CallbackContext(ctx)
261
- before_agent_callback_content = self.before_agent_callback(
262
- callback_context=callback_context
263
- )
264
291
 
265
- if before_agent_callback_content:
266
- ret_event = Event(
267
- invocation_id=ctx.invocation_id,
268
- author=self.name,
269
- branch=ctx.branch,
270
- content=before_agent_callback_content,
271
- actions=callback_context._event_actions,
292
+ for callback in self.canonical_before_agent_callbacks:
293
+ before_agent_callback_content = callback(
294
+ callback_context=callback_context
272
295
  )
273
- ctx.end_invocation = True
274
- return ret_event
296
+ if inspect.isawaitable(before_agent_callback_content):
297
+ before_agent_callback_content = await before_agent_callback_content
298
+ if before_agent_callback_content:
299
+ ret_event = Event(
300
+ invocation_id=ctx.invocation_id,
301
+ author=self.name,
302
+ branch=ctx.branch,
303
+ content=before_agent_callback_content,
304
+ actions=callback_context._event_actions,
305
+ )
306
+ ctx.end_invocation = True
307
+ return ret_event
275
308
 
276
309
  if callback_context.state.has_delta():
277
310
  ret_event = Event(
@@ -283,7 +316,7 @@ class BaseAgent(BaseModel):
283
316
 
284
317
  return ret_event
285
318
 
286
- def __handle_after_agent_callback(
319
+ async def __handle_after_agent_callback(
287
320
  self, invocation_context: InvocationContext
288
321
  ) -> Optional[Event]:
289
322
  """Runs the after_agent_callback if it exists.
@@ -293,15 +326,26 @@ class BaseAgent(BaseModel):
293
326
  """
294
327
  ret_event = None
295
328
 
296
- if not isinstance(self.after_agent_callback, Callable):
329
+ if not self.canonical_after_agent_callbacks:
297
330
  return ret_event
298
331
 
299
332
  callback_context = CallbackContext(invocation_context)
300
- after_agent_callback_content = self.after_agent_callback(
301
- callback_context=callback_context
302
- )
303
333
 
304
- if after_agent_callback_content or callback_context.state.has_delta():
334
+ for callback in self.canonical_after_agent_callbacks:
335
+ after_agent_callback_content = callback(callback_context=callback_context)
336
+ if inspect.isawaitable(after_agent_callback_content):
337
+ after_agent_callback_content = await after_agent_callback_content
338
+ if after_agent_callback_content:
339
+ ret_event = Event(
340
+ invocation_id=invocation_context.invocation_id,
341
+ author=self.name,
342
+ branch=invocation_context.branch,
343
+ content=after_agent_callback_content,
344
+ actions=callback_context._event_actions,
345
+ )
346
+ return ret_event
347
+
348
+ if callback_context.state.has_delta():
305
349
  ret_event = Event(
306
350
  invocation_id=invocation_context.invocation_id,
307
351
  author=self.name,
@@ -0,0 +1,330 @@
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 typing import Any
18
+ from typing import AsyncGenerator
19
+ from typing import Callable
20
+ from typing import final
21
+ from typing import Optional
22
+ from typing import TYPE_CHECKING
23
+
24
+ from google.genai import types
25
+ from opentelemetry import trace
26
+ from pydantic import BaseModel
27
+ from pydantic import ConfigDict
28
+ from pydantic import Field
29
+ from pydantic import field_validator
30
+ from typing_extensions import override
31
+
32
+ from ..events.event import Event
33
+ from .callback_context import CallbackContext
34
+
35
+ if TYPE_CHECKING:
36
+ from .invocation_context import InvocationContext
37
+
38
+ tracer = trace.get_tracer('gcp.vertex.agent')
39
+
40
+ BeforeAgentCallback = Callable[[CallbackContext], Optional[types.Content]]
41
+
42
+
43
+ AfterAgentCallback = Callable[[CallbackContext], Optional[types.Content]]
44
+
45
+
46
+ class BaseAgent(BaseModel):
47
+ """Base class for all agents in Agent Development Kit."""
48
+
49
+ model_config = ConfigDict(
50
+ arbitrary_types_allowed=True,
51
+ extra='forbid',
52
+ )
53
+
54
+ name: str
55
+ """The agent's name.
56
+
57
+ Agent name must be a Python identifier and unique within the agent tree.
58
+ Agent name cannot be "user", since it's reserved for end-user's input.
59
+ """
60
+
61
+ description: str = ''
62
+ """Description about the agent's capability.
63
+
64
+ The model uses this to determine whether to delegate control to the agent.
65
+ One-line description is enough and preferred.
66
+ """
67
+
68
+ parent_agent: Optional[BaseAgent] = Field(default=None, init=False)
69
+ """The parent agent of this agent.
70
+
71
+ Note that an agent can ONLY be added as sub-agent once.
72
+
73
+ If you want to add one agent twice as sub-agent, consider to create two agent
74
+ instances with identical config, but with different name and add them to the
75
+ agent tree.
76
+ """
77
+ sub_agents: list[BaseAgent] = Field(default_factory=list)
78
+ """The sub-agents of this agent."""
79
+
80
+ before_agent_callback: Optional[BeforeAgentCallback] = None
81
+ """Callback signature that is invoked before the agent run.
82
+
83
+ Args:
84
+ callback_context: MUST be named 'callback_context' (enforced).
85
+
86
+ Returns:
87
+ Optional[types.Content]: The content to return to the user.
88
+ When the content is present, the agent run will be skipped and the
89
+ provided content will be returned to user.
90
+ """
91
+ after_agent_callback: Optional[AfterAgentCallback] = None
92
+ """Callback signature that is invoked after the agent run.
93
+
94
+ Args:
95
+ callback_context: MUST be named 'callback_context' (enforced).
96
+
97
+ Returns:
98
+ Optional[types.Content]: The content to return to the user.
99
+ When the content is present, the provided content will be used as agent
100
+ response and appended to event history as agent response.
101
+ """
102
+
103
+ @final
104
+ async def run_async(
105
+ self,
106
+ parent_context: InvocationContext,
107
+ ) -> AsyncGenerator[Event, None]:
108
+ """Entry method to run an agent via text-based conversation.
109
+
110
+ Args:
111
+ parent_context: InvocationContext, the invocation context of the parent
112
+ agent.
113
+
114
+ Yields:
115
+ Event: the events generated by the agent.
116
+ """
117
+
118
+ with tracer.start_as_current_span(f'agent_run [{self.name}]'):
119
+ ctx = self._create_invocation_context(parent_context)
120
+
121
+ if event := self.__handle_before_agent_callback(ctx):
122
+ yield event
123
+ if ctx.end_invocation:
124
+ return
125
+
126
+ async for event in self._run_async_impl(ctx):
127
+ yield event
128
+
129
+ if ctx.end_invocation:
130
+ return
131
+
132
+ if event := self.__handle_after_agent_callback(ctx):
133
+ yield event
134
+
135
+ @final
136
+ async def run_live(
137
+ self,
138
+ parent_context: InvocationContext,
139
+ ) -> AsyncGenerator[Event, None]:
140
+ """Entry method to run an agent via video/audio-based conversation.
141
+
142
+ Args:
143
+ parent_context: InvocationContext, the invocation context of the parent
144
+ agent.
145
+
146
+ Yields:
147
+ Event: the events generated by the agent.
148
+ """
149
+ with tracer.start_as_current_span(f'agent_run [{self.name}]'):
150
+ ctx = self._create_invocation_context(parent_context)
151
+ # TODO(hangfei): support before/after_agent_callback
152
+
153
+ async for event in self._run_live_impl(ctx):
154
+ yield event
155
+
156
+ async def _run_async_impl(
157
+ self, ctx: InvocationContext
158
+ ) -> AsyncGenerator[Event, None]:
159
+ """Core logic to run this agent via text-based conversation.
160
+
161
+ Args:
162
+ ctx: InvocationContext, the invocation context for this agent.
163
+
164
+ Yields:
165
+ Event: the events generated by the agent.
166
+ """
167
+ raise NotImplementedError(
168
+ f'_run_async_impl for {type(self)} is not implemented.'
169
+ )
170
+ yield # AsyncGenerator requires having at least one yield statement
171
+
172
+ async def _run_live_impl(
173
+ self, ctx: InvocationContext
174
+ ) -> AsyncGenerator[Event, None]:
175
+ """Core logic to run this agent via video/audio-based conversation.
176
+
177
+ Args:
178
+ ctx: InvocationContext, the invocation context for this agent.
179
+
180
+ Yields:
181
+ Event: the events generated by the agent.
182
+ """
183
+ raise NotImplementedError(
184
+ f'_run_live_impl for {type(self)} is not implemented.'
185
+ )
186
+ yield # AsyncGenerator requires having at least one yield statement
187
+
188
+ @property
189
+ def root_agent(self) -> BaseAgent:
190
+ """Gets the root agent of this agent."""
191
+ root_agent = self
192
+ while root_agent.parent_agent is not None:
193
+ root_agent = root_agent.parent_agent
194
+ return root_agent
195
+
196
+ def find_agent(self, name: str) -> Optional[BaseAgent]:
197
+ """Finds the agent with the given name in this agent and its descendants.
198
+
199
+ Args:
200
+ name: The name of the agent to find.
201
+
202
+ Returns:
203
+ The agent with the matching name, or None if no such agent is found.
204
+ """
205
+ if self.name == name:
206
+ return self
207
+ return self.find_sub_agent(name)
208
+
209
+ def find_sub_agent(self, name: str) -> Optional[BaseAgent]:
210
+ """Finds the agent with the given name in this agent's descendants.
211
+
212
+ Args:
213
+ name: The name of the agent to find.
214
+
215
+ Returns:
216
+ The agent with the matching name, or None if no such agent is found.
217
+ """
218
+ for sub_agent in self.sub_agents:
219
+ if result := sub_agent.find_agent(name):
220
+ return result
221
+ return None
222
+
223
+ def _create_invocation_context(
224
+ self, parent_context: InvocationContext
225
+ ) -> InvocationContext:
226
+ """Creates a new invocation context for this agent."""
227
+ invocation_context = parent_context.model_copy(update={'agent': self})
228
+ if parent_context.branch:
229
+ invocation_context.branch = f'{parent_context.branch}.{self.name}'
230
+ return invocation_context
231
+
232
+ def __handle_before_agent_callback(
233
+ self, ctx: InvocationContext
234
+ ) -> Optional[Event]:
235
+ """Runs the before_agent_callback if it exists.
236
+
237
+ Returns:
238
+ Optional[Event]: an event if callback provides content or changed state.
239
+ """
240
+ ret_event = None
241
+
242
+ if not isinstance(self.before_agent_callback, Callable):
243
+ return ret_event
244
+
245
+ callback_context = CallbackContext(ctx)
246
+ before_agent_callback_content = self.before_agent_callback(
247
+ callback_context=callback_context
248
+ )
249
+
250
+ if before_agent_callback_content:
251
+ ret_event = Event(
252
+ invocation_id=ctx.invocation_id,
253
+ author=self.name,
254
+ branch=ctx.branch,
255
+ content=before_agent_callback_content,
256
+ actions=callback_context._event_actions,
257
+ )
258
+ ctx.end_invocation = True
259
+ return ret_event
260
+
261
+ if callback_context.state.has_delta():
262
+ ret_event = Event(
263
+ invocation_id=ctx.invocation_id,
264
+ author=self.name,
265
+ branch=ctx.branch,
266
+ actions=callback_context._event_actions,
267
+ )
268
+
269
+ return ret_event
270
+
271
+ def __handle_after_agent_callback(
272
+ self, invocation_context: InvocationContext
273
+ ) -> Optional[Event]:
274
+ """Runs the after_agent_callback if it exists.
275
+
276
+ Returns:
277
+ Optional[Event]: an event if callback provides content or changed state.
278
+ """
279
+ ret_event = None
280
+
281
+ if not isinstance(self.after_agent_callback, Callable):
282
+ return ret_event
283
+
284
+ callback_context = CallbackContext(invocation_context)
285
+ after_agent_callback_content = self.after_agent_callback(
286
+ callback_context=callback_context
287
+ )
288
+
289
+ if after_agent_callback_content or callback_context.state.has_delta():
290
+ ret_event = Event(
291
+ invocation_id=invocation_context.invocation_id,
292
+ author=self.name,
293
+ branch=invocation_context.branch,
294
+ content=after_agent_callback_content,
295
+ actions=callback_context._event_actions,
296
+ )
297
+
298
+ return ret_event
299
+
300
+ @override
301
+ def model_post_init(self, __context: Any) -> None:
302
+ self.__set_parent_agent_for_sub_agents()
303
+
304
+ @field_validator('name', mode='after')
305
+ @classmethod
306
+ def __validate_name(cls, value: str):
307
+ if not value.isidentifier():
308
+ raise ValueError(
309
+ f'Found invalid agent name: `{value}`.'
310
+ ' Agent name must be a valid identifier. It should start with a'
311
+ ' letter (a-z, A-Z) or an underscore (_), and can only contain'
312
+ ' letters, digits (0-9), and underscores.'
313
+ )
314
+ if value == 'user':
315
+ raise ValueError(
316
+ "Agent name cannot be `user`. `user` is reserved for end-user's"
317
+ ' input.'
318
+ )
319
+ return value
320
+
321
+ def __set_parent_agent_for_sub_agents(self) -> BaseAgent:
322
+ for sub_agent in self.sub_agents:
323
+ if sub_agent.parent_agent is not None:
324
+ raise ValueError(
325
+ f'Agent `{sub_agent.name}` already has a parent agent, current'
326
+ f' parent: `{sub_agent.parent_agent.name}`, trying to add:'
327
+ f' `{self.name}`'
328
+ )
329
+ sub_agent.parent_agent = self
330
+ return self
@@ -60,12 +60,7 @@ class CallbackContext(ReadonlyContext):
60
60
  """
61
61
  return self._state
62
62
 
63
- @property
64
- def user_content(self) -> Optional[types.Content]:
65
- """The user content that started this invocation. READONLY field."""
66
- return self._invocation_context.user_content
67
-
68
- def load_artifact(
63
+ async def load_artifact(
69
64
  self, filename: str, version: Optional[int] = None
70
65
  ) -> Optional[types.Part]:
71
66
  """Loads an artifact attached to the current session.
@@ -80,7 +75,7 @@ class CallbackContext(ReadonlyContext):
80
75
  """
81
76
  if self._invocation_context.artifact_service is None:
82
77
  raise ValueError("Artifact service is not initialized.")
83
- return self._invocation_context.artifact_service.load_artifact(
78
+ return await self._invocation_context.artifact_service.load_artifact(
84
79
  app_name=self._invocation_context.app_name,
85
80
  user_id=self._invocation_context.user_id,
86
81
  session_id=self._invocation_context.session.id,
@@ -88,7 +83,7 @@ class CallbackContext(ReadonlyContext):
88
83
  version=version,
89
84
  )
90
85
 
91
- def save_artifact(self, filename: str, artifact: types.Part) -> int:
86
+ async def save_artifact(self, filename: str, artifact: types.Part) -> int:
92
87
  """Saves an artifact and records it as delta for the current session.
93
88
 
94
89
  Args:
@@ -100,7 +95,7 @@ class CallbackContext(ReadonlyContext):
100
95
  """
101
96
  if self._invocation_context.artifact_service is None:
102
97
  raise ValueError("Artifact service is not initialized.")
103
- version = self._invocation_context.artifact_service.save_artifact(
98
+ version = await self._invocation_context.artifact_service.save_artifact(
104
99
  app_name=self._invocation_context.app_name,
105
100
  user_id=self._invocation_context.user_id,
106
101
  session_id=self._invocation_context.session.id,
@@ -110,6 +110,7 @@ class InvocationContext(BaseModel):
110
110
  arbitrary_types_allowed=True,
111
111
  extra="forbid",
112
112
  )
113
+ """The pydantic model config."""
113
114
 
114
115
  artifact_service: Optional[BaseArtifactService] = None
115
116
  session_service: BaseSessionService
@@ -53,6 +53,7 @@ class LangGraphAgent(BaseAgent):
53
53
  model_config = ConfigDict(
54
54
  arbitrary_types_allowed=True,
55
55
  )
56
+ """The pydantic model config."""
56
57
 
57
58
  graph: CompiledGraph
58
59
 
@@ -24,6 +24,7 @@ class LiveRequest(BaseModel):
24
24
  """Request send to live agents."""
25
25
 
26
26
  model_config = ConfigDict(ser_json_bytes='base64', val_json_bytes='base64')
27
+ """The pydantic model config."""
27
28
 
28
29
  content: Optional[types.Content] = None
29
30
  """If set, send the content to the model in turn-by-turn mode."""