from collections.abc import Mapping from types import MappingProxyType from pydantic import ConfigDict, Field, field_validator from openhands.integrations.provider import CUSTOM_SECRETS_TYPE, PROVIDER_TOKEN_TYPE from openhands.integrations.service_types import ProviderType from openhands.storage.data_models.settings import Settings class ConversationInitData(Settings): """Session initialization data for the web environment - a deep copy of the global config is made and then overridden with this data.""" git_provider_tokens: PROVIDER_TOKEN_TYPE | None = Field(default=None, frozen=True) custom_secrets: CUSTOM_SECRETS_TYPE | None = Field(default=None, frozen=True) selected_repository: str | None = Field(default=None) replay_json: str | None = Field(default=None) selected_branch: str | None = Field(default=None) conversation_instructions: str | None = Field(default=None) git_provider: ProviderType | None = Field(default=None) model_config = ConfigDict( arbitrary_types_allowed=True, ) @field_validator('git_provider_tokens', 'custom_secrets') @classmethod def immutable_validator(cls, value: Mapping | None) -> MappingProxyType | None: """Ensure git_provider_tokens and custom_secrets are always MappingProxyType. This validator converts any Mapping (including dict) to MappingProxyType, ensuring type safety and immutability. If the value is None, it returns None. """ if value is None: return None if isinstance(value, MappingProxyType): return value return MappingProxyType(value)