google-adk-extras 0.1.1__py3-none-any.whl → 0.2.3__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_extras/__init__.py +31 -1
- google_adk_extras/adk_builder.py +1030 -0
- google_adk_extras/artifacts/__init__.py +25 -12
- google_adk_extras/artifacts/base_custom_artifact_service.py +148 -11
- google_adk_extras/artifacts/local_folder_artifact_service.py +133 -13
- google_adk_extras/artifacts/s3_artifact_service.py +135 -19
- google_adk_extras/artifacts/sql_artifact_service.py +109 -10
- google_adk_extras/credentials/__init__.py +34 -0
- google_adk_extras/credentials/base_custom_credential_service.py +113 -0
- google_adk_extras/credentials/github_oauth2_credential_service.py +213 -0
- google_adk_extras/credentials/google_oauth2_credential_service.py +216 -0
- google_adk_extras/credentials/http_basic_auth_credential_service.py +388 -0
- google_adk_extras/credentials/jwt_credential_service.py +345 -0
- google_adk_extras/credentials/microsoft_oauth2_credential_service.py +250 -0
- google_adk_extras/credentials/x_oauth2_credential_service.py +240 -0
- google_adk_extras/custom_agent_loader.py +156 -0
- google_adk_extras/enhanced_adk_web_server.py +137 -0
- google_adk_extras/enhanced_fastapi.py +470 -0
- google_adk_extras/enhanced_runner.py +38 -0
- google_adk_extras/memory/__init__.py +30 -13
- google_adk_extras/memory/base_custom_memory_service.py +37 -5
- google_adk_extras/memory/sql_memory_service.py +105 -19
- google_adk_extras/memory/yaml_file_memory_service.py +115 -22
- google_adk_extras/sessions/__init__.py +29 -13
- google_adk_extras/sessions/base_custom_session_service.py +133 -11
- google_adk_extras/sessions/sql_session_service.py +127 -16
- google_adk_extras/sessions/yaml_file_session_service.py +122 -14
- google_adk_extras-0.2.3.dist-info/METADATA +302 -0
- google_adk_extras-0.2.3.dist-info/RECORD +37 -0
- google_adk_extras/py.typed +0 -0
- google_adk_extras-0.1.1.dist-info/METADATA +0 -175
- google_adk_extras-0.1.1.dist-info/RECORD +0 -25
- {google_adk_extras-0.1.1.dist-info → google_adk_extras-0.2.3.dist-info}/WHEEL +0 -0
- {google_adk_extras-0.1.1.dist-info → google_adk_extras-0.2.3.dist-info}/licenses/LICENSE +0 -0
- {google_adk_extras-0.1.1.dist-info → google_adk_extras-0.2.3.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,240 @@
|
|
1
|
+
"""X (Twitter) OAuth2 credential service implementation."""
|
2
|
+
|
3
|
+
from typing import Optional, List
|
4
|
+
import logging
|
5
|
+
|
6
|
+
from google.adk.auth.credential_service.session_state_credential_service import SessionStateCredentialService
|
7
|
+
from google.adk.auth.credential_service.base_credential_service import CallbackContext
|
8
|
+
from google.adk.auth import AuthConfig, AuthCredential, AuthCredentialTypes
|
9
|
+
from google.adk.auth.auth_credential import OAuth2Auth
|
10
|
+
from fastapi.openapi.models import OAuth2
|
11
|
+
from fastapi.openapi.models import OAuthFlowAuthorizationCode, OAuthFlows
|
12
|
+
|
13
|
+
from .base_custom_credential_service import BaseCustomCredentialService
|
14
|
+
|
15
|
+
logger = logging.getLogger(__name__)
|
16
|
+
|
17
|
+
|
18
|
+
class XOAuth2CredentialService(BaseCustomCredentialService):
|
19
|
+
"""X (Twitter) OAuth2 credential service for handling X API authentication flows.
|
20
|
+
|
21
|
+
This service provides pre-configured OAuth2 flows for X API v2 including
|
22
|
+
reading tweets, posting content, and managing user data.
|
23
|
+
|
24
|
+
Args:
|
25
|
+
client_id: The X OAuth2 client ID from X Developer Portal.
|
26
|
+
client_secret: The X OAuth2 client secret from X Developer Portal.
|
27
|
+
scopes: List of OAuth2 scopes to request. Common scopes include:
|
28
|
+
- "tweet.read" - Read tweets
|
29
|
+
- "tweet.write" - Write tweets
|
30
|
+
- "tweet.moderate.write" - Moderate tweets
|
31
|
+
- "users.read" - Read user information
|
32
|
+
- "follows.read" - Read follows information
|
33
|
+
- "follows.write" - Manage follows
|
34
|
+
- "offline.access" - Maintain access when user is offline
|
35
|
+
- "space.read" - Read Spaces information
|
36
|
+
- "mute.read" - Read muted accounts
|
37
|
+
- "mute.write" - Manage muted accounts
|
38
|
+
- "like.read" - Read likes information
|
39
|
+
- "like.write" - Manage likes
|
40
|
+
- "list.read" - Read list information
|
41
|
+
- "list.write" - Manage lists
|
42
|
+
- "block.read" - Read blocked accounts
|
43
|
+
- "block.write" - Manage blocked accounts
|
44
|
+
use_session_state: If True, stores credentials in session state. If False,
|
45
|
+
uses in-memory storage. Default is True for persistence.
|
46
|
+
|
47
|
+
Example:
|
48
|
+
```python
|
49
|
+
credential_service = XOAuth2CredentialService(
|
50
|
+
client_id="your-x-client-id",
|
51
|
+
client_secret="your-x-client-secret",
|
52
|
+
scopes=["tweet.read", "tweet.write", "users.read", "offline.access"]
|
53
|
+
)
|
54
|
+
await credential_service.initialize()
|
55
|
+
|
56
|
+
# Use with Runner
|
57
|
+
runner = Runner(
|
58
|
+
agent=agent,
|
59
|
+
session_service=session_service,
|
60
|
+
credential_service=credential_service,
|
61
|
+
app_name="my_app"
|
62
|
+
)
|
63
|
+
```
|
64
|
+
"""
|
65
|
+
|
66
|
+
# X OAuth2 endpoints
|
67
|
+
X_AUTH_URL = "https://twitter.com/i/oauth2/authorize"
|
68
|
+
X_TOKEN_URL = "https://api.twitter.com/2/oauth2/token"
|
69
|
+
|
70
|
+
# Common X OAuth2 scopes
|
71
|
+
COMMON_SCOPES = {
|
72
|
+
# Tweet scopes
|
73
|
+
"tweet.read": "Read tweets",
|
74
|
+
"tweet.write": "Write tweets",
|
75
|
+
"tweet.moderate.write": "Hide and unhide replies to your tweets",
|
76
|
+
|
77
|
+
# User scopes
|
78
|
+
"users.read": "Read user profile information",
|
79
|
+
|
80
|
+
# Follows scopes
|
81
|
+
"follows.read": "Read who a user is following or who is following a user",
|
82
|
+
"follows.write": "Follow and unfollow other users",
|
83
|
+
|
84
|
+
# Offline access
|
85
|
+
"offline.access": "Maintain access to accounts when users are offline",
|
86
|
+
|
87
|
+
# Space scopes
|
88
|
+
"space.read": "Read Spaces information",
|
89
|
+
|
90
|
+
# Mute scopes
|
91
|
+
"mute.read": "Read muted accounts",
|
92
|
+
"mute.write": "Mute and unmute accounts",
|
93
|
+
|
94
|
+
# Like scopes
|
95
|
+
"like.read": "Read liked tweets",
|
96
|
+
"like.write": "Like and unlike tweets",
|
97
|
+
|
98
|
+
# List scopes
|
99
|
+
"list.read": "Read list information",
|
100
|
+
"list.write": "Create and manage lists",
|
101
|
+
|
102
|
+
# Block scopes
|
103
|
+
"block.read": "Read blocked accounts",
|
104
|
+
"block.write": "Block and unblock accounts",
|
105
|
+
|
106
|
+
# Bookmark scopes
|
107
|
+
"bookmark.read": "Read bookmarked tweets",
|
108
|
+
"bookmark.write": "Bookmark and unbookmark tweets",
|
109
|
+
|
110
|
+
# Direct message scopes
|
111
|
+
"dm.read": "Read direct messages",
|
112
|
+
"dm.write": "Send direct messages",
|
113
|
+
}
|
114
|
+
|
115
|
+
def __init__(
|
116
|
+
self,
|
117
|
+
client_id: str,
|
118
|
+
client_secret: str,
|
119
|
+
scopes: Optional[List[str]] = None,
|
120
|
+
use_session_state: bool = True
|
121
|
+
):
|
122
|
+
"""Initialize the X OAuth2 credential service.
|
123
|
+
|
124
|
+
Args:
|
125
|
+
client_id: X OAuth2 client ID.
|
126
|
+
client_secret: X OAuth2 client secret.
|
127
|
+
scopes: List of OAuth2 scopes to request.
|
128
|
+
use_session_state: Whether to use session state for credential storage.
|
129
|
+
"""
|
130
|
+
super().__init__()
|
131
|
+
self.client_id = client_id
|
132
|
+
self.client_secret = client_secret
|
133
|
+
self.scopes = scopes or ["tweet.read", "users.read", "offline.access"]
|
134
|
+
self.use_session_state = use_session_state
|
135
|
+
|
136
|
+
# Underlying credential service for storage
|
137
|
+
if use_session_state:
|
138
|
+
self._storage_service = SessionStateCredentialService()
|
139
|
+
else:
|
140
|
+
from google.adk.auth.credential_service.in_memory_credential_service import InMemoryCredentialService
|
141
|
+
self._storage_service = InMemoryCredentialService()
|
142
|
+
|
143
|
+
async def _initialize_impl(self) -> None:
|
144
|
+
"""Initialize the X OAuth2 credential service.
|
145
|
+
|
146
|
+
Validates the client credentials and sets up the OAuth2 auth scheme.
|
147
|
+
|
148
|
+
Raises:
|
149
|
+
ValueError: If client_id or client_secret is missing.
|
150
|
+
"""
|
151
|
+
if not self.client_id:
|
152
|
+
raise ValueError("X OAuth2 client_id is required")
|
153
|
+
if not self.client_secret:
|
154
|
+
raise ValueError("X OAuth2 client_secret is required")
|
155
|
+
if not self.scopes:
|
156
|
+
raise ValueError("At least one OAuth2 scope is required")
|
157
|
+
|
158
|
+
# Validate scopes against known X scopes
|
159
|
+
unknown_scopes = set(self.scopes) - set(self.COMMON_SCOPES.keys())
|
160
|
+
if unknown_scopes:
|
161
|
+
logger.warning(f"Unknown X OAuth2 scopes: {unknown_scopes}")
|
162
|
+
|
163
|
+
logger.info(f"Initialized X OAuth2 credential service with scopes: {self.scopes}")
|
164
|
+
|
165
|
+
def create_auth_config(self) -> AuthConfig:
|
166
|
+
"""Create an AuthConfig for X OAuth2 authentication.
|
167
|
+
|
168
|
+
Returns:
|
169
|
+
AuthConfig: Configured auth config for X OAuth2 flow.
|
170
|
+
"""
|
171
|
+
self._check_initialized()
|
172
|
+
|
173
|
+
# Create OAuth2 auth scheme
|
174
|
+
auth_scheme = OAuth2(
|
175
|
+
flows=OAuthFlows(
|
176
|
+
authorizationCode=OAuthFlowAuthorizationCode(
|
177
|
+
authorizationUrl=self.X_AUTH_URL,
|
178
|
+
tokenUrl=self.X_TOKEN_URL,
|
179
|
+
scopes={
|
180
|
+
scope: self.COMMON_SCOPES.get(scope, f"X API scope: {scope}")
|
181
|
+
for scope in self.scopes
|
182
|
+
}
|
183
|
+
)
|
184
|
+
)
|
185
|
+
)
|
186
|
+
|
187
|
+
# Create OAuth2 credential
|
188
|
+
auth_credential = AuthCredential(
|
189
|
+
auth_type=AuthCredentialTypes.OAUTH2,
|
190
|
+
oauth2=OAuth2Auth(
|
191
|
+
client_id=self.client_id,
|
192
|
+
client_secret=self.client_secret
|
193
|
+
)
|
194
|
+
)
|
195
|
+
|
196
|
+
return AuthConfig(
|
197
|
+
auth_scheme=auth_scheme,
|
198
|
+
raw_auth_credential=auth_credential
|
199
|
+
)
|
200
|
+
|
201
|
+
async def load_credential(
|
202
|
+
self,
|
203
|
+
auth_config: AuthConfig,
|
204
|
+
callback_context: CallbackContext,
|
205
|
+
) -> Optional[AuthCredential]:
|
206
|
+
"""Load X OAuth2 credential from storage.
|
207
|
+
|
208
|
+
Args:
|
209
|
+
auth_config: The auth config containing credential key information.
|
210
|
+
callback_context: The current callback context.
|
211
|
+
|
212
|
+
Returns:
|
213
|
+
Optional[AuthCredential]: The stored credential or None if not found.
|
214
|
+
"""
|
215
|
+
self._check_initialized()
|
216
|
+
return await self._storage_service.load_credential(auth_config, callback_context)
|
217
|
+
|
218
|
+
async def save_credential(
|
219
|
+
self,
|
220
|
+
auth_config: AuthConfig,
|
221
|
+
callback_context: CallbackContext,
|
222
|
+
) -> None:
|
223
|
+
"""Save X OAuth2 credential to storage.
|
224
|
+
|
225
|
+
Args:
|
226
|
+
auth_config: The auth config containing the credential to save.
|
227
|
+
callback_context: The current callback context.
|
228
|
+
"""
|
229
|
+
self._check_initialized()
|
230
|
+
await self._storage_service.save_credential(auth_config, callback_context)
|
231
|
+
|
232
|
+
logger.info(f"Saved X OAuth2 credential for user {callback_context._invocation_context.user_id}")
|
233
|
+
|
234
|
+
def get_supported_scopes(self) -> dict:
|
235
|
+
"""Get dictionary of supported X OAuth2 scopes and their descriptions.
|
236
|
+
|
237
|
+
Returns:
|
238
|
+
dict: Mapping of scope names to descriptions.
|
239
|
+
"""
|
240
|
+
return self.COMMON_SCOPES.copy()
|
@@ -0,0 +1,156 @@
|
|
1
|
+
"""CustomAgentLoader - Enhanced agent loader for programmatic agent management.
|
2
|
+
|
3
|
+
This module provides CustomAgentLoader which extends Google ADK's agent loading
|
4
|
+
capabilities to support programmatically registered agent instances with
|
5
|
+
thread-safe registry management.
|
6
|
+
"""
|
7
|
+
|
8
|
+
import logging
|
9
|
+
import threading
|
10
|
+
from typing import Dict, List, Optional
|
11
|
+
|
12
|
+
from google.adk.agents.base_agent import BaseAgent
|
13
|
+
from google.adk.cli.utils.base_agent_loader import BaseAgentLoader
|
14
|
+
|
15
|
+
logger = logging.getLogger(__name__)
|
16
|
+
|
17
|
+
|
18
|
+
class CustomAgentLoader(BaseAgentLoader):
|
19
|
+
"""Enhanced agent loader for programmatic agent management.
|
20
|
+
|
21
|
+
This loader allows you to:
|
22
|
+
1. Register agent instances directly for programmatic control
|
23
|
+
2. Dynamically add/remove agents at runtime
|
24
|
+
3. Thread-safe access to agent registry
|
25
|
+
|
26
|
+
Examples:
|
27
|
+
# Register and use agents
|
28
|
+
loader = CustomAgentLoader()
|
29
|
+
loader.register_agent("my_agent", my_agent_instance)
|
30
|
+
agent = loader.load_agent("my_agent")
|
31
|
+
|
32
|
+
# List all registered agents
|
33
|
+
agents = loader.list_agents() # ['my_agent']
|
34
|
+
"""
|
35
|
+
|
36
|
+
def __init__(self):
|
37
|
+
"""Initialize CustomAgentLoader."""
|
38
|
+
self._registered_agents: Dict[str, BaseAgent] = {}
|
39
|
+
self._lock = threading.RLock() # Thread-safe access to registry
|
40
|
+
|
41
|
+
logger.debug("CustomAgentLoader initialized")
|
42
|
+
|
43
|
+
def register_agent(self, name: str, agent: BaseAgent) -> None:
|
44
|
+
"""Register an agent instance by name.
|
45
|
+
|
46
|
+
Args:
|
47
|
+
name: Agent name for discovery and loading.
|
48
|
+
agent: BaseAgent instance to register.
|
49
|
+
|
50
|
+
Raises:
|
51
|
+
ValueError: If name is empty or agent is not a BaseAgent instance.
|
52
|
+
"""
|
53
|
+
if not name or not name.strip():
|
54
|
+
raise ValueError("Agent name cannot be empty")
|
55
|
+
|
56
|
+
if not isinstance(agent, BaseAgent):
|
57
|
+
raise ValueError(f"Agent must be BaseAgent instance, got {type(agent)}")
|
58
|
+
|
59
|
+
with self._lock:
|
60
|
+
if name in self._registered_agents:
|
61
|
+
logger.info("Replacing existing registered agent: %s", name)
|
62
|
+
else:
|
63
|
+
logger.info("Registering new agent instance: %s", name)
|
64
|
+
|
65
|
+
self._registered_agents[name] = agent
|
66
|
+
|
67
|
+
def unregister_agent(self, name: str) -> bool:
|
68
|
+
"""Unregister an agent instance by name.
|
69
|
+
|
70
|
+
Args:
|
71
|
+
name: Name of agent to unregister.
|
72
|
+
|
73
|
+
Returns:
|
74
|
+
bool: True if agent was found and removed, False otherwise.
|
75
|
+
"""
|
76
|
+
with self._lock:
|
77
|
+
if name in self._registered_agents:
|
78
|
+
del self._registered_agents[name]
|
79
|
+
logger.info("Unregistered agent instance: %s", name)
|
80
|
+
return True
|
81
|
+
else:
|
82
|
+
logger.debug("Agent not found in registry: %s", name)
|
83
|
+
return False
|
84
|
+
|
85
|
+
def is_registered(self, name: str) -> bool:
|
86
|
+
"""Check if an agent is registered by name.
|
87
|
+
|
88
|
+
Args:
|
89
|
+
name: Agent name to check.
|
90
|
+
|
91
|
+
Returns:
|
92
|
+
bool: True if agent is registered, False otherwise.
|
93
|
+
"""
|
94
|
+
with self._lock:
|
95
|
+
return name in self._registered_agents
|
96
|
+
|
97
|
+
def get_registered_agents(self) -> Dict[str, BaseAgent]:
|
98
|
+
"""Get a copy of all registered agents.
|
99
|
+
|
100
|
+
Returns:
|
101
|
+
Dict[str, BaseAgent]: Copy of registered agents mapping.
|
102
|
+
"""
|
103
|
+
with self._lock:
|
104
|
+
return self._registered_agents.copy()
|
105
|
+
|
106
|
+
def clear_registry(self) -> None:
|
107
|
+
"""Clear all registered agents from the registry."""
|
108
|
+
with self._lock:
|
109
|
+
count = len(self._registered_agents)
|
110
|
+
self._registered_agents.clear()
|
111
|
+
logger.info("Cleared %d registered agents", count)
|
112
|
+
|
113
|
+
def load_agent(self, name: str) -> BaseAgent:
|
114
|
+
"""Load an agent by name.
|
115
|
+
|
116
|
+
Args:
|
117
|
+
name: Name of agent to load.
|
118
|
+
|
119
|
+
Returns:
|
120
|
+
BaseAgent: The loaded agent instance.
|
121
|
+
|
122
|
+
Raises:
|
123
|
+
ValueError: If agent is not found in registry.
|
124
|
+
"""
|
125
|
+
with self._lock:
|
126
|
+
if name in self._registered_agents:
|
127
|
+
logger.debug("Loading registered agent: %s", name)
|
128
|
+
return self._registered_agents[name]
|
129
|
+
|
130
|
+
# Agent not found
|
131
|
+
available_agents = self.list_agents()
|
132
|
+
raise ValueError(
|
133
|
+
f"Agent '{name}' not found. "
|
134
|
+
f"Available agents: {available_agents if available_agents else 'None'}"
|
135
|
+
)
|
136
|
+
|
137
|
+
def list_agents(self) -> List[str]:
|
138
|
+
"""List all available agents from registry.
|
139
|
+
|
140
|
+
Returns:
|
141
|
+
List[str]: Sorted list of all registered agent names.
|
142
|
+
"""
|
143
|
+
with self._lock:
|
144
|
+
agent_names = list(self._registered_agents.keys())
|
145
|
+
|
146
|
+
sorted_agents = sorted(agent_names)
|
147
|
+
logger.debug("Total registered agents: %d", len(sorted_agents))
|
148
|
+
return sorted_agents
|
149
|
+
|
150
|
+
|
151
|
+
def __repr__(self) -> str:
|
152
|
+
"""String representation of the loader."""
|
153
|
+
with self._lock:
|
154
|
+
registered_count = len(self._registered_agents)
|
155
|
+
|
156
|
+
return f"CustomAgentLoader(registered={registered_count})"
|
@@ -0,0 +1,137 @@
|
|
1
|
+
"""Enhanced ADK Web Server that uses EnhancedRunner.
|
2
|
+
|
3
|
+
This module provides the EnhancedAdkWebServer class which extends Google ADK's
|
4
|
+
AdkWebServer to use our EnhancedRunner with advanced features.
|
5
|
+
"""
|
6
|
+
|
7
|
+
import os
|
8
|
+
from typing import Optional
|
9
|
+
|
10
|
+
from google.adk.cli.adk_web_server import AdkWebServer
|
11
|
+
from google.adk.auth.credential_service.in_memory_credential_service import InMemoryCredentialService
|
12
|
+
from google.adk.cli.utils import cleanup
|
13
|
+
from google.adk.cli.utils import envs
|
14
|
+
from google.adk.runners import Runner
|
15
|
+
|
16
|
+
from .enhanced_runner import EnhancedRunner
|
17
|
+
|
18
|
+
|
19
|
+
class EnhancedAdkWebServer(AdkWebServer):
|
20
|
+
"""Enhanced ADK Web Server that creates EnhancedRunner instances.
|
21
|
+
|
22
|
+
This class extends Google's AdkWebServer to use our EnhancedRunner with:
|
23
|
+
- Advanced tool execution strategies (MCP, OpenAPI, Function tools)
|
24
|
+
- Circuit breakers and retry policies for resilience
|
25
|
+
- YAML system context and enhanced configuration
|
26
|
+
- Performance monitoring and debugging capabilities
|
27
|
+
- Credential service integration (inherited)
|
28
|
+
|
29
|
+
The EnhancedAdkWebServer is a drop-in replacement for AdkWebServer that
|
30
|
+
provides significantly enhanced capabilities while maintaining full
|
31
|
+
backward compatibility with all existing APIs.
|
32
|
+
|
33
|
+
Examples:
|
34
|
+
Basic usage (drop-in replacement):
|
35
|
+
```python
|
36
|
+
enhanced_server = EnhancedAdkWebServer(
|
37
|
+
agent_loader=agent_loader,
|
38
|
+
session_service=session_service,
|
39
|
+
artifact_service=artifact_service,
|
40
|
+
memory_service=memory_service,
|
41
|
+
credential_service=credential_service,
|
42
|
+
eval_sets_manager=eval_sets_manager,
|
43
|
+
eval_set_results_manager=eval_set_results_manager,
|
44
|
+
agents_dir="./agents"
|
45
|
+
)
|
46
|
+
```
|
47
|
+
|
48
|
+
With enhanced features:
|
49
|
+
```python
|
50
|
+
enhanced_config = EnhancedRunConfig.from_yaml_dict({
|
51
|
+
'max_llm_calls': 200,
|
52
|
+
'tool_timeouts': {'mcp_tools': 30.0},
|
53
|
+
'circuit_breaker': {'failure_threshold': 3},
|
54
|
+
'debug': {'enabled': True}
|
55
|
+
})
|
56
|
+
|
57
|
+
strategy_manager = ToolExecutionStrategyManager()
|
58
|
+
strategy_manager.register_strategy('mcp', McpToolExecutionStrategy(timeout=45.0))
|
59
|
+
|
60
|
+
enhanced_server = EnhancedAdkWebServer(
|
61
|
+
agent_loader=agent_loader,
|
62
|
+
session_service=session_service,
|
63
|
+
# ... other services ...
|
64
|
+
enhanced_config=enhanced_config,
|
65
|
+
yaml_context=YamlSystemContext(
|
66
|
+
system_name="my-agent-system",
|
67
|
+
config_path="/path/to/config.yaml"
|
68
|
+
),
|
69
|
+
tool_strategy_manager=strategy_manager
|
70
|
+
)
|
71
|
+
```
|
72
|
+
"""
|
73
|
+
|
74
|
+
def __init__(self, **kwargs):
|
75
|
+
"""Initialize EnhancedAdkWebServer.
|
76
|
+
|
77
|
+
Args:
|
78
|
+
enhanced_config: Enhanced configuration for runners (optional)
|
79
|
+
yaml_context: YAML system context for error handling (optional)
|
80
|
+
tool_strategy_manager: Tool execution strategy manager (optional)
|
81
|
+
**kwargs: All other parameters passed to AdkWebServer
|
82
|
+
"""
|
83
|
+
# Ensure a credential service exists; default to InMemory if not provided
|
84
|
+
if 'credential_service' not in kwargs or kwargs.get('credential_service') is None:
|
85
|
+
kwargs['credential_service'] = InMemoryCredentialService()
|
86
|
+
|
87
|
+
# Initialize base AdkWebServer with all standard parameters
|
88
|
+
super().__init__(**kwargs)
|
89
|
+
|
90
|
+
# No enhanced configuration retained in simplified scope
|
91
|
+
|
92
|
+
async def get_runner_async(self, app_name: str) -> EnhancedRunner:
|
93
|
+
"""Returns the enhanced runner for the given app.
|
94
|
+
|
95
|
+
This method overrides AdkWebServer.get_runner_async to create
|
96
|
+
EnhancedRunner instances instead of standard Runner instances.
|
97
|
+
|
98
|
+
The logic is identical to the parent class except:
|
99
|
+
1. Creates EnhancedRunner instead of Runner
|
100
|
+
2. Passes enhanced configuration parameters
|
101
|
+
3. Maintains full compatibility with cleanup and caching
|
102
|
+
|
103
|
+
Args:
|
104
|
+
app_name: The name of the application/agent to get runner for
|
105
|
+
|
106
|
+
Returns:
|
107
|
+
An EnhancedRunner instance for the specified app
|
108
|
+
"""
|
109
|
+
# EXACT copy of parent logic for cleanup and caching
|
110
|
+
if app_name in self.runners_to_clean:
|
111
|
+
self.runners_to_clean.remove(app_name)
|
112
|
+
runner = self.runner_dict.pop(app_name, None)
|
113
|
+
await cleanup.close_runners(list([runner]))
|
114
|
+
|
115
|
+
# Load environment for the agent
|
116
|
+
envs.load_dotenv_for_agent(os.path.basename(app_name), self.agents_dir)
|
117
|
+
|
118
|
+
# Return cached runner if available
|
119
|
+
if app_name in self.runner_dict:
|
120
|
+
return self.runner_dict[app_name]
|
121
|
+
|
122
|
+
# Load agent and create new EnhancedRunner
|
123
|
+
root_agent = self.agent_loader.load_agent(app_name)
|
124
|
+
|
125
|
+
# Create EnhancedRunner (thin wrapper over ADK Runner)
|
126
|
+
runner = EnhancedRunner(
|
127
|
+
app_name=app_name,
|
128
|
+
agent=root_agent,
|
129
|
+
artifact_service=self.artifact_service,
|
130
|
+
session_service=self.session_service,
|
131
|
+
memory_service=self.memory_service,
|
132
|
+
credential_service=self.credential_service,
|
133
|
+
)
|
134
|
+
|
135
|
+
# Cache and return runner (same as parent)
|
136
|
+
self.runner_dict[app_name] = runner
|
137
|
+
return runner
|