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.
- google/adk/a2a/converters/event_converter.py +5 -85
- google/adk/a2a/executor/a2a_agent_executor.py +45 -16
- google/adk/agents/__init__.py +5 -0
- google/adk/agents/agent_config.py +46 -0
- google/adk/agents/base_agent.py +234 -41
- google/adk/agents/callback_context.py +41 -0
- google/adk/agents/common_configs.py +79 -0
- google/adk/agents/config_agent_utils.py +184 -0
- google/adk/agents/config_schemas/AgentConfig.json +544 -0
- google/adk/agents/invocation_context.py +5 -1
- google/adk/agents/llm_agent.py +190 -9
- google/adk/agents/loop_agent.py +29 -0
- google/adk/agents/parallel_agent.py +24 -3
- google/adk/agents/remote_a2a_agent.py +15 -3
- google/adk/agents/sequential_agent.py +22 -1
- google/adk/artifacts/gcs_artifact_service.py +24 -2
- google/adk/auth/auth_handler.py +3 -3
- google/adk/auth/credential_manager.py +23 -23
- google/adk/auth/credential_service/base_credential_service.py +6 -6
- google/adk/auth/credential_service/in_memory_credential_service.py +10 -8
- google/adk/auth/credential_service/session_state_credential_service.py +8 -8
- google/adk/auth/exchanger/oauth2_credential_exchanger.py +3 -3
- google/adk/auth/oauth2_credential_util.py +2 -2
- google/adk/auth/refresher/oauth2_credential_refresher.py +4 -4
- google/adk/cli/agent_graph.py +3 -1
- google/adk/cli/browser/index.html +1 -1
- google/adk/cli/browser/main-SRBSE46V.js +3914 -0
- google/adk/cli/browser/polyfills-B6TNHZQ6.js +17 -0
- google/adk/cli/fast_api.py +42 -2
- google/adk/cli/utils/agent_loader.py +35 -1
- google/adk/code_executors/base_code_executor.py +14 -19
- google/adk/code_executors/built_in_code_executor.py +4 -1
- google/adk/evaluation/base_eval_service.py +46 -2
- google/adk/evaluation/evaluation_generator.py +1 -1
- google/adk/evaluation/in_memory_eval_sets_manager.py +151 -0
- google/adk/evaluation/local_eval_service.py +389 -0
- google/adk/evaluation/local_eval_sets_manager.py +23 -8
- google/adk/flows/llm_flows/auto_flow.py +6 -11
- google/adk/flows/llm_flows/base_llm_flow.py +41 -23
- google/adk/flows/llm_flows/contents.py +16 -10
- google/adk/flows/llm_flows/functions.py +76 -33
- google/adk/memory/in_memory_memory_service.py +20 -14
- google/adk/models/anthropic_llm.py +44 -5
- google/adk/models/google_llm.py +11 -6
- google/adk/models/lite_llm.py +21 -4
- google/adk/plugins/__init__.py +17 -0
- google/adk/plugins/base_plugin.py +317 -0
- google/adk/plugins/plugin_manager.py +265 -0
- google/adk/runners.py +122 -18
- google/adk/sessions/database_session_service.py +26 -28
- google/adk/sessions/vertex_ai_session_service.py +14 -7
- google/adk/tools/agent_tool.py +1 -0
- google/adk/tools/apihub_tool/apihub_toolset.py +38 -39
- google/adk/tools/application_integration_tool/application_integration_toolset.py +35 -37
- google/adk/tools/application_integration_tool/integration_connector_tool.py +2 -3
- google/adk/tools/base_tool.py +9 -9
- google/adk/tools/base_toolset.py +7 -5
- google/adk/tools/bigquery/__init__.py +3 -3
- google/adk/tools/enterprise_search_tool.py +4 -2
- google/adk/tools/google_api_tool/google_api_tool.py +16 -1
- google/adk/tools/google_api_tool/google_api_toolset.py +9 -7
- google/adk/tools/google_api_tool/google_api_toolsets.py +41 -20
- google/adk/tools/google_search_tool.py +4 -2
- google/adk/tools/langchain_tool.py +2 -3
- google/adk/tools/long_running_tool.py +21 -0
- google/adk/tools/mcp_tool/mcp_toolset.py +27 -28
- google/adk/tools/openapi_tool/openapi_spec_parser/openapi_toolset.py +8 -8
- google/adk/tools/openapi_tool/openapi_spec_parser/rest_api_tool.py +4 -6
- google/adk/tools/retrieval/vertex_ai_rag_retrieval.py +3 -2
- google/adk/tools/tool_context.py +0 -10
- google/adk/tools/url_context_tool.py +4 -2
- google/adk/tools/vertex_ai_search_tool.py +4 -2
- google/adk/utils/model_name_utils.py +90 -0
- google/adk/version.py +1 -1
- {google_adk-1.6.1.dist-info → google_adk-1.7.0.dist-info}/METADATA +2 -2
- {google_adk-1.6.1.dist-info → google_adk-1.7.0.dist-info}/RECORD +79 -69
- google/adk/cli/browser/main-RXDVX3K6.js +0 -3914
- google/adk/cli/browser/polyfills-FFHMD2TL.js +0 -17
- {google_adk-1.6.1.dist-info → google_adk-1.7.0.dist-info}/WHEEL +0 -0
- {google_adk-1.6.1.dist-info → google_adk-1.7.0.dist-info}/entry_points.txt +0 -0
- {google_adk-1.6.1.dist-info → google_adk-1.7.0.dist-info}/licenses/LICENSE +0 -0
google/adk/agents/llm_agent.py
CHANGED
@@ -14,14 +14,17 @@
|
|
14
14
|
|
15
15
|
from __future__ import annotations
|
16
16
|
|
17
|
+
import importlib
|
17
18
|
import inspect
|
18
19
|
import logging
|
19
20
|
from typing import Any
|
20
21
|
from typing import AsyncGenerator
|
21
22
|
from typing import Awaitable
|
22
23
|
from typing import Callable
|
24
|
+
from typing import List
|
23
25
|
from typing import Literal
|
24
26
|
from typing import Optional
|
27
|
+
from typing import Type
|
25
28
|
from typing import Union
|
26
29
|
|
27
30
|
from google.genai import types
|
@@ -48,8 +51,11 @@ from ..tools.base_tool import BaseTool
|
|
48
51
|
from ..tools.base_toolset import BaseToolset
|
49
52
|
from ..tools.function_tool import FunctionTool
|
50
53
|
from ..tools.tool_context import ToolContext
|
54
|
+
from ..utils.feature_decorator import working_in_progress
|
51
55
|
from .base_agent import BaseAgent
|
56
|
+
from .base_agent import BaseAgentConfig
|
52
57
|
from .callback_context import CallbackContext
|
58
|
+
from .common_configs import CodeConfig
|
53
59
|
from .invocation_context import InvocationContext
|
54
60
|
from .readonly_context import ReadonlyContext
|
55
61
|
|
@@ -164,9 +170,9 @@ class LlmAgent(BaseAgent):
|
|
164
170
|
"""Controls content inclusion in model requests.
|
165
171
|
|
166
172
|
Options:
|
167
|
-
|
168
|
-
|
169
|
-
|
173
|
+
default: Model receives relevant conversation history
|
174
|
+
none: Model receives no prior history, operates solely on current
|
175
|
+
instruction and input
|
170
176
|
"""
|
171
177
|
|
172
178
|
# Controlled input/output configurations - Start
|
@@ -175,8 +181,9 @@ class LlmAgent(BaseAgent):
|
|
175
181
|
output_schema: Optional[type[BaseModel]] = None
|
176
182
|
"""The output schema when agent replies.
|
177
183
|
|
178
|
-
NOTE:
|
179
|
-
|
184
|
+
NOTE:
|
185
|
+
When this is set, agent can ONLY reply and CANNOT use any tools, such as
|
186
|
+
function tools, RAGs, agent transfer, etc.
|
180
187
|
"""
|
181
188
|
output_key: Optional[str] = None
|
182
189
|
"""The key in session state to store the output of the agent.
|
@@ -191,9 +198,9 @@ class LlmAgent(BaseAgent):
|
|
191
198
|
planner: Optional[BasePlanner] = None
|
192
199
|
"""Instructs the agent to make a plan and execute it step by step.
|
193
200
|
|
194
|
-
NOTE:
|
195
|
-
|
196
|
-
|
201
|
+
NOTE:
|
202
|
+
To use model's built-in thinking features, set the `thinking_config`
|
203
|
+
field in `google.adk.planners.built_in_planner`.
|
197
204
|
"""
|
198
205
|
|
199
206
|
code_executor: Optional[BaseCodeExecutor] = None
|
@@ -202,7 +209,8 @@ class LlmAgent(BaseAgent):
|
|
202
209
|
|
203
210
|
Check out available code executions in `google.adk.code_executor` package.
|
204
211
|
|
205
|
-
NOTE:
|
212
|
+
NOTE:
|
213
|
+
To use model's built-in code executor, use the `BuiltInCodeExecutor`.
|
206
214
|
"""
|
207
215
|
# Advance features - End
|
208
216
|
|
@@ -516,5 +524,178 @@ class LlmAgent(BaseAgent):
|
|
516
524
|
)
|
517
525
|
return generate_content_config
|
518
526
|
|
527
|
+
@classmethod
|
528
|
+
@working_in_progress('LlmAgent._resolve_tools is not ready for use.')
|
529
|
+
def _resolve_tools(cls, tools_config: list[CodeConfig]) -> list[Any]:
|
530
|
+
"""Resolve tools from configuration.
|
531
|
+
|
532
|
+
Args:
|
533
|
+
tools_config: List of tool configurations (CodeConfig objects).
|
534
|
+
|
535
|
+
Returns:
|
536
|
+
List of resolved tool objects.
|
537
|
+
"""
|
538
|
+
|
539
|
+
resolved_tools = []
|
540
|
+
for tool_config in tools_config:
|
541
|
+
if '.' not in tool_config.name:
|
542
|
+
module = importlib.import_module('google.adk.tools')
|
543
|
+
obj = getattr(module, tool_config.name)
|
544
|
+
if isinstance(obj, ToolUnion):
|
545
|
+
resolved_tools.append(obj)
|
546
|
+
else:
|
547
|
+
raise ValueError(
|
548
|
+
f'Invalid tool name: {tool_config.name} is not a built-in tool.'
|
549
|
+
)
|
550
|
+
else:
|
551
|
+
from .config_agent_utils import resolve_code_reference
|
552
|
+
|
553
|
+
resolved_tools.append(resolve_code_reference(tool_config))
|
554
|
+
|
555
|
+
return resolved_tools
|
556
|
+
|
557
|
+
@classmethod
|
558
|
+
@override
|
559
|
+
@working_in_progress('LlmAgent.from_config is not ready for use.')
|
560
|
+
def from_config(
|
561
|
+
cls: Type[LlmAgent],
|
562
|
+
config: LlmAgentConfig,
|
563
|
+
config_abs_path: str,
|
564
|
+
) -> LlmAgent:
|
565
|
+
from .config_agent_utils import resolve_callbacks
|
566
|
+
|
567
|
+
agent = super().from_config(config, config_abs_path)
|
568
|
+
if config.model:
|
569
|
+
agent.model = config.model
|
570
|
+
if config.instruction:
|
571
|
+
agent.instruction = config.instruction
|
572
|
+
if config.disallow_transfer_to_parent:
|
573
|
+
agent.disallow_transfer_to_parent = config.disallow_transfer_to_parent
|
574
|
+
if config.disallow_transfer_to_peers:
|
575
|
+
agent.disallow_transfer_to_peers = config.disallow_transfer_to_peers
|
576
|
+
if config.include_contents != 'default':
|
577
|
+
agent.include_contents = config.include_contents
|
578
|
+
if config.output_key:
|
579
|
+
agent.output_key = config.output_key
|
580
|
+
if config.tools:
|
581
|
+
agent.tools = cls._resolve_tools(config.tools)
|
582
|
+
if config.before_model_callbacks:
|
583
|
+
agent.before_model_callback = resolve_callbacks(
|
584
|
+
config.before_model_callbacks
|
585
|
+
)
|
586
|
+
if config.after_model_callbacks:
|
587
|
+
agent.after_model_callback = resolve_callbacks(
|
588
|
+
config.after_model_callbacks
|
589
|
+
)
|
590
|
+
if config.before_tool_callbacks:
|
591
|
+
agent.before_tool_callback = resolve_callbacks(
|
592
|
+
config.before_tool_callbacks
|
593
|
+
)
|
594
|
+
if config.after_tool_callbacks:
|
595
|
+
agent.after_tool_callback = resolve_callbacks(config.after_tool_callbacks)
|
596
|
+
return agent
|
597
|
+
|
519
598
|
|
520
599
|
Agent: TypeAlias = LlmAgent
|
600
|
+
|
601
|
+
|
602
|
+
class LlmAgentConfig(BaseAgentConfig):
|
603
|
+
"""The config for the YAML schema of a LlmAgent."""
|
604
|
+
|
605
|
+
agent_class: Literal['LlmAgent', ''] = 'LlmAgent'
|
606
|
+
"""The value is used to uniquely identify the LlmAgent class. If it is
|
607
|
+
empty, it is by default an LlmAgent."""
|
608
|
+
|
609
|
+
model: Optional[str] = None
|
610
|
+
"""Optional. LlmAgent.model. If not set, the model will be inherited from
|
611
|
+
the ancestor."""
|
612
|
+
|
613
|
+
instruction: str
|
614
|
+
"""Required. LlmAgent.instruction."""
|
615
|
+
|
616
|
+
disallow_transfer_to_parent: Optional[bool] = None
|
617
|
+
"""Optional. LlmAgent.disallow_transfer_to_parent."""
|
618
|
+
|
619
|
+
disallow_transfer_to_peers: Optional[bool] = None
|
620
|
+
"""Optional. LlmAgent.disallow_transfer_to_peers."""
|
621
|
+
|
622
|
+
output_key: Optional[str] = None
|
623
|
+
"""Optional. LlmAgent.output_key."""
|
624
|
+
|
625
|
+
include_contents: Literal['default', 'none'] = 'default'
|
626
|
+
"""Optional. LlmAgent.include_contents."""
|
627
|
+
|
628
|
+
tools: Optional[list[CodeConfig]] = None
|
629
|
+
"""Optional. LlmAgent.tools.
|
630
|
+
|
631
|
+
Examples:
|
632
|
+
|
633
|
+
For ADK built-in tools in `google.adk.tools` package, they can be referenced
|
634
|
+
directly with the name:
|
635
|
+
|
636
|
+
```
|
637
|
+
tools:
|
638
|
+
- name: google_search
|
639
|
+
- name: load_memory
|
640
|
+
```
|
641
|
+
|
642
|
+
For user-defined tools, they can be referenced with fully qualified name:
|
643
|
+
|
644
|
+
```
|
645
|
+
tools:
|
646
|
+
- name: my_library.my_tools.my_tool
|
647
|
+
```
|
648
|
+
|
649
|
+
For tools that needs to be created via functions:
|
650
|
+
|
651
|
+
```
|
652
|
+
tools:
|
653
|
+
- name: my_library.my_tools.create_tool
|
654
|
+
args:
|
655
|
+
- name: param1
|
656
|
+
value: value1
|
657
|
+
- name: param2
|
658
|
+
value: value2
|
659
|
+
```
|
660
|
+
|
661
|
+
For more advanced tools, instead of specifying arguments in config, it's
|
662
|
+
recommended to define them in Python files and reference them. E.g.,
|
663
|
+
|
664
|
+
```
|
665
|
+
# tools.py
|
666
|
+
my_mcp_toolset = MCPToolset(
|
667
|
+
connection_params=StdioServerParameters(
|
668
|
+
command="npx",
|
669
|
+
args=["-y", "@notionhq/notion-mcp-server"],
|
670
|
+
env={"OPENAPI_MCP_HEADERS": NOTION_HEADERS},
|
671
|
+
)
|
672
|
+
)
|
673
|
+
```
|
674
|
+
|
675
|
+
Then, reference the toolset in config:
|
676
|
+
|
677
|
+
```
|
678
|
+
tools:
|
679
|
+
- name: tools.my_mcp_toolset
|
680
|
+
```
|
681
|
+
"""
|
682
|
+
|
683
|
+
before_model_callbacks: Optional[List[CodeConfig]] = None
|
684
|
+
"""Optional. LlmAgent.before_model_callbacks.
|
685
|
+
|
686
|
+
Example:
|
687
|
+
|
688
|
+
```
|
689
|
+
before_model_callbacks:
|
690
|
+
- name: my_library.callbacks.before_model_callback
|
691
|
+
```
|
692
|
+
"""
|
693
|
+
|
694
|
+
after_model_callbacks: Optional[List[CodeConfig]] = None
|
695
|
+
"""Optional. LlmAgent.after_model_callbacks."""
|
696
|
+
|
697
|
+
before_tool_callbacks: Optional[List[CodeConfig]] = None
|
698
|
+
"""Optional. LlmAgent.before_tool_callbacks."""
|
699
|
+
|
700
|
+
after_tool_callbacks: Optional[List[CodeConfig]] = None
|
701
|
+
"""Optional. LlmAgent.after_tool_callbacks."""
|
google/adk/agents/loop_agent.py
CHANGED
@@ -16,14 +16,20 @@
|
|
16
16
|
|
17
17
|
from __future__ import annotations
|
18
18
|
|
19
|
+
from typing import Any
|
19
20
|
from typing import AsyncGenerator
|
21
|
+
from typing import Dict
|
22
|
+
from typing import Literal
|
20
23
|
from typing import Optional
|
24
|
+
from typing import Type
|
21
25
|
|
22
26
|
from typing_extensions import override
|
23
27
|
|
24
28
|
from ..agents.invocation_context import InvocationContext
|
25
29
|
from ..events.event import Event
|
30
|
+
from ..utils.feature_decorator import working_in_progress
|
26
31
|
from .base_agent import BaseAgent
|
32
|
+
from .base_agent import BaseAgentConfig
|
27
33
|
|
28
34
|
|
29
35
|
class LoopAgent(BaseAgent):
|
@@ -60,3 +66,26 @@ class LoopAgent(BaseAgent):
|
|
60
66
|
) -> AsyncGenerator[Event, None]:
|
61
67
|
raise NotImplementedError('This is not supported yet for LoopAgent.')
|
62
68
|
yield # AsyncGenerator requires having at least one yield statement
|
69
|
+
|
70
|
+
@classmethod
|
71
|
+
@override
|
72
|
+
@working_in_progress('LoopAgent.from_config is not ready for use.')
|
73
|
+
def from_config(
|
74
|
+
cls: Type[LoopAgent],
|
75
|
+
config: LoopAgentConfig,
|
76
|
+
config_abs_path: str,
|
77
|
+
) -> LoopAgent:
|
78
|
+
agent = super().from_config(config, config_abs_path)
|
79
|
+
if config.max_iterations:
|
80
|
+
agent.max_iterations = config.max_iterations
|
81
|
+
return agent
|
82
|
+
|
83
|
+
|
84
|
+
@working_in_progress('LoopAgentConfig is not ready for use.')
|
85
|
+
class LoopAgentConfig(BaseAgentConfig):
|
86
|
+
"""The config for the YAML schema of a LoopAgent."""
|
87
|
+
|
88
|
+
agent_class: Literal['LoopAgent'] = 'LoopAgent'
|
89
|
+
|
90
|
+
max_iterations: Optional[int] = None
|
91
|
+
"""Optional. LoopAgent.max_iterations."""
|
@@ -18,9 +18,13 @@ from __future__ import annotations
|
|
18
18
|
|
19
19
|
import asyncio
|
20
20
|
from typing import AsyncGenerator
|
21
|
+
from typing import Literal
|
22
|
+
from typing import Type
|
21
23
|
|
22
24
|
from typing_extensions import override
|
23
25
|
|
26
|
+
from ..agents.base_agent import BaseAgentConfig
|
27
|
+
from ..agents.base_agent import working_in_progress
|
24
28
|
from ..agents.invocation_context import InvocationContext
|
25
29
|
from ..events.event import Event
|
26
30
|
from .base_agent import BaseAgent
|
@@ -33,9 +37,9 @@ def _create_branch_ctx_for_sub_agent(
|
|
33
37
|
) -> InvocationContext:
|
34
38
|
"""Create isolated branch for every sub-agent."""
|
35
39
|
invocation_context = invocation_context.model_copy()
|
36
|
-
branch_suffix = f
|
40
|
+
branch_suffix = f'{agent.name}.{sub_agent.name}'
|
37
41
|
invocation_context.branch = (
|
38
|
-
f
|
42
|
+
f'{invocation_context.branch}.{branch_suffix}'
|
39
43
|
if invocation_context.branch
|
40
44
|
else branch_suffix
|
41
45
|
)
|
@@ -109,5 +113,22 @@ class ParallelAgent(BaseAgent):
|
|
109
113
|
async def _run_live_impl(
|
110
114
|
self, ctx: InvocationContext
|
111
115
|
) -> AsyncGenerator[Event, None]:
|
112
|
-
raise NotImplementedError(
|
116
|
+
raise NotImplementedError('This is not supported yet for ParallelAgent.')
|
113
117
|
yield # AsyncGenerator requires having at least one yield statement
|
118
|
+
|
119
|
+
@classmethod
|
120
|
+
@override
|
121
|
+
@working_in_progress('ParallelAgent.from_config is not ready for use.')
|
122
|
+
def from_config(
|
123
|
+
cls: Type[ParallelAgent],
|
124
|
+
config: ParallelAgentConfig,
|
125
|
+
config_abs_path: str,
|
126
|
+
) -> ParallelAgent:
|
127
|
+
return super().from_config(config, config_abs_path)
|
128
|
+
|
129
|
+
|
130
|
+
@working_in_progress('ParallelAgentConfig is not ready for use.')
|
131
|
+
class ParallelAgentConfig(BaseAgentConfig):
|
132
|
+
"""The config for the YAML schema of a ParallelAgent."""
|
133
|
+
|
134
|
+
agent_class: Literal['ParallelAgent'] = 'ParallelAgent'
|
@@ -26,7 +26,7 @@ import uuid
|
|
26
26
|
|
27
27
|
try:
|
28
28
|
from a2a.client import A2AClient
|
29
|
-
from a2a.client.client import A2ACardResolver
|
29
|
+
from a2a.client.client import A2ACardResolver
|
30
30
|
from a2a.types import AgentCard
|
31
31
|
from a2a.types import Message as A2AMessage
|
32
32
|
from a2a.types import MessageSendParams as A2AMessageSendParams
|
@@ -35,7 +35,6 @@ try:
|
|
35
35
|
from a2a.types import SendMessageRequest
|
36
36
|
from a2a.types import SendMessageSuccessResponse
|
37
37
|
from a2a.types import Task as A2ATask
|
38
|
-
|
39
38
|
except ImportError as e:
|
40
39
|
import sys
|
41
40
|
|
@@ -46,6 +45,12 @@ except ImportError as e:
|
|
46
45
|
else:
|
47
46
|
raise e
|
48
47
|
|
48
|
+
try:
|
49
|
+
from a2a.utils.constants import AGENT_CARD_WELL_KNOWN_PATH
|
50
|
+
except ImportError:
|
51
|
+
# Fallback for older versions of a2a-sdk.
|
52
|
+
AGENT_CARD_WELL_KNOWN_PATH = "/.well-known/agent.json"
|
53
|
+
|
49
54
|
from google.genai import types as genai_types
|
50
55
|
import httpx
|
51
56
|
|
@@ -63,11 +68,18 @@ from ..flows.llm_flows.functions import find_matching_function_call
|
|
63
68
|
from ..utils.feature_decorator import experimental
|
64
69
|
from .base_agent import BaseAgent
|
65
70
|
|
71
|
+
__all__ = [
|
72
|
+
"A2AClientError",
|
73
|
+
"AGENT_CARD_WELL_KNOWN_PATH",
|
74
|
+
"AgentCardResolutionError",
|
75
|
+
"RemoteA2aAgent",
|
76
|
+
]
|
77
|
+
|
78
|
+
|
66
79
|
# Constants
|
67
80
|
A2A_METADATA_PREFIX = "a2a:"
|
68
81
|
DEFAULT_TIMEOUT = 600.0
|
69
82
|
|
70
|
-
|
71
83
|
logger = logging.getLogger("google_adk." + __name__)
|
72
84
|
|
73
85
|
|
@@ -17,9 +17,13 @@
|
|
17
17
|
from __future__ import annotations
|
18
18
|
|
19
19
|
from typing import AsyncGenerator
|
20
|
+
from typing import Literal
|
21
|
+
from typing import Type
|
20
22
|
|
21
23
|
from typing_extensions import override
|
22
24
|
|
25
|
+
from ..agents.base_agent import BaseAgentConfig
|
26
|
+
from ..agents.base_agent import working_in_progress
|
23
27
|
from ..agents.invocation_context import InvocationContext
|
24
28
|
from ..events.event import Event
|
25
29
|
from .base_agent import BaseAgent
|
@@ -60,7 +64,7 @@ class SequentialAgent(BaseAgent):
|
|
60
64
|
Signals that the model has successfully completed the user's question
|
61
65
|
or task.
|
62
66
|
"""
|
63
|
-
return
|
67
|
+
return 'Task completion signaled.'
|
64
68
|
|
65
69
|
if isinstance(sub_agent, LlmAgent):
|
66
70
|
# Use function name to dedupe.
|
@@ -74,3 +78,20 @@ class SequentialAgent(BaseAgent):
|
|
74
78
|
for sub_agent in self.sub_agents:
|
75
79
|
async for event in sub_agent.run_live(ctx):
|
76
80
|
yield event
|
81
|
+
|
82
|
+
@classmethod
|
83
|
+
@override
|
84
|
+
@working_in_progress('SequentialAgent.from_config is not ready for use.')
|
85
|
+
def from_config(
|
86
|
+
cls: Type[SequentialAgent],
|
87
|
+
config: SequentialAgentConfig,
|
88
|
+
config_abs_path: str,
|
89
|
+
) -> SequentialAgent:
|
90
|
+
return super().from_config(config, config_abs_path)
|
91
|
+
|
92
|
+
|
93
|
+
@working_in_progress('SequentialAgentConfig is not ready for use.')
|
94
|
+
class SequentialAgentConfig(BaseAgentConfig):
|
95
|
+
"""The config for the YAML schema of a SequentialAgent."""
|
96
|
+
|
97
|
+
agent_class: Literal['SequentialAgent'] = 'SequentialAgent'
|
@@ -12,7 +12,14 @@
|
|
12
12
|
# See the License for the specific language governing permissions and
|
13
13
|
# limitations under the License.
|
14
14
|
|
15
|
-
"""An artifact service implementation using Google Cloud Storage (GCS).
|
15
|
+
"""An artifact service implementation using Google Cloud Storage (GCS).
|
16
|
+
|
17
|
+
The blob name format used depends on whether the filename has a user namespace:
|
18
|
+
- For files with user namespace (starting with "user:"):
|
19
|
+
{app_name}/{user_id}/user/{filename}/{version}
|
20
|
+
- For regular session-scoped files:
|
21
|
+
{app_name}/{user_id}/{session_id}/{filename}/{version}
|
22
|
+
"""
|
16
23
|
from __future__ import annotations
|
17
24
|
|
18
25
|
import logging
|
@@ -187,10 +194,25 @@ class GcsArtifactService(BaseArtifactService):
|
|
187
194
|
async def list_versions(
|
188
195
|
self, *, app_name: str, user_id: str, session_id: str, filename: str
|
189
196
|
) -> list[int]:
|
197
|
+
"""Lists all available versions of an artifact.
|
198
|
+
|
199
|
+
This method retrieves all versions of a specific artifact by querying GCS blobs
|
200
|
+
that match the constructed blob name prefix.
|
201
|
+
|
202
|
+
Args:
|
203
|
+
app_name: The name of the application.
|
204
|
+
user_id: The ID of the user who owns the artifact.
|
205
|
+
session_id: The ID of the session (ignored for user-namespaced files).
|
206
|
+
filename: The name of the artifact file.
|
207
|
+
|
208
|
+
Returns:
|
209
|
+
A list of version numbers (integers) available for the specified artifact.
|
210
|
+
Returns an empty list if no versions are found.
|
211
|
+
"""
|
190
212
|
prefix = self._get_blob_name(app_name, user_id, session_id, filename, "")
|
191
213
|
blobs = self.storage_client.list_blobs(self.bucket, prefix=prefix)
|
192
214
|
versions = []
|
193
215
|
for blob in blobs:
|
194
|
-
_,
|
216
|
+
*_, version = blob.name.split("/")
|
195
217
|
versions.append(int(version))
|
196
218
|
return versions
|
google/adk/auth/auth_handler.py
CHANGED
@@ -30,9 +30,9 @@ if TYPE_CHECKING:
|
|
30
30
|
try:
|
31
31
|
from authlib.integrations.requests_client import OAuth2Session
|
32
32
|
|
33
|
-
|
33
|
+
AUTHLIB_AVAILABLE = True
|
34
34
|
except ImportError:
|
35
|
-
|
35
|
+
AUTHLIB_AVAILABLE = False
|
36
36
|
|
37
37
|
|
38
38
|
class AuthHandler:
|
@@ -146,7 +146,7 @@ class AuthHandler:
|
|
146
146
|
ValueError: If the authorization endpoint is not configured in the auth
|
147
147
|
scheme.
|
148
148
|
"""
|
149
|
-
if not
|
149
|
+
if not AUTHLIB_AVAILABLE:
|
150
150
|
return (
|
151
151
|
self.auth_config.raw_auth_credential.model_copy(deep=True)
|
152
152
|
if self.auth_config.raw_auth_credential
|
@@ -16,7 +16,7 @@ from __future__ import annotations
|
|
16
16
|
|
17
17
|
from typing import Optional
|
18
18
|
|
19
|
-
from ..
|
19
|
+
from ..agents.callback_context import CallbackContext
|
20
20
|
from ..utils.feature_decorator import experimental
|
21
21
|
from .auth_credential import AuthCredential
|
22
22
|
from .auth_credential import AuthCredentialTypes
|
@@ -63,7 +63,7 @@ class CredentialManager:
|
|
63
63
|
)
|
64
64
|
|
65
65
|
# Load and prepare credential
|
66
|
-
credential = await manager.load_auth_credential(
|
66
|
+
credential = await manager.load_auth_credential(callback_context)
|
67
67
|
```
|
68
68
|
"""
|
69
69
|
|
@@ -100,11 +100,11 @@ class CredentialManager:
|
|
100
100
|
"""
|
101
101
|
self._exchanger_registry.register(credential_type, exchanger_instance)
|
102
102
|
|
103
|
-
async def request_credential(self,
|
104
|
-
|
103
|
+
async def request_credential(self, callback_context: CallbackContext) -> None:
|
104
|
+
callback_context.request_credential(self._auth_config)
|
105
105
|
|
106
106
|
async def get_auth_credential(
|
107
|
-
self,
|
107
|
+
self, callback_context: CallbackContext
|
108
108
|
) -> Optional[AuthCredential]:
|
109
109
|
"""Load and prepare authentication credential through a structured workflow."""
|
110
110
|
|
@@ -116,14 +116,14 @@ class CredentialManager:
|
|
116
116
|
return self._auth_config.raw_auth_credential
|
117
117
|
|
118
118
|
# Step 3: Try to load existing processed credential
|
119
|
-
credential = await self._load_existing_credential(
|
119
|
+
credential = await self._load_existing_credential(callback_context)
|
120
120
|
|
121
121
|
# Step 4: If no existing credential, load from auth response
|
122
122
|
# TODO instead of load from auth response, we can store auth response in
|
123
123
|
# credential service.
|
124
124
|
was_from_auth_response = False
|
125
125
|
if not credential:
|
126
|
-
credential = await self._load_from_auth_response(
|
126
|
+
credential = await self._load_from_auth_response(callback_context)
|
127
127
|
was_from_auth_response = True
|
128
128
|
|
129
129
|
# Step 5: If still no credential available, return None
|
@@ -134,22 +134,23 @@ class CredentialManager:
|
|
134
134
|
credential, was_exchanged = await self._exchange_credential(credential)
|
135
135
|
|
136
136
|
# Step 7: Refresh credential if expired
|
137
|
+
was_refreshed = False
|
137
138
|
if not was_exchanged:
|
138
139
|
credential, was_refreshed = await self._refresh_credential(credential)
|
139
140
|
|
140
141
|
# Step 8: Save credential if it was modified
|
141
142
|
if was_from_auth_response or was_exchanged or was_refreshed:
|
142
|
-
await self._save_credential(
|
143
|
+
await self._save_credential(callback_context, credential)
|
143
144
|
|
144
145
|
return credential
|
145
146
|
|
146
147
|
async def _load_existing_credential(
|
147
|
-
self,
|
148
|
+
self, callback_context: CallbackContext
|
148
149
|
) -> Optional[AuthCredential]:
|
149
150
|
"""Load existing credential from credential service or cached exchanged credential."""
|
150
151
|
|
151
152
|
# Try loading from credential service first
|
152
|
-
credential = await self._load_from_credential_service(
|
153
|
+
credential = await self._load_from_credential_service(callback_context)
|
153
154
|
if credential:
|
154
155
|
return credential
|
155
156
|
|
@@ -160,23 +161,21 @@ class CredentialManager:
|
|
160
161
|
return None
|
161
162
|
|
162
163
|
async def _load_from_credential_service(
|
163
|
-
self,
|
164
|
+
self, callback_context: CallbackContext
|
164
165
|
) -> Optional[AuthCredential]:
|
165
166
|
"""Load credential from credential service if available."""
|
166
|
-
credential_service =
|
167
|
+
credential_service = callback_context._invocation_context.credential_service
|
167
168
|
if credential_service:
|
168
169
|
# Note: This should be made async in a future refactor
|
169
170
|
# For now, assuming synchronous operation
|
170
|
-
return await
|
171
|
-
self._auth_config, tool_context
|
172
|
-
)
|
171
|
+
return await callback_context.load_credential(self._auth_config)
|
173
172
|
return None
|
174
173
|
|
175
174
|
async def _load_from_auth_response(
|
176
|
-
self,
|
175
|
+
self, callback_context: CallbackContext
|
177
176
|
) -> Optional[AuthCredential]:
|
178
|
-
"""Load credential from auth response in
|
179
|
-
return
|
177
|
+
"""Load credential from auth response in callback context."""
|
178
|
+
return callback_context.get_auth_response(self._auth_config)
|
180
179
|
|
181
180
|
async def _exchange_credential(
|
182
181
|
self, credential: AuthCredential
|
@@ -251,11 +250,12 @@ class CredentialManager:
|
|
251
250
|
# Additional validation can be added here
|
252
251
|
|
253
252
|
async def _save_credential(
|
254
|
-
self,
|
253
|
+
self, callback_context: CallbackContext, credential: AuthCredential
|
255
254
|
) -> None:
|
256
255
|
"""Save credential to credential service if available."""
|
257
|
-
|
256
|
+
# Update the exchanged credential in config
|
257
|
+
self._auth_config.exchanged_auth_credential = credential
|
258
|
+
|
259
|
+
credential_service = callback_context._invocation_context.credential_service
|
258
260
|
if credential_service:
|
259
|
-
|
260
|
-
self._auth_config.exchanged_auth_credential = credential
|
261
|
-
await credential_service.save_credential(self._auth_config, tool_context)
|
261
|
+
await callback_context.save_credential(self._auth_config)
|
@@ -18,7 +18,7 @@ from abc import ABC
|
|
18
18
|
from abc import abstractmethod
|
19
19
|
from typing import Optional
|
20
20
|
|
21
|
-
from ...
|
21
|
+
from ...agents.callback_context import CallbackContext
|
22
22
|
from ...utils.feature_decorator import experimental
|
23
23
|
from ..auth_credential import AuthCredential
|
24
24
|
from ..auth_tool import AuthConfig
|
@@ -33,10 +33,10 @@ class BaseCredentialService(ABC):
|
|
33
33
|
async def load_credential(
|
34
34
|
self,
|
35
35
|
auth_config: AuthConfig,
|
36
|
-
|
36
|
+
callback_context: CallbackContext,
|
37
37
|
) -> Optional[AuthCredential]:
|
38
38
|
"""
|
39
|
-
Loads the credential by auth config and current
|
39
|
+
Loads the credential by auth config and current callback context from the
|
40
40
|
backend credential store.
|
41
41
|
|
42
42
|
Args:
|
@@ -44,7 +44,7 @@ class BaseCredentialService(ABC):
|
|
44
44
|
credential information. auth_config.get_credential_key will be used to
|
45
45
|
build the key to load the credential.
|
46
46
|
|
47
|
-
|
47
|
+
callback_context: The context of the current invocation when the tool is
|
48
48
|
trying to load the credential.
|
49
49
|
|
50
50
|
Returns:
|
@@ -56,7 +56,7 @@ class BaseCredentialService(ABC):
|
|
56
56
|
async def save_credential(
|
57
57
|
self,
|
58
58
|
auth_config: AuthConfig,
|
59
|
-
|
59
|
+
callback_context: CallbackContext,
|
60
60
|
) -> None:
|
61
61
|
"""
|
62
62
|
Saves the exchanged_auth_credential in auth config to the backend credential
|
@@ -67,7 +67,7 @@ class BaseCredentialService(ABC):
|
|
67
67
|
credential information. auth_config.get_credential_key will be used to
|
68
68
|
build the key to save the credential.
|
69
69
|
|
70
|
-
|
70
|
+
callback_context: The context of the current invocation when the tool is
|
71
71
|
trying to save the credential.
|
72
72
|
|
73
73
|
Returns:
|