google-adk 1.6.1__py3-none-any.whl → 1.7.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 (81) hide show
  1. google/adk/a2a/converters/event_converter.py +5 -85
  2. google/adk/a2a/executor/a2a_agent_executor.py +45 -16
  3. google/adk/agents/__init__.py +5 -0
  4. google/adk/agents/agent_config.py +46 -0
  5. google/adk/agents/base_agent.py +234 -41
  6. google/adk/agents/callback_context.py +41 -0
  7. google/adk/agents/common_configs.py +79 -0
  8. google/adk/agents/config_agent_utils.py +184 -0
  9. google/adk/agents/config_schemas/AgentConfig.json +544 -0
  10. google/adk/agents/invocation_context.py +5 -1
  11. google/adk/agents/llm_agent.py +190 -9
  12. google/adk/agents/loop_agent.py +29 -0
  13. google/adk/agents/parallel_agent.py +24 -3
  14. google/adk/agents/remote_a2a_agent.py +15 -3
  15. google/adk/agents/sequential_agent.py +22 -1
  16. google/adk/artifacts/gcs_artifact_service.py +24 -2
  17. google/adk/auth/auth_handler.py +3 -3
  18. google/adk/auth/credential_manager.py +23 -23
  19. google/adk/auth/credential_service/base_credential_service.py +6 -6
  20. google/adk/auth/credential_service/in_memory_credential_service.py +10 -8
  21. google/adk/auth/credential_service/session_state_credential_service.py +8 -8
  22. google/adk/auth/exchanger/oauth2_credential_exchanger.py +3 -3
  23. google/adk/auth/oauth2_credential_util.py +2 -2
  24. google/adk/auth/refresher/oauth2_credential_refresher.py +4 -4
  25. google/adk/cli/agent_graph.py +3 -1
  26. google/adk/cli/browser/index.html +1 -1
  27. google/adk/cli/browser/main-SRBSE46V.js +3914 -0
  28. google/adk/cli/browser/polyfills-B6TNHZQ6.js +17 -0
  29. google/adk/cli/fast_api.py +42 -2
  30. google/adk/cli/utils/agent_loader.py +35 -1
  31. google/adk/code_executors/base_code_executor.py +14 -19
  32. google/adk/code_executors/built_in_code_executor.py +4 -1
  33. google/adk/evaluation/base_eval_service.py +46 -2
  34. google/adk/evaluation/evaluation_generator.py +1 -1
  35. google/adk/evaluation/in_memory_eval_sets_manager.py +151 -0
  36. google/adk/evaluation/local_eval_service.py +389 -0
  37. google/adk/evaluation/local_eval_sets_manager.py +23 -8
  38. google/adk/flows/llm_flows/auto_flow.py +6 -11
  39. google/adk/flows/llm_flows/base_llm_flow.py +41 -23
  40. google/adk/flows/llm_flows/contents.py +16 -10
  41. google/adk/flows/llm_flows/functions.py +76 -33
  42. google/adk/memory/in_memory_memory_service.py +20 -14
  43. google/adk/models/anthropic_llm.py +44 -5
  44. google/adk/models/google_llm.py +11 -6
  45. google/adk/models/lite_llm.py +21 -4
  46. google/adk/plugins/__init__.py +17 -0
  47. google/adk/plugins/base_plugin.py +317 -0
  48. google/adk/plugins/plugin_manager.py +265 -0
  49. google/adk/runners.py +122 -18
  50. google/adk/sessions/database_session_service.py +26 -28
  51. google/adk/sessions/vertex_ai_session_service.py +14 -7
  52. google/adk/tools/agent_tool.py +1 -0
  53. google/adk/tools/apihub_tool/apihub_toolset.py +38 -39
  54. google/adk/tools/application_integration_tool/application_integration_toolset.py +35 -37
  55. google/adk/tools/application_integration_tool/integration_connector_tool.py +2 -3
  56. google/adk/tools/base_tool.py +9 -9
  57. google/adk/tools/base_toolset.py +7 -5
  58. google/adk/tools/bigquery/__init__.py +3 -3
  59. google/adk/tools/enterprise_search_tool.py +4 -2
  60. google/adk/tools/google_api_tool/google_api_tool.py +16 -1
  61. google/adk/tools/google_api_tool/google_api_toolset.py +9 -7
  62. google/adk/tools/google_api_tool/google_api_toolsets.py +41 -20
  63. google/adk/tools/google_search_tool.py +4 -2
  64. google/adk/tools/langchain_tool.py +2 -3
  65. google/adk/tools/long_running_tool.py +21 -0
  66. google/adk/tools/mcp_tool/mcp_toolset.py +27 -28
  67. google/adk/tools/openapi_tool/openapi_spec_parser/openapi_toolset.py +8 -8
  68. google/adk/tools/openapi_tool/openapi_spec_parser/rest_api_tool.py +4 -6
  69. google/adk/tools/retrieval/vertex_ai_rag_retrieval.py +3 -2
  70. google/adk/tools/tool_context.py +0 -10
  71. google/adk/tools/url_context_tool.py +4 -2
  72. google/adk/tools/vertex_ai_search_tool.py +4 -2
  73. google/adk/utils/model_name_utils.py +90 -0
  74. google/adk/version.py +1 -1
  75. {google_adk-1.6.1.dist-info → google_adk-1.7.0.dist-info}/METADATA +2 -2
  76. {google_adk-1.6.1.dist-info → google_adk-1.7.0.dist-info}/RECORD +79 -69
  77. google/adk/cli/browser/main-RXDVX3K6.js +0 -3914
  78. google/adk/cli/browser/polyfills-FFHMD2TL.js +0 -17
  79. {google_adk-1.6.1.dist-info → google_adk-1.7.0.dist-info}/WHEEL +0 -0
  80. {google_adk-1.6.1.dist-info → google_adk-1.7.0.dist-info}/entry_points.txt +0 -0
  81. {google_adk-1.6.1.dist-info → google_adk-1.7.0.dist-info}/licenses/LICENSE +0 -0
google/adk/runners.py CHANGED
@@ -17,9 +17,14 @@ from __future__ import annotations
17
17
  import asyncio
18
18
  import logging
19
19
  import queue
20
+ import time
21
+ from typing import Any
20
22
  from typing import AsyncGenerator
23
+ from typing import Callable
21
24
  from typing import Generator
25
+ from typing import List
22
26
  from typing import Optional
27
+ import uuid
23
28
  import warnings
24
29
 
25
30
  from google.genai import types
@@ -36,10 +41,13 @@ from .artifacts.in_memory_artifact_service import InMemoryArtifactService
36
41
  from .auth.credential_service.base_credential_service import BaseCredentialService
37
42
  from .code_executors.built_in_code_executor import BuiltInCodeExecutor
38
43
  from .events.event import Event
44
+ from .events.event import EventActions
39
45
  from .flows.llm_flows.functions import find_matching_function_call
40
46
  from .memory.base_memory_service import BaseMemoryService
41
47
  from .memory.in_memory_memory_service import InMemoryMemoryService
42
48
  from .platform.thread import create_thread
49
+ from .plugins.base_plugin import BasePlugin
50
+ from .plugins.plugin_manager import PluginManager
43
51
  from .sessions.base_session_service import BaseSessionService
44
52
  from .sessions.in_memory_session_service import InMemorySessionService
45
53
  from .sessions.session import Session
@@ -60,6 +68,7 @@ class Runner:
60
68
  app_name: The application name of the runner.
61
69
  agent: The root agent to run.
62
70
  artifact_service: The artifact service for the runner.
71
+ plugin_manager: The plugin manager for the runner.
63
72
  session_service: The session service for the runner.
64
73
  memory_service: The memory service for the runner.
65
74
  """
@@ -70,6 +79,8 @@ class Runner:
70
79
  """The root agent to run."""
71
80
  artifact_service: Optional[BaseArtifactService] = None
72
81
  """The artifact service for the runner."""
82
+ plugin_manager: PluginManager
83
+ """The plugin manager for the runner."""
73
84
  session_service: BaseSessionService
74
85
  """The session service for the runner."""
75
86
  memory_service: Optional[BaseMemoryService] = None
@@ -82,6 +93,7 @@ class Runner:
82
93
  *,
83
94
  app_name: str,
84
95
  agent: BaseAgent,
96
+ plugins: Optional[List[BasePlugin]] = None,
85
97
  artifact_service: Optional[BaseArtifactService] = None,
86
98
  session_service: BaseSessionService,
87
99
  memory_service: Optional[BaseMemoryService] = None,
@@ -102,6 +114,7 @@ class Runner:
102
114
  self.session_service = session_service
103
115
  self.memory_service = memory_service
104
116
  self.credential_service = credential_service
117
+ self.plugin_manager = PluginManager(plugins=plugins)
105
118
 
106
119
  def run(
107
120
  self,
@@ -113,8 +126,9 @@ class Runner:
113
126
  ) -> Generator[Event, None, None]:
114
127
  """Runs the agent.
115
128
 
116
- NOTE: This sync interface is only for local testing and convenience purpose.
117
- Consider using `run_async` for production usage.
129
+ NOTE:
130
+ This sync interface is only for local testing and convenience purpose.
131
+ Consider using `run_async` for production usage.
118
132
 
119
133
  Args:
120
134
  user_id: The user ID of the session.
@@ -164,6 +178,7 @@ class Runner:
164
178
  user_id: str,
165
179
  session_id: str,
166
180
  new_message: types.Content,
181
+ state_delta: Optional[dict[str, Any]] = None,
167
182
  run_config: RunConfig = RunConfig(),
168
183
  ) -> AsyncGenerator[Event, None]:
169
184
  """Main entry method to run the agent in this runner.
@@ -191,19 +206,83 @@ class Runner:
191
206
  )
192
207
  root_agent = self.agent
193
208
 
209
+ # Modify user message before execution.
210
+ modified_user_message = (
211
+ await invocation_context.plugin_manager.run_on_user_message_callback(
212
+ invocation_context=invocation_context, user_message=new_message
213
+ )
214
+ )
215
+ if modified_user_message is not None:
216
+ new_message = modified_user_message
217
+
194
218
  if new_message:
195
219
  await self._append_new_message_to_session(
196
220
  session,
197
221
  new_message,
198
222
  invocation_context,
199
223
  run_config.save_input_blobs_as_artifacts,
224
+ state_delta,
200
225
  )
201
226
 
202
227
  invocation_context.agent = self._find_agent_to_run(session, root_agent)
203
- async for event in invocation_context.agent.run_async(invocation_context):
228
+
229
+ async def execute(ctx: InvocationContext) -> AsyncGenerator[Event]:
230
+ async for event in ctx.agent.run_async(ctx):
231
+ yield event
232
+
233
+ async for event in self._exec_with_plugin(
234
+ invocation_context, session, execute
235
+ ):
236
+ yield event
237
+
238
+ async def _exec_with_plugin(
239
+ self,
240
+ invocation_context: InvocationContext,
241
+ session: Session,
242
+ execute_fn: Callable[[InvocationContext], AsyncGenerator[Event, None]],
243
+ ) -> AsyncGenerator[Event, None]:
244
+ """Wraps execution with plugin callbacks.
245
+
246
+ Args:
247
+ invocation_context: The invocation context
248
+ session: The current session
249
+ execute_fn: A callable that returns an AsyncGenerator of Events
250
+
251
+ Yields:
252
+ Events from the execution, including any generated by plugins
253
+ """
254
+
255
+ plugin_manager = invocation_context.plugin_manager
256
+
257
+ # Step 1: Run the before_run callbacks to see if we should early exit.
258
+ early_exit_result = await plugin_manager.run_before_run_callback(
259
+ invocation_context=invocation_context
260
+ )
261
+ if isinstance(early_exit_result, Event):
262
+ await self.session_service.append_event(
263
+ session=session,
264
+ event=Event(
265
+ invocation_id=invocation_context.invocation_id,
266
+ author='model',
267
+ content=early_exit_result,
268
+ ),
269
+ )
270
+ yield early_exit_result
271
+ else:
272
+ # Step 2: Otherwise continue with normal execution
273
+ async for event in execute_fn(invocation_context):
204
274
  if not event.partial:
205
275
  await self.session_service.append_event(session=session, event=event)
206
- yield event
276
+ # Step 3: Run the on_event callbacks to optionally modify the event.
277
+ modified_event = await plugin_manager.run_on_event_callback(
278
+ invocation_context=invocation_context, event=event
279
+ )
280
+ yield (modified_event if modified_event else event)
281
+
282
+ # Step 4: Run the after_run callbacks to optionally modify the context.
283
+ await plugin_manager.run_after_run_callback(
284
+ invocation_context=invocation_context
285
+ )
207
286
 
208
287
  async def _append_new_message_to_session(
209
288
  self,
@@ -211,6 +290,7 @@ class Runner:
211
290
  new_message: types.Content,
212
291
  invocation_context: InvocationContext,
213
292
  save_input_blobs_as_artifacts: bool = False,
293
+ state_delta: Optional[dict[str, Any]] = None,
214
294
  ):
215
295
  """Appends a new message to the session.
216
296
 
@@ -242,11 +322,19 @@ class Runner:
242
322
  text=f'Uploaded file: {file_name}. It is saved into artifacts'
243
323
  )
244
324
  # Appends only. We do not yield the event because it's not from the model.
245
- event = Event(
246
- invocation_id=invocation_context.invocation_id,
247
- author='user',
248
- content=new_message,
249
- )
325
+ if state_delta:
326
+ event = Event(
327
+ invocation_id=invocation_context.invocation_id,
328
+ author='user',
329
+ actions=EventActions(state_delta=state_delta),
330
+ content=new_message,
331
+ )
332
+ else:
333
+ event = Event(
334
+ invocation_id=invocation_context.invocation_id,
335
+ author='user',
336
+ content=new_message,
337
+ )
250
338
  await self.session_service.append_event(session=session, event=event)
251
339
 
252
340
  async def run_live(
@@ -278,7 +366,7 @@ class Runner:
278
366
  This feature is **experimental** and its API or behavior may change
279
367
  in future releases.
280
368
 
281
- .. note::
369
+ .. NOTE::
282
370
  Either `session` or both `user_id` and `session_id` must be provided.
283
371
  """
284
372
  if session is None and (user_id is None or session_id is None):
@@ -345,8 +433,14 @@ class Runner:
345
433
  invocation_context.active_streaming_tools[tool.__name__] = (
346
434
  active_streaming_tool
347
435
  )
348
- async for event in invocation_context.agent.run_live(invocation_context):
349
- await self.session_service.append_event(session=session, event=event)
436
+
437
+ async def execute(ctx: InvocationContext) -> AsyncGenerator[Event]:
438
+ async for event in ctx.agent.run_live(ctx):
439
+ yield event
440
+
441
+ async for event in self._exec_with_plugin(
442
+ invocation_context, session, execute
443
+ ):
350
444
  yield event
351
445
 
352
446
  def _find_agent_to_run(
@@ -355,9 +449,10 @@ class Runner:
355
449
  """Finds the agent to run to continue the session.
356
450
 
357
451
  A qualified agent must be either of:
452
+
358
453
  - The agent that returned a function call and the last user message is a
359
454
  function response to this function call.
360
- - The root agent;
455
+ - The root agent.
361
456
  - An LlmAgent who replied last and is capable to transfer to any other agent
362
457
  in the agent hierarchy.
363
458
 
@@ -366,7 +461,8 @@ class Runner:
366
461
  root_agent: The root agent of the runner.
367
462
 
368
463
  Returns:
369
- The agent of the last message in the session or the root agent.
464
+ The agent to run. (the active agent that should reply to the latest user
465
+ message)
370
466
  """
371
467
  # If the last event is a function response, should send this response to
372
468
  # the agent that returned the corressponding function call regardless the
@@ -395,8 +491,8 @@ class Runner:
395
491
  def _is_transferable_across_agent_tree(self, agent_to_run: BaseAgent) -> bool:
396
492
  """Whether the agent to run can transfer to any other agent in the agent tree.
397
493
 
398
- This typically means all agent_to_run's parent through root agent can
399
- transfer to their parent_agent.
494
+ This typically means all agent_to_run's ancestor can transfer to their
495
+ parent_agent all the way to the root_agent.
400
496
 
401
497
  Args:
402
498
  agent_to_run: The agent to check for transferability.
@@ -407,7 +503,7 @@ class Runner:
407
503
  agent = agent_to_run
408
504
  while agent:
409
505
  if not isinstance(agent, LlmAgent):
410
- # Only LLM-based Agent can provider agent transfer capability.
506
+ # Only LLM-based Agent can provide agent transfer capability.
411
507
  return False
412
508
  if agent.disallow_transfer_to_parent:
413
509
  return False
@@ -450,6 +546,7 @@ class Runner:
450
546
  session_service=self.session_service,
451
547
  memory_service=self.memory_service,
452
548
  credential_service=self.credential_service,
549
+ plugin_manager=self.plugin_manager,
453
550
  invocation_id=invocation_id,
454
551
  agent=self.agent,
455
552
  session=session,
@@ -538,7 +635,13 @@ class InMemoryRunner(Runner):
538
635
  session service for the runner.
539
636
  """
540
637
 
541
- def __init__(self, agent: BaseAgent, *, app_name: str = 'InMemoryRunner'):
638
+ def __init__(
639
+ self,
640
+ agent: BaseAgent,
641
+ *,
642
+ app_name: str = 'InMemoryRunner',
643
+ plugins: Optional[list[BasePlugin]] = None,
644
+ ):
542
645
  """Initializes the InMemoryRunner.
543
646
 
544
647
  Args:
@@ -551,6 +654,7 @@ class InMemoryRunner(Runner):
551
654
  app_name=app_name,
552
655
  agent=agent,
553
656
  artifact_service=InMemoryArtifactService(),
657
+ plugins=plugins,
554
658
  session_service=self._in_memory_session_service,
555
659
  memory_service=InMemoryMemoryService(),
556
660
  )
@@ -137,7 +137,7 @@ class StorageSession(Base):
137
137
  DateTime(), default=func.now(), onupdate=func.now()
138
138
  )
139
139
 
140
- storage_events: Mapped[list["StorageEvent"]] = relationship(
140
+ storage_events: Mapped[list[StorageEvent]] = relationship(
141
141
  "StorageEvent",
142
142
  back_populates="storage_session",
143
143
  )
@@ -373,11 +373,11 @@ class DatabaseSessionService(BaseSessionService):
373
373
  # 4. Build the session object with generated id
374
374
  # 5. Return the session
375
375
 
376
- with self.database_session_factory() as session_factory:
376
+ with self.database_session_factory() as sql_session:
377
377
 
378
378
  # Fetch app and user states from storage
379
- storage_app_state = session_factory.get(StorageAppState, (app_name))
380
- storage_user_state = session_factory.get(
379
+ storage_app_state = sql_session.get(StorageAppState, (app_name))
380
+ storage_user_state = sql_session.get(
381
381
  StorageUserState, (app_name, user_id)
382
382
  )
383
383
 
@@ -387,12 +387,12 @@ class DatabaseSessionService(BaseSessionService):
387
387
  # Create state tables if not exist
388
388
  if not storage_app_state:
389
389
  storage_app_state = StorageAppState(app_name=app_name, state={})
390
- session_factory.add(storage_app_state)
390
+ sql_session.add(storage_app_state)
391
391
  if not storage_user_state:
392
392
  storage_user_state = StorageUserState(
393
393
  app_name=app_name, user_id=user_id, state={}
394
394
  )
395
- session_factory.add(storage_user_state)
395
+ sql_session.add(storage_user_state)
396
396
 
397
397
  # Extract state deltas
398
398
  app_state_delta, user_state_delta, session_state = _extract_state_delta(
@@ -416,10 +416,10 @@ class DatabaseSessionService(BaseSessionService):
416
416
  id=session_id,
417
417
  state=session_state,
418
418
  )
419
- session_factory.add(storage_session)
420
- session_factory.commit()
419
+ sql_session.add(storage_session)
420
+ sql_session.commit()
421
421
 
422
- session_factory.refresh(storage_session)
422
+ sql_session.refresh(storage_session)
423
423
 
424
424
  # Merge states for response
425
425
  merged_state = _merge_state(app_state, user_state, session_state)
@@ -444,8 +444,8 @@ class DatabaseSessionService(BaseSessionService):
444
444
  # 1. Get the storage session entry from session table
445
445
  # 2. Get all the events based on session id and filtering config
446
446
  # 3. Convert and return the session
447
- with self.database_session_factory() as session_factory:
448
- storage_session = session_factory.get(
447
+ with self.database_session_factory() as sql_session:
448
+ storage_session = sql_session.get(
449
449
  StorageSession, (app_name, user_id, session_id)
450
450
  )
451
451
  if storage_session is None:
@@ -458,7 +458,7 @@ class DatabaseSessionService(BaseSessionService):
458
458
  timestamp_filter = True
459
459
 
460
460
  storage_events = (
461
- session_factory.query(StorageEvent)
461
+ sql_session.query(StorageEvent)
462
462
  .filter(StorageEvent.app_name == app_name)
463
463
  .filter(StorageEvent.session_id == storage_session.id)
464
464
  .filter(StorageEvent.user_id == user_id)
@@ -473,8 +473,8 @@ class DatabaseSessionService(BaseSessionService):
473
473
  )
474
474
 
475
475
  # Fetch states from storage
476
- storage_app_state = session_factory.get(StorageAppState, (app_name))
477
- storage_user_state = session_factory.get(
476
+ storage_app_state = sql_session.get(StorageAppState, (app_name))
477
+ storage_user_state = sql_session.get(
478
478
  StorageUserState, (app_name, user_id)
479
479
  )
480
480
 
@@ -500,9 +500,9 @@ class DatabaseSessionService(BaseSessionService):
500
500
  async def list_sessions(
501
501
  self, *, app_name: str, user_id: str
502
502
  ) -> ListSessionsResponse:
503
- with self.database_session_factory() as session_factory:
503
+ with self.database_session_factory() as sql_session:
504
504
  results = (
505
- session_factory.query(StorageSession)
505
+ sql_session.query(StorageSession)
506
506
  .filter(StorageSession.app_name == app_name)
507
507
  .filter(StorageSession.user_id == user_id)
508
508
  .all()
@@ -523,14 +523,14 @@ class DatabaseSessionService(BaseSessionService):
523
523
  async def delete_session(
524
524
  self, app_name: str, user_id: str, session_id: str
525
525
  ) -> None:
526
- with self.database_session_factory() as session_factory:
526
+ with self.database_session_factory() as sql_session:
527
527
  stmt = delete(StorageSession).where(
528
528
  StorageSession.app_name == app_name,
529
529
  StorageSession.user_id == user_id,
530
530
  StorageSession.id == session_id,
531
531
  )
532
- session_factory.execute(stmt)
533
- session_factory.commit()
532
+ sql_session.execute(stmt)
533
+ sql_session.commit()
534
534
 
535
535
  @override
536
536
  async def append_event(self, session: Session, event: Event) -> Event:
@@ -542,8 +542,8 @@ class DatabaseSessionService(BaseSessionService):
542
542
  # 1. Check if timestamp is stale
543
543
  # 2. Update session attributes based on event config
544
544
  # 3. Store event to table
545
- with self.database_session_factory() as session_factory:
546
- storage_session = session_factory.get(
545
+ with self.database_session_factory() as sql_session:
546
+ storage_session = sql_session.get(
547
547
  StorageSession, (session.app_name, session.user_id, session.id)
548
548
  )
549
549
 
@@ -557,10 +557,8 @@ class DatabaseSessionService(BaseSessionService):
557
557
  )
558
558
 
559
559
  # Fetch states from storage
560
- storage_app_state = session_factory.get(
561
- StorageAppState, (session.app_name)
562
- )
563
- storage_user_state = session_factory.get(
560
+ storage_app_state = sql_session.get(StorageAppState, (session.app_name))
561
+ storage_user_state = sql_session.get(
564
562
  StorageUserState, (session.app_name, session.user_id)
565
563
  )
566
564
 
@@ -589,10 +587,10 @@ class DatabaseSessionService(BaseSessionService):
589
587
  session_state.update(session_state_delta)
590
588
  storage_session.state = session_state
591
589
 
592
- session_factory.add(StorageEvent.from_event(session, event))
590
+ sql_session.add(StorageEvent.from_event(session, event))
593
591
 
594
- session_factory.commit()
595
- session_factory.refresh(storage_session)
592
+ sql_session.commit()
593
+ sql_session.refresh(storage_session)
596
594
 
597
595
  # Update timestamp with commit time
598
596
  session.last_update_time = storage_session.update_timestamp_tz
@@ -216,29 +216,36 @@ class VertexAiSessionService(BaseSessionService):
216
216
  path=f'reasoningEngines/{reasoning_engine_id}/sessions/{session_id}/events',
217
217
  request_dict={},
218
218
  )
219
- list_events_api_response = _convert_api_response(list_events_api_response)
219
+ converted_api_response = _convert_api_response(list_events_api_response)
220
220
 
221
- # Handles empty response case
222
- if not list_events_api_response or list_events_api_response.get(
221
+ # Handles empty response case where there are no events to fetch
222
+ if not converted_api_response or converted_api_response.get(
223
223
  'httpHeaders', None
224
224
  ):
225
225
  return session
226
226
 
227
227
  session.events += [
228
228
  _from_api_event(event)
229
- for event in list_events_api_response['sessionEvents']
229
+ for event in converted_api_response['sessionEvents']
230
230
  ]
231
231
 
232
- while list_events_api_response.get('nextPageToken', None):
233
- page_token = list_events_api_response.get('nextPageToken', None)
232
+ while converted_api_response.get('nextPageToken', None):
233
+ page_token = converted_api_response.get('nextPageToken', None)
234
234
  list_events_api_response = await api_client.async_request(
235
235
  http_method='GET',
236
236
  path=f'reasoningEngines/{reasoning_engine_id}/sessions/{session_id}/events?pageToken={page_token}',
237
237
  request_dict={},
238
238
  )
239
+ converted_api_response = _convert_api_response(list_events_api_response)
240
+
241
+ # Handles empty response case where there are no more events to fetch
242
+ if not converted_api_response or converted_api_response.get(
243
+ 'httpHeaders', None
244
+ ):
245
+ break
239
246
  session.events += [
240
247
  _from_api_event(event)
241
- for event in list_events_api_response['sessionEvents']
248
+ for event in converted_api_response['sessionEvents']
242
249
  ]
243
250
 
244
251
  session.events = [
@@ -116,6 +116,7 @@ class AgentTool(BaseTool):
116
116
  artifact_service=ForwardingArtifactService(tool_context),
117
117
  session_service=InMemorySessionService(),
118
118
  memory_service=InMemoryMemoryService(),
119
+ credential_service=tool_context._invocation_context.credential_service,
119
120
  )
120
121
  session = await runner.session_service.create_session(
121
122
  app_name=self.agent.name,
@@ -35,27 +35,25 @@ from .clients.apihub_client import APIHubClient
35
35
  class APIHubToolset(BaseToolset):
36
36
  """APIHubTool generates tools from a given API Hub resource.
37
37
 
38
- Examples:
38
+ Examples::
39
39
 
40
- ```
41
- apihub_toolset = APIHubToolset(
42
- apihub_resource_name="projects/test-project/locations/us-central1/apis/test-api",
43
- service_account_json="...",
44
- tool_filter=lambda tool, ctx=None: tool.name in ('my_tool',
45
- 'my_other_tool')
46
- )
47
-
48
- # Get all available tools
49
- agent = LlmAgent(tools=apihub_toolset)
40
+ apihub_toolset = APIHubToolset(
41
+ apihub_resource_name="projects/test-project/locations/us-central1/apis/test-api",
42
+ service_account_json="...",
43
+ tool_filter=lambda tool, ctx=None: tool.name in ('my_tool',
44
+ 'my_other_tool')
45
+ )
50
46
 
51
- ```
47
+ # Get all available tools
48
+ agent = LlmAgent(tools=apihub_toolset)
52
49
 
53
50
  **apihub_resource_name** is the resource name from API Hub. It must include
54
- API name, and can optionally include API version and spec name.
55
- - If apihub_resource_name includes a spec resource name, the content of that
56
- spec will be used for generating the tools.
57
- - If apihub_resource_name includes only an api or a version name, the
58
- first spec of the first version of that API will be used.
51
+ API name, and can optionally include API version and spec name.
52
+
53
+ - If apihub_resource_name includes a spec resource name, the content of that
54
+ spec will be used for generating the tools.
55
+ - If apihub_resource_name includes only an api or a version name, the
56
+ first spec of the first version of that API will be used.
59
57
  """
60
58
 
61
59
  def __init__(
@@ -78,44 +76,45 @@ class APIHubToolset(BaseToolset):
78
76
  ):
79
77
  """Initializes the APIHubTool with the given parameters.
80
78
 
81
- Examples:
82
- ```
83
- apihub_toolset = APIHubToolset(
84
- apihub_resource_name="projects/test-project/locations/us-central1/apis/test-api",
85
- service_account_json="...",
86
- )
79
+ Examples::
87
80
 
88
- # Get all available tools
89
- agent = LlmAgent(tools=[apihub_toolset])
81
+ apihub_toolset = APIHubToolset(
82
+ apihub_resource_name="projects/test-project/locations/us-central1/apis/test-api",
83
+ service_account_json="...",
84
+ )
90
85
 
91
- apihub_toolset = APIHubToolset(
92
- apihub_resource_name="projects/test-project/locations/us-central1/apis/test-api",
93
- service_account_json="...",
94
- tool_filter = ['my_tool']
95
- )
96
- # Get a specific tool
97
- agent = LlmAgent(tools=[
98
- ...,
99
- apihub_toolset,
100
- ])
101
- ```
86
+ # Get all available tools
87
+ agent = LlmAgent(tools=[apihub_toolset])
88
+
89
+ apihub_toolset = APIHubToolset(
90
+ apihub_resource_name="projects/test-project/locations/us-central1/apis/test-api",
91
+ service_account_json="...",
92
+ tool_filter = ['my_tool']
93
+ )
94
+ # Get a specific tool
95
+ agent = LlmAgent(tools=[
96
+ ...,
97
+ apihub_toolset,
98
+ ])
102
99
 
103
100
  **apihub_resource_name** is the resource name from API Hub. It must include
104
101
  API name, and can optionally include API version and spec name.
102
+
105
103
  - If apihub_resource_name includes a spec resource name, the content of that
106
104
  spec will be used for generating the tools.
107
105
  - If apihub_resource_name includes only an api or a version name, the
108
106
  first spec of the first version of that API will be used.
109
107
 
110
108
  Example:
109
+
111
110
  * projects/xxx/locations/us-central1/apis/apiname/...
112
111
  * https://console.cloud.google.com/apigee/api-hub/apis/apiname?project=xxx
113
112
 
114
113
  Args:
115
114
  apihub_resource_name: The resource name of the API in API Hub.
116
- Example: `projects/test-project/locations/us-central1/apis/test-api`.
117
- access_token: Google Access token. Generate with gcloud cli `gcloud auth
118
- auth print-access-token`. Used for fetching API Specs from API Hub.
115
+ Example: ``projects/test-project/locations/us-central1/apis/test-api``.
116
+ access_token: Google Access token. Generate with gcloud cli
117
+ ``gcloud auth auth print-access-token``. Used for fetching API Specs from API Hub.
119
118
  service_account_json: The service account config as a json string.
120
119
  Required if not using default service credential. It is used for
121
120
  creating the API Hub client and fetching the API Specs from API Hub.