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,213 @@
|
|
1
|
+
"""GitHub 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 GitHubOAuth2CredentialService(BaseCustomCredentialService):
|
19
|
+
"""GitHub OAuth2 credential service for handling GitHub authentication flows.
|
20
|
+
|
21
|
+
This service provides pre-configured OAuth2 flows for GitHub APIs including
|
22
|
+
repository access, user information, and organization management.
|
23
|
+
|
24
|
+
Args:
|
25
|
+
client_id: The GitHub OAuth2 client ID from GitHub Developer Settings.
|
26
|
+
client_secret: The GitHub OAuth2 client secret from GitHub Developer Settings.
|
27
|
+
scopes: List of OAuth2 scopes to request. Common scopes include:
|
28
|
+
- "user" - Access to user profile information
|
29
|
+
- "user:email" - Access to user email addresses
|
30
|
+
- "repo" - Full access to repositories
|
31
|
+
- "public_repo" - Access to public repositories only
|
32
|
+
- "admin:org" - Full access to organization data
|
33
|
+
- "read:org" - Read access to organization data
|
34
|
+
- "notifications" - Access to notifications
|
35
|
+
use_session_state: If True, stores credentials in session state. If False,
|
36
|
+
uses in-memory storage. Default is True for persistence.
|
37
|
+
|
38
|
+
Example:
|
39
|
+
```python
|
40
|
+
credential_service = GitHubOAuth2CredentialService(
|
41
|
+
client_id="your-github-client-id",
|
42
|
+
client_secret="your-github-client-secret",
|
43
|
+
scopes=["user", "repo", "read:org"]
|
44
|
+
)
|
45
|
+
await credential_service.initialize()
|
46
|
+
|
47
|
+
# Use with Runner
|
48
|
+
runner = Runner(
|
49
|
+
agent=agent,
|
50
|
+
session_service=session_service,
|
51
|
+
credential_service=credential_service,
|
52
|
+
app_name="my_app"
|
53
|
+
)
|
54
|
+
```
|
55
|
+
"""
|
56
|
+
|
57
|
+
# GitHub OAuth2 endpoints
|
58
|
+
GITHUB_AUTH_URL = "https://github.com/login/oauth/authorize"
|
59
|
+
GITHUB_TOKEN_URL = "https://github.com/login/oauth/access_token"
|
60
|
+
|
61
|
+
# Common GitHub OAuth2 scopes
|
62
|
+
COMMON_SCOPES = {
|
63
|
+
"user": "Access to user profile information",
|
64
|
+
"user:email": "Access to user email addresses",
|
65
|
+
"user:follow": "Access to follow/unfollow users",
|
66
|
+
"repo": "Full access to public and private repositories",
|
67
|
+
"public_repo": "Access to public repositories only",
|
68
|
+
"repo:status": "Access to commit status",
|
69
|
+
"repo_deployment": "Access to deployment status",
|
70
|
+
"admin:org": "Full control of orgs and teams, read/write org projects",
|
71
|
+
"write:org": "Read and write access to organization membership and projects",
|
72
|
+
"read:org": "Read-only access to organization membership and projects",
|
73
|
+
"admin:public_key": "Full control of user public keys",
|
74
|
+
"write:public_key": "Write access to user public keys",
|
75
|
+
"read:public_key": "Read access to user public keys",
|
76
|
+
"admin:repo_hook": "Full control of repository hooks",
|
77
|
+
"write:repo_hook": "Write access to repository hooks",
|
78
|
+
"read:repo_hook": "Read access to repository hooks",
|
79
|
+
"admin:org_hook": "Full control of organization hooks",
|
80
|
+
"gist": "Write access to gists",
|
81
|
+
"notifications": "Access to notifications",
|
82
|
+
"delete_repo": "Delete repositories",
|
83
|
+
"write:packages": "Upload packages to GitHub Package Registry",
|
84
|
+
"read:packages": "Download packages from GitHub Package Registry",
|
85
|
+
"workflow": "Update GitHub Action workflows"
|
86
|
+
}
|
87
|
+
|
88
|
+
def __init__(
|
89
|
+
self,
|
90
|
+
client_id: str,
|
91
|
+
client_secret: str,
|
92
|
+
scopes: Optional[List[str]] = None,
|
93
|
+
use_session_state: bool = True
|
94
|
+
):
|
95
|
+
"""Initialize the GitHub OAuth2 credential service.
|
96
|
+
|
97
|
+
Args:
|
98
|
+
client_id: GitHub OAuth2 client ID.
|
99
|
+
client_secret: GitHub OAuth2 client secret.
|
100
|
+
scopes: List of OAuth2 scopes to request.
|
101
|
+
use_session_state: Whether to use session state for credential storage.
|
102
|
+
"""
|
103
|
+
super().__init__()
|
104
|
+
self.client_id = client_id
|
105
|
+
self.client_secret = client_secret
|
106
|
+
self.scopes = scopes or ["user", "repo"]
|
107
|
+
self.use_session_state = use_session_state
|
108
|
+
|
109
|
+
# Underlying credential service for storage
|
110
|
+
if use_session_state:
|
111
|
+
self._storage_service = SessionStateCredentialService()
|
112
|
+
else:
|
113
|
+
from google.adk.auth.credential_service.in_memory_credential_service import InMemoryCredentialService
|
114
|
+
self._storage_service = InMemoryCredentialService()
|
115
|
+
|
116
|
+
async def _initialize_impl(self) -> None:
|
117
|
+
"""Initialize the GitHub OAuth2 credential service.
|
118
|
+
|
119
|
+
Validates the client credentials and sets up the OAuth2 auth scheme.
|
120
|
+
|
121
|
+
Raises:
|
122
|
+
ValueError: If client_id or client_secret is missing.
|
123
|
+
"""
|
124
|
+
if not self.client_id:
|
125
|
+
raise ValueError("GitHub OAuth2 client_id is required")
|
126
|
+
if not self.client_secret:
|
127
|
+
raise ValueError("GitHub OAuth2 client_secret is required")
|
128
|
+
if not self.scopes:
|
129
|
+
raise ValueError("At least one OAuth2 scope is required")
|
130
|
+
|
131
|
+
# Validate scopes against known GitHub scopes
|
132
|
+
unknown_scopes = set(self.scopes) - set(self.COMMON_SCOPES.keys())
|
133
|
+
if unknown_scopes:
|
134
|
+
logger.warning(f"Unknown GitHub OAuth2 scopes: {unknown_scopes}")
|
135
|
+
|
136
|
+
logger.info(f"Initialized GitHub OAuth2 credential service with scopes: {self.scopes}")
|
137
|
+
|
138
|
+
def create_auth_config(self) -> AuthConfig:
|
139
|
+
"""Create an AuthConfig for GitHub OAuth2 authentication.
|
140
|
+
|
141
|
+
Returns:
|
142
|
+
AuthConfig: Configured auth config for GitHub OAuth2 flow.
|
143
|
+
"""
|
144
|
+
self._check_initialized()
|
145
|
+
|
146
|
+
# Create OAuth2 auth scheme
|
147
|
+
auth_scheme = OAuth2(
|
148
|
+
flows=OAuthFlows(
|
149
|
+
authorizationCode=OAuthFlowAuthorizationCode(
|
150
|
+
authorizationUrl=self.GITHUB_AUTH_URL,
|
151
|
+
tokenUrl=self.GITHUB_TOKEN_URL,
|
152
|
+
scopes={
|
153
|
+
scope: self.COMMON_SCOPES.get(scope, f"GitHub scope: {scope}")
|
154
|
+
for scope in self.scopes
|
155
|
+
}
|
156
|
+
)
|
157
|
+
)
|
158
|
+
)
|
159
|
+
|
160
|
+
# Create OAuth2 credential
|
161
|
+
auth_credential = AuthCredential(
|
162
|
+
auth_type=AuthCredentialTypes.OAUTH2,
|
163
|
+
oauth2=OAuth2Auth(
|
164
|
+
client_id=self.client_id,
|
165
|
+
client_secret=self.client_secret
|
166
|
+
)
|
167
|
+
)
|
168
|
+
|
169
|
+
return AuthConfig(
|
170
|
+
auth_scheme=auth_scheme,
|
171
|
+
raw_auth_credential=auth_credential
|
172
|
+
)
|
173
|
+
|
174
|
+
async def load_credential(
|
175
|
+
self,
|
176
|
+
auth_config: AuthConfig,
|
177
|
+
callback_context: CallbackContext,
|
178
|
+
) -> Optional[AuthCredential]:
|
179
|
+
"""Load GitHub OAuth2 credential from storage.
|
180
|
+
|
181
|
+
Args:
|
182
|
+
auth_config: The auth config containing credential key information.
|
183
|
+
callback_context: The current callback context.
|
184
|
+
|
185
|
+
Returns:
|
186
|
+
Optional[AuthCredential]: The stored credential or None if not found.
|
187
|
+
"""
|
188
|
+
self._check_initialized()
|
189
|
+
return await self._storage_service.load_credential(auth_config, callback_context)
|
190
|
+
|
191
|
+
async def save_credential(
|
192
|
+
self,
|
193
|
+
auth_config: AuthConfig,
|
194
|
+
callback_context: CallbackContext,
|
195
|
+
) -> None:
|
196
|
+
"""Save GitHub OAuth2 credential to storage.
|
197
|
+
|
198
|
+
Args:
|
199
|
+
auth_config: The auth config containing the credential to save.
|
200
|
+
callback_context: The current callback context.
|
201
|
+
"""
|
202
|
+
self._check_initialized()
|
203
|
+
await self._storage_service.save_credential(auth_config, callback_context)
|
204
|
+
|
205
|
+
logger.info(f"Saved GitHub OAuth2 credential for user {callback_context._invocation_context.user_id}")
|
206
|
+
|
207
|
+
def get_supported_scopes(self) -> dict:
|
208
|
+
"""Get dictionary of supported GitHub OAuth2 scopes and their descriptions.
|
209
|
+
|
210
|
+
Returns:
|
211
|
+
dict: Mapping of scope names to descriptions.
|
212
|
+
"""
|
213
|
+
return self.COMMON_SCOPES.copy()
|
@@ -0,0 +1,216 @@
|
|
1
|
+
"""Google 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 GoogleOAuth2CredentialService(BaseCustomCredentialService):
|
19
|
+
"""Google OAuth2 credential service for handling Google authentication flows.
|
20
|
+
|
21
|
+
This service provides pre-configured OAuth2 flows for Google services including
|
22
|
+
Gmail, Calendar, Drive, and other Google APIs.
|
23
|
+
|
24
|
+
Args:
|
25
|
+
client_id: The Google OAuth2 client ID from Google Cloud Console.
|
26
|
+
client_secret: The Google OAuth2 client secret from Google Cloud Console.
|
27
|
+
scopes: List of OAuth2 scopes to request. Common scopes include:
|
28
|
+
- "openid" - OpenID Connect authentication
|
29
|
+
- "email" - Access to email address
|
30
|
+
- "profile" - Access to basic profile info
|
31
|
+
- "https://www.googleapis.com/auth/calendar" - Google Calendar access
|
32
|
+
- "https://www.googleapis.com/auth/gmail.readonly" - Gmail read access
|
33
|
+
- "https://www.googleapis.com/auth/drive" - Google Drive access
|
34
|
+
use_session_state: If True, stores credentials in session state. If False,
|
35
|
+
uses in-memory storage. Default is True for persistence.
|
36
|
+
|
37
|
+
Example:
|
38
|
+
```python
|
39
|
+
credential_service = GoogleOAuth2CredentialService(
|
40
|
+
client_id="your-client-id.apps.googleusercontent.com",
|
41
|
+
client_secret="your-client-secret",
|
42
|
+
scopes=["openid", "email", "https://www.googleapis.com/auth/calendar"]
|
43
|
+
)
|
44
|
+
await credential_service.initialize()
|
45
|
+
|
46
|
+
# Use with Runner
|
47
|
+
runner = Runner(
|
48
|
+
agent=agent,
|
49
|
+
session_service=session_service,
|
50
|
+
credential_service=credential_service,
|
51
|
+
app_name="my_app"
|
52
|
+
)
|
53
|
+
```
|
54
|
+
"""
|
55
|
+
|
56
|
+
# Google OAuth2 endpoints
|
57
|
+
GOOGLE_AUTH_URL = "https://accounts.google.com/o/oauth2/auth"
|
58
|
+
GOOGLE_TOKEN_URL = "https://oauth2.googleapis.com/token"
|
59
|
+
|
60
|
+
# Common Google OAuth2 scopes
|
61
|
+
COMMON_SCOPES = {
|
62
|
+
"openid": "OpenID Connect authentication",
|
63
|
+
"email": "Access to email address",
|
64
|
+
"profile": "Access to basic profile information",
|
65
|
+
"calendar": "https://www.googleapis.com/auth/calendar",
|
66
|
+
"calendar.readonly": "https://www.googleapis.com/auth/calendar.readonly",
|
67
|
+
"gmail.readonly": "https://www.googleapis.com/auth/gmail.readonly",
|
68
|
+
"gmail.modify": "https://www.googleapis.com/auth/gmail.modify",
|
69
|
+
"drive": "https://www.googleapis.com/auth/drive",
|
70
|
+
"drive.readonly": "https://www.googleapis.com/auth/drive.readonly",
|
71
|
+
"cloud-platform": "https://www.googleapis.com/auth/cloud-platform",
|
72
|
+
}
|
73
|
+
|
74
|
+
def __init__(
|
75
|
+
self,
|
76
|
+
client_id: str,
|
77
|
+
client_secret: str,
|
78
|
+
scopes: Optional[List[str]] = None,
|
79
|
+
use_session_state: bool = True
|
80
|
+
):
|
81
|
+
"""Initialize the Google OAuth2 credential service.
|
82
|
+
|
83
|
+
Args:
|
84
|
+
client_id: Google OAuth2 client ID.
|
85
|
+
client_secret: Google OAuth2 client secret.
|
86
|
+
scopes: List of OAuth2 scopes to request.
|
87
|
+
use_session_state: Whether to use session state for credential storage.
|
88
|
+
"""
|
89
|
+
super().__init__()
|
90
|
+
self.client_id = client_id
|
91
|
+
self.client_secret = client_secret
|
92
|
+
self.scopes = scopes or ["openid", "email", "profile"]
|
93
|
+
self.use_session_state = use_session_state
|
94
|
+
|
95
|
+
# Resolve scope shortcuts to full URLs
|
96
|
+
self._resolved_scopes = self._resolve_scopes(self.scopes)
|
97
|
+
|
98
|
+
# Underlying credential service for storage
|
99
|
+
if use_session_state:
|
100
|
+
self._storage_service = SessionStateCredentialService()
|
101
|
+
else:
|
102
|
+
from google.adk.auth.credential_service.in_memory_credential_service import InMemoryCredentialService
|
103
|
+
self._storage_service = InMemoryCredentialService()
|
104
|
+
|
105
|
+
def _resolve_scopes(self, scopes: List[str]) -> List[str]:
|
106
|
+
"""Resolve scope shortcuts to full Google OAuth2 scope URLs.
|
107
|
+
|
108
|
+
Args:
|
109
|
+
scopes: List of scope names or URLs.
|
110
|
+
|
111
|
+
Returns:
|
112
|
+
List of resolved scope URLs.
|
113
|
+
"""
|
114
|
+
resolved = []
|
115
|
+
for scope in scopes:
|
116
|
+
if scope in self.COMMON_SCOPES:
|
117
|
+
resolved_scope = self.COMMON_SCOPES[scope]
|
118
|
+
# If the value is a URL, use it; otherwise it's just a description
|
119
|
+
if resolved_scope.startswith('https://'):
|
120
|
+
resolved.append(resolved_scope)
|
121
|
+
else:
|
122
|
+
resolved.append(scope)
|
123
|
+
else:
|
124
|
+
resolved.append(scope)
|
125
|
+
return resolved
|
126
|
+
|
127
|
+
async def _initialize_impl(self) -> None:
|
128
|
+
"""Initialize the Google OAuth2 credential service.
|
129
|
+
|
130
|
+
Validates the client credentials and sets up the OAuth2 auth scheme.
|
131
|
+
|
132
|
+
Raises:
|
133
|
+
ValueError: If client_id or client_secret is missing.
|
134
|
+
"""
|
135
|
+
if not self.client_id:
|
136
|
+
raise ValueError("Google OAuth2 client_id is required")
|
137
|
+
if not self.client_secret:
|
138
|
+
raise ValueError("Google OAuth2 client_secret is required")
|
139
|
+
if not self._resolved_scopes:
|
140
|
+
raise ValueError("At least one OAuth2 scope is required")
|
141
|
+
|
142
|
+
logger.info(f"Initialized Google OAuth2 credential service with scopes: {self._resolved_scopes}")
|
143
|
+
|
144
|
+
def create_auth_config(self) -> AuthConfig:
|
145
|
+
"""Create an AuthConfig for Google OAuth2 authentication.
|
146
|
+
|
147
|
+
Returns:
|
148
|
+
AuthConfig: Configured auth config for Google OAuth2 flow.
|
149
|
+
"""
|
150
|
+
self._check_initialized()
|
151
|
+
|
152
|
+
# Create OAuth2 auth scheme
|
153
|
+
auth_scheme = OAuth2(
|
154
|
+
flows=OAuthFlows(
|
155
|
+
authorizationCode=OAuthFlowAuthorizationCode(
|
156
|
+
authorizationUrl=self.GOOGLE_AUTH_URL,
|
157
|
+
tokenUrl=self.GOOGLE_TOKEN_URL,
|
158
|
+
scopes={scope: f"Access to {scope}" for scope in self._resolved_scopes}
|
159
|
+
)
|
160
|
+
)
|
161
|
+
)
|
162
|
+
|
163
|
+
# Create OAuth2 credential
|
164
|
+
auth_credential = AuthCredential(
|
165
|
+
auth_type=AuthCredentialTypes.OAUTH2,
|
166
|
+
oauth2=OAuth2Auth(
|
167
|
+
client_id=self.client_id,
|
168
|
+
client_secret=self.client_secret
|
169
|
+
)
|
170
|
+
)
|
171
|
+
|
172
|
+
return AuthConfig(
|
173
|
+
auth_scheme=auth_scheme,
|
174
|
+
raw_auth_credential=auth_credential
|
175
|
+
)
|
176
|
+
|
177
|
+
async def load_credential(
|
178
|
+
self,
|
179
|
+
auth_config: AuthConfig,
|
180
|
+
callback_context: CallbackContext,
|
181
|
+
) -> Optional[AuthCredential]:
|
182
|
+
"""Load Google OAuth2 credential from storage.
|
183
|
+
|
184
|
+
Args:
|
185
|
+
auth_config: The auth config containing credential key information.
|
186
|
+
callback_context: The current callback context.
|
187
|
+
|
188
|
+
Returns:
|
189
|
+
Optional[AuthCredential]: The stored credential or None if not found.
|
190
|
+
"""
|
191
|
+
self._check_initialized()
|
192
|
+
return await self._storage_service.load_credential(auth_config, callback_context)
|
193
|
+
|
194
|
+
async def save_credential(
|
195
|
+
self,
|
196
|
+
auth_config: AuthConfig,
|
197
|
+
callback_context: CallbackContext,
|
198
|
+
) -> None:
|
199
|
+
"""Save Google OAuth2 credential to storage.
|
200
|
+
|
201
|
+
Args:
|
202
|
+
auth_config: The auth config containing the credential to save.
|
203
|
+
callback_context: The current callback context.
|
204
|
+
"""
|
205
|
+
self._check_initialized()
|
206
|
+
await self._storage_service.save_credential(auth_config, callback_context)
|
207
|
+
|
208
|
+
logger.info(f"Saved Google OAuth2 credential for user {callback_context._invocation_context.user_id}")
|
209
|
+
|
210
|
+
def get_supported_scopes(self) -> dict:
|
211
|
+
"""Get dictionary of supported Google OAuth2 scopes and their descriptions.
|
212
|
+
|
213
|
+
Returns:
|
214
|
+
dict: Mapping of scope names to descriptions.
|
215
|
+
"""
|
216
|
+
return self.COMMON_SCOPES.copy()
|