Spaces:
Build error
Build error
from types import MappingProxyType | |
from typing import Any | |
from pydantic import ( | |
BaseModel, | |
Field, | |
SerializationInfo, | |
field_serializer, | |
model_validator, | |
) | |
from pydantic.json import pydantic_encoder | |
from openhands.events.stream import EventStream | |
from openhands.integrations.provider import ( | |
CUSTOM_SECRETS_TYPE, | |
CUSTOM_SECRETS_TYPE_WITH_JSON_SCHEMA, | |
PROVIDER_TOKEN_TYPE, | |
PROVIDER_TOKEN_TYPE_WITH_JSON_SCHEMA, | |
CustomSecret, | |
ProviderToken, | |
) | |
from openhands.integrations.service_types import ProviderType | |
class UserSecrets(BaseModel): | |
provider_tokens: PROVIDER_TOKEN_TYPE_WITH_JSON_SCHEMA = Field( | |
default_factory=lambda: MappingProxyType({}) | |
) | |
custom_secrets: CUSTOM_SECRETS_TYPE_WITH_JSON_SCHEMA = Field( | |
default_factory=lambda: MappingProxyType({}) | |
) | |
model_config = { | |
'frozen': True, | |
'validate_assignment': True, | |
'arbitrary_types_allowed': True, | |
} | |
def provider_tokens_serializer( | |
self, provider_tokens: PROVIDER_TOKEN_TYPE, info: SerializationInfo | |
) -> dict[str, dict[str, str | Any]]: | |
tokens = {} | |
expose_secrets = info.context and info.context.get('expose_secrets', False) | |
for token_type, provider_token in provider_tokens.items(): | |
if not provider_token or not provider_token.token: | |
continue | |
token_type_str = ( | |
token_type.value | |
if isinstance(token_type, ProviderType) | |
else str(token_type) | |
) | |
token = None | |
if provider_token.token: | |
token = ( | |
provider_token.token.get_secret_value() | |
if expose_secrets | |
else pydantic_encoder(provider_token.token) | |
) | |
tokens[token_type_str] = { | |
'token': token, | |
'host': provider_token.host, | |
'user_id': provider_token.user_id, | |
} | |
return tokens | |
def custom_secrets_serializer( | |
self, custom_secrets: CUSTOM_SECRETS_TYPE, info: SerializationInfo | |
): | |
secrets = {} | |
expose_secrets = info.context and info.context.get('expose_secrets', False) | |
if custom_secrets: | |
for secret_name, secret_value in custom_secrets.items(): | |
secrets[secret_name] = { | |
'secret': secret_value.secret.get_secret_value() | |
if expose_secrets | |
else pydantic_encoder(secret_value.secret), | |
'description': secret_value.description, | |
} | |
return secrets | |
def convert_dict_to_mappingproxy( | |
cls, data: dict[str, dict[str, Any] | MappingProxyType] | PROVIDER_TOKEN_TYPE | |
) -> dict[str, MappingProxyType | None]: | |
"""Custom deserializer to convert dictionary into MappingProxyType""" | |
if not isinstance(data, dict): | |
raise ValueError('UserSecrets must be initialized with a dictionary') | |
new_data: dict[str, MappingProxyType | None] = {} | |
if 'provider_tokens' in data: | |
tokens = data['provider_tokens'] | |
if isinstance( | |
tokens, dict | |
): # Ensure conversion happens only for dict inputs | |
converted_tokens = {} | |
for key, value in tokens.items(): | |
try: | |
provider_type = ( | |
ProviderType(key) if isinstance(key, str) else key | |
) | |
converted_tokens[provider_type] = ProviderToken.from_value( | |
value | |
) | |
except ValueError: | |
# Skip invalid provider types or tokens | |
continue | |
# Convert to MappingProxyType | |
new_data['provider_tokens'] = MappingProxyType(converted_tokens) | |
elif isinstance(tokens, MappingProxyType): | |
new_data['provider_tokens'] = tokens | |
if 'custom_secrets' in data: | |
secrets = data['custom_secrets'] | |
if isinstance(secrets, dict): | |
converted_secrets = {} | |
for key, value in secrets.items(): | |
try: | |
converted_secrets[key] = CustomSecret.from_value(value) | |
except ValueError: | |
continue | |
new_data['custom_secrets'] = MappingProxyType(converted_secrets) | |
elif isinstance(secrets, MappingProxyType): | |
new_data['custom_secrets'] = secrets | |
return new_data | |
def set_event_stream_secrets(self, event_stream: EventStream) -> None: | |
""" | |
This ensures that provider tokens and custom secrets masked from the event stream | |
Args: | |
event_stream: Agent session's event stream | |
""" | |
secrets = self.get_env_vars() | |
event_stream.set_secrets(secrets) | |
def get_env_vars(self) -> dict[str, str]: | |
secret_store = self.model_dump(context={'expose_secrets': True}) | |
custom_secrets = secret_store.get('custom_secrets', {}) | |
secrets = {} | |
for secret_name, value in custom_secrets.items(): | |
secrets[secret_name] = value['secret'] | |
return secrets | |
def get_custom_secrets_descriptions(self) -> dict[str, str]: | |
secrets = {} | |
for secret_name, secret in self.custom_secrets.items(): | |
secrets[secret_name] = secret.description | |
return secrets | |