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.
Files changed (35) hide show
  1. google_adk_extras/__init__.py +31 -1
  2. google_adk_extras/adk_builder.py +1030 -0
  3. google_adk_extras/artifacts/__init__.py +25 -12
  4. google_adk_extras/artifacts/base_custom_artifact_service.py +148 -11
  5. google_adk_extras/artifacts/local_folder_artifact_service.py +133 -13
  6. google_adk_extras/artifacts/s3_artifact_service.py +135 -19
  7. google_adk_extras/artifacts/sql_artifact_service.py +109 -10
  8. google_adk_extras/credentials/__init__.py +34 -0
  9. google_adk_extras/credentials/base_custom_credential_service.py +113 -0
  10. google_adk_extras/credentials/github_oauth2_credential_service.py +213 -0
  11. google_adk_extras/credentials/google_oauth2_credential_service.py +216 -0
  12. google_adk_extras/credentials/http_basic_auth_credential_service.py +388 -0
  13. google_adk_extras/credentials/jwt_credential_service.py +345 -0
  14. google_adk_extras/credentials/microsoft_oauth2_credential_service.py +250 -0
  15. google_adk_extras/credentials/x_oauth2_credential_service.py +240 -0
  16. google_adk_extras/custom_agent_loader.py +156 -0
  17. google_adk_extras/enhanced_adk_web_server.py +137 -0
  18. google_adk_extras/enhanced_fastapi.py +470 -0
  19. google_adk_extras/enhanced_runner.py +38 -0
  20. google_adk_extras/memory/__init__.py +30 -13
  21. google_adk_extras/memory/base_custom_memory_service.py +37 -5
  22. google_adk_extras/memory/sql_memory_service.py +105 -19
  23. google_adk_extras/memory/yaml_file_memory_service.py +115 -22
  24. google_adk_extras/sessions/__init__.py +29 -13
  25. google_adk_extras/sessions/base_custom_session_service.py +133 -11
  26. google_adk_extras/sessions/sql_session_service.py +127 -16
  27. google_adk_extras/sessions/yaml_file_session_service.py +122 -14
  28. google_adk_extras-0.2.3.dist-info/METADATA +302 -0
  29. google_adk_extras-0.2.3.dist-info/RECORD +37 -0
  30. google_adk_extras/py.typed +0 -0
  31. google_adk_extras-0.1.1.dist-info/METADATA +0 -175
  32. google_adk_extras-0.1.1.dist-info/RECORD +0 -25
  33. {google_adk_extras-0.1.1.dist-info → google_adk_extras-0.2.3.dist-info}/WHEEL +0 -0
  34. {google_adk_extras-0.1.1.dist-info → google_adk_extras-0.2.3.dist-info}/licenses/LICENSE +0 -0
  35. {google_adk_extras-0.1.1.dist-info → google_adk_extras-0.2.3.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,388 @@
1
+ """HTTP Basic Auth credential service implementation."""
2
+
3
+ from typing import Optional, Dict
4
+ import logging
5
+ import base64
6
+
7
+ from google.adk.auth.credential_service.session_state_credential_service import SessionStateCredentialService
8
+ from google.adk.auth.credential_service.base_credential_service import CallbackContext
9
+ from google.adk.auth import AuthConfig, AuthCredential, AuthCredentialTypes
10
+ from google.adk.auth.auth_credential import HttpAuth, HttpCredentials
11
+ from fastapi.openapi.models import HTTPBase
12
+
13
+ from .base_custom_credential_service import BaseCustomCredentialService
14
+
15
+ logger = logging.getLogger(__name__)
16
+
17
+
18
+ class HTTPBasicAuthCredentialService(BaseCustomCredentialService):
19
+ """HTTP Basic Auth credential service for username/password authentication.
20
+
21
+ This service manages HTTP Basic Authentication credentials, encoding username
22
+ and password combinations for API authentication.
23
+
24
+ Args:
25
+ username: The username for basic authentication.
26
+ password: The password for basic authentication.
27
+ realm: Optional realm parameter for HTTP Basic Auth.
28
+ use_session_state: If True, stores credentials in session state. If False,
29
+ uses in-memory storage. Default is True for persistence.
30
+
31
+ Example:
32
+ ```python
33
+ credential_service = HTTPBasicAuthCredentialService(
34
+ username="api_user",
35
+ password="api_password",
36
+ realm="API Access"
37
+ )
38
+ await credential_service.initialize()
39
+
40
+ # Use with Runner
41
+ runner = Runner(
42
+ agent=agent,
43
+ session_service=session_service,
44
+ credential_service=credential_service,
45
+ app_name="my_app"
46
+ )
47
+ ```
48
+
49
+ Security Note:
50
+ Basic Auth transmits credentials in base64 encoding, which is not encryption.
51
+ Always use HTTPS when using Basic Auth to protect credentials in transit.
52
+ """
53
+
54
+ def __init__(
55
+ self,
56
+ username: str,
57
+ password: str,
58
+ realm: Optional[str] = None,
59
+ use_session_state: bool = True
60
+ ):
61
+ """Initialize the HTTP Basic Auth credential service.
62
+
63
+ Args:
64
+ username: Username for basic authentication.
65
+ password: Password for basic authentication.
66
+ realm: Optional realm for HTTP Basic Auth.
67
+ use_session_state: Whether to use session state for credential storage.
68
+ """
69
+ super().__init__()
70
+ self.username = username
71
+ self.password = password
72
+ self.realm = realm
73
+ self.use_session_state = use_session_state
74
+
75
+ # Underlying credential service for storage
76
+ if use_session_state:
77
+ self._storage_service = SessionStateCredentialService()
78
+ else:
79
+ from google.adk.auth.credential_service.in_memory_credential_service import InMemoryCredentialService
80
+ self._storage_service = InMemoryCredentialService()
81
+
82
+ async def _initialize_impl(self) -> None:
83
+ """Initialize the HTTP Basic Auth credential service.
84
+
85
+ Validates the username and password configuration.
86
+
87
+ Raises:
88
+ ValueError: If username or password is missing.
89
+ """
90
+ if not self.username:
91
+ raise ValueError("Username is required for HTTP Basic Auth")
92
+ if not self.password:
93
+ raise ValueError("Password is required for HTTP Basic Auth")
94
+
95
+ logger.info(f"Initialized HTTP Basic Auth credential service for user: {self.username}")
96
+
97
+ def encode_basic_auth(self, username: str, password: str) -> str:
98
+ """Encode username and password for HTTP Basic Auth.
99
+
100
+ Args:
101
+ username: The username to encode.
102
+ password: The password to encode.
103
+
104
+ Returns:
105
+ str: Base64 encoded credentials in format "Basic <encoded>".
106
+ """
107
+ credentials = f"{username}:{password}"
108
+ encoded_credentials = base64.b64encode(credentials.encode('utf-8')).decode('ascii')
109
+ return f"Basic {encoded_credentials}"
110
+
111
+ def decode_basic_auth(self, auth_header: str) -> tuple[str, str]:
112
+ """Decode HTTP Basic Auth header to extract username and password.
113
+
114
+ Args:
115
+ auth_header: The Authorization header value.
116
+
117
+ Returns:
118
+ tuple[str, str]: Tuple of (username, password).
119
+
120
+ Raises:
121
+ ValueError: If the auth header is invalid.
122
+ """
123
+ if not auth_header.startswith("Basic "):
124
+ raise ValueError("Invalid Basic Auth header format")
125
+
126
+ encoded_credentials = auth_header[6:] # Remove "Basic " prefix
127
+ try:
128
+ credentials = base64.b64decode(encoded_credentials).decode('utf-8')
129
+ username, password = credentials.split(':', 1)
130
+ return username, password
131
+ except Exception as e:
132
+ raise ValueError(f"Failed to decode Basic Auth credentials: {e}")
133
+
134
+ def create_auth_config(self) -> AuthConfig:
135
+ """Create an AuthConfig for HTTP Basic Authentication.
136
+
137
+ Returns:
138
+ AuthConfig: Configured auth config for HTTP Basic Auth.
139
+ """
140
+ self._check_initialized()
141
+
142
+ # Create HTTP Basic auth scheme
143
+ auth_scheme = HTTPBase(scheme="basic")
144
+
145
+ # Create HTTP Basic credential
146
+ auth_credential = AuthCredential(
147
+ auth_type=AuthCredentialTypes.HTTP,
148
+ http=HttpAuth(
149
+ scheme="basic",
150
+ credentials=HttpCredentials(
151
+ username=self.username,
152
+ password=self.password
153
+ )
154
+ )
155
+ )
156
+
157
+ return AuthConfig(
158
+ auth_scheme=auth_scheme,
159
+ raw_auth_credential=auth_credential
160
+ )
161
+
162
+ async def load_credential(
163
+ self,
164
+ auth_config: AuthConfig,
165
+ callback_context: CallbackContext,
166
+ ) -> Optional[AuthCredential]:
167
+ """Load HTTP Basic Auth credential from storage.
168
+
169
+ Args:
170
+ auth_config: The auth config containing credential key information.
171
+ callback_context: The current callback context.
172
+
173
+ Returns:
174
+ Optional[AuthCredential]: The stored credential or None if not found.
175
+ """
176
+ self._check_initialized()
177
+ return await self._storage_service.load_credential(auth_config, callback_context)
178
+
179
+ async def save_credential(
180
+ self,
181
+ auth_config: AuthConfig,
182
+ callback_context: CallbackContext,
183
+ ) -> None:
184
+ """Save HTTP Basic Auth credential to storage.
185
+
186
+ Args:
187
+ auth_config: The auth config containing the credential to save.
188
+ callback_context: The current callback context.
189
+ """
190
+ self._check_initialized()
191
+ await self._storage_service.save_credential(auth_config, callback_context)
192
+
193
+ logger.info(f"Saved HTTP Basic Auth credential for user {callback_context._invocation_context.user_id}")
194
+
195
+ def validate_credentials(self, test_username: str, test_password: str) -> bool:
196
+ """Validate if provided credentials match the configured ones.
197
+
198
+ Args:
199
+ test_username: Username to validate.
200
+ test_password: Password to validate.
201
+
202
+ Returns:
203
+ bool: True if credentials match, False otherwise.
204
+ """
205
+ self._check_initialized()
206
+ return self.username == test_username and self.password == test_password
207
+
208
+ def get_auth_header(self) -> str:
209
+ """Get the Authorization header value for HTTP Basic Auth.
210
+
211
+ Returns:
212
+ str: The complete Authorization header value.
213
+ """
214
+ self._check_initialized()
215
+ return self.encode_basic_auth(self.username, self.password)
216
+
217
+ def get_credential_info(self) -> Dict[str, str]:
218
+ """Get information about the configured credentials (without passwords).
219
+
220
+ Returns:
221
+ Dict[str, str]: Credential information (excluding sensitive data).
222
+ """
223
+ self._check_initialized()
224
+
225
+ info = {
226
+ "username": self.username,
227
+ "auth_type": "HTTP Basic Auth",
228
+ "password_set": bool(self.password)
229
+ }
230
+
231
+ if self.realm:
232
+ info["realm"] = self.realm
233
+
234
+ return info
235
+
236
+
237
+ class HTTPBasicAuthWithCredentialsService(BaseCustomCredentialService):
238
+ """HTTP Basic Auth service that accepts multiple username/password pairs.
239
+
240
+ This variant allows managing multiple sets of credentials, useful for
241
+ scenarios where different users or contexts require different credentials.
242
+
243
+ Args:
244
+ credentials: Dictionary mapping usernames to passwords.
245
+ default_username: Default username to use if not specified.
246
+ realm: Optional realm parameter for HTTP Basic Auth.
247
+ use_session_state: If True, stores credentials in session state. If False,
248
+ uses in-memory storage. Default is True for persistence.
249
+
250
+ Example:
251
+ ```python
252
+ credential_service = HTTPBasicAuthWithCredentialsService(
253
+ credentials={
254
+ "admin": "admin_password",
255
+ "user1": "user1_password",
256
+ "api_client": "api_secret"
257
+ },
258
+ default_username="api_client"
259
+ )
260
+ await credential_service.initialize()
261
+ ```
262
+ """
263
+
264
+ def __init__(
265
+ self,
266
+ credentials: Dict[str, str],
267
+ default_username: Optional[str] = None,
268
+ realm: Optional[str] = None,
269
+ use_session_state: bool = True
270
+ ):
271
+ """Initialize the multi-credential HTTP Basic Auth service.
272
+
273
+ Args:
274
+ credentials: Dictionary of username -> password mappings.
275
+ default_username: Default username to use.
276
+ realm: Optional realm for HTTP Basic Auth.
277
+ use_session_state: Whether to use session state for credential storage.
278
+ """
279
+ super().__init__()
280
+ self.credentials = credentials.copy()
281
+ self.default_username = default_username or (list(credentials.keys())[0] if credentials else None)
282
+ self.realm = realm
283
+ self.use_session_state = use_session_state
284
+
285
+ # Underlying credential service for storage
286
+ if use_session_state:
287
+ self._storage_service = SessionStateCredentialService()
288
+ else:
289
+ from google.adk.auth.credential_service.in_memory_credential_service import InMemoryCredentialService
290
+ self._storage_service = InMemoryCredentialService()
291
+
292
+ async def _initialize_impl(self) -> None:
293
+ """Initialize the multi-credential HTTP Basic Auth service.
294
+
295
+ Validates that credentials are provided and default username exists.
296
+
297
+ Raises:
298
+ ValueError: If configuration is invalid.
299
+ """
300
+ if not self.credentials:
301
+ raise ValueError("At least one username/password pair is required")
302
+ if self.default_username and self.default_username not in self.credentials:
303
+ raise ValueError(f"Default username '{self.default_username}' not found in credentials")
304
+ if not self.default_username:
305
+ raise ValueError("Default username is required when multiple credentials are provided")
306
+
307
+ logger.info(f"Initialized HTTP Basic Auth service with {len(self.credentials)} credential sets")
308
+
309
+ def create_auth_config(self, username: Optional[str] = None) -> AuthConfig:
310
+ """Create an AuthConfig for HTTP Basic Authentication.
311
+
312
+ Args:
313
+ username: Username to use. If None, uses default_username.
314
+
315
+ Returns:
316
+ AuthConfig: Configured auth config for HTTP Basic Auth.
317
+
318
+ Raises:
319
+ ValueError: If username is not found in credentials.
320
+ """
321
+ self._check_initialized()
322
+
323
+ username = username or self.default_username
324
+ if username not in self.credentials:
325
+ raise ValueError(f"Username '{username}' not found in credentials")
326
+
327
+ password = self.credentials[username]
328
+
329
+ # Create HTTP Basic auth scheme
330
+ auth_scheme = HTTPBase(scheme="basic")
331
+
332
+ # Create HTTP Basic credential
333
+ auth_credential = AuthCredential(
334
+ auth_type=AuthCredentialTypes.HTTP,
335
+ http=HttpAuth(
336
+ scheme="basic",
337
+ credentials=HttpCredentials(
338
+ username=username,
339
+ password=password
340
+ )
341
+ )
342
+ )
343
+
344
+ return AuthConfig(
345
+ auth_scheme=auth_scheme,
346
+ raw_auth_credential=auth_credential
347
+ )
348
+
349
+ async def load_credential(
350
+ self,
351
+ auth_config: AuthConfig,
352
+ callback_context: CallbackContext,
353
+ ) -> Optional[AuthCredential]:
354
+ """Load HTTP Basic Auth credential from storage.
355
+
356
+ Args:
357
+ auth_config: The auth config containing credential key information.
358
+ callback_context: The current callback context.
359
+
360
+ Returns:
361
+ Optional[AuthCredential]: The stored credential or None if not found.
362
+ """
363
+ self._check_initialized()
364
+ return await self._storage_service.load_credential(auth_config, callback_context)
365
+
366
+ async def save_credential(
367
+ self,
368
+ auth_config: AuthConfig,
369
+ callback_context: CallbackContext,
370
+ ) -> None:
371
+ """Save HTTP Basic Auth credential to storage.
372
+
373
+ Args:
374
+ auth_config: The auth config containing the credential to save.
375
+ callback_context: The current callback context.
376
+ """
377
+ self._check_initialized()
378
+ await self._storage_service.save_credential(auth_config, callback_context)
379
+
380
+ logger.info(f"Saved HTTP Basic Auth credential for user {callback_context._invocation_context.user_id}")
381
+
382
+ def get_available_usernames(self) -> list[str]:
383
+ """Get list of available usernames.
384
+
385
+ Returns:
386
+ list[str]: List of configured usernames.
387
+ """
388
+ return list(self.credentials.keys())