Fix issue #8248: [Bug]: Run pre-commit (#8249)

This commit is contained in:
OpenHands
2025-05-04 05:00:10 -04:00
committed by GitHub
parent 722711db3b
commit 2c085ae79e
10 changed files with 70 additions and 76 deletions

View File

@@ -3,7 +3,7 @@ from datetime import datetime, timezone
from fastapi import APIRouter, Body, Depends, status
from fastapi.responses import JSONResponse
from pydantic import BaseModel, ValidationError
from pydantic import BaseModel
from openhands.core.config.llm_config import LLMConfig
from openhands.core.logger import openhands_logger as logger
@@ -61,10 +61,9 @@ class InitSessionRequest(BaseModel):
image_urls: list[str] | None = None
replay_json: str | None = None
suggested_task: SuggestedTask | None = None
model_config = {
"extra": "forbid"
}
model_config = {'extra': 'forbid'}
async def _create_new_conversation(
user_id: str | None,
@@ -246,7 +245,7 @@ async def new_conversation(
},
status_code=status.HTTP_400_BAD_REQUEST,
)
@app.get('/conversations')
async def search_conversations(

View File

@@ -1,32 +1,33 @@
from fastapi import APIRouter, Depends, status
from fastapi.responses import JSONResponse
from pydantic import SecretStr
from openhands.integrations.service_types import ProviderType
from openhands.core.logger import openhands_logger as logger
from openhands.integrations.utils import validate_provider_token
from openhands.server.settings import GETCustomSecrets, POSTCustomSecrets, POSTProviderModel
from openhands.server.user_auth import get_secrets_store, get_user_secrets, get_user_settings_store
from openhands.server.settings import (
GETCustomSecrets,
POSTCustomSecrets,
POSTProviderModel,
)
from openhands.server.user_auth import (
get_secrets_store,
get_user_secrets,
)
from openhands.storage.data_models.settings import Settings
from openhands.storage.data_models.user_secrets import UserSecrets
from openhands.storage.settings.secret_store import SecretsStore
from openhands.storage.settings.settings_store import SettingsStore
from openhands.core.logger import openhands_logger as logger
app = APIRouter(prefix='/api')
# =================================================
# SECTION: Handle git provider tokens
# =================================================
async def invalidate_legacy_secrets_store(
settings: Settings,
settings_store: SettingsStore,
secrets_store: SecretsStore) -> UserSecrets | None:
settings: Settings, settings_store: SettingsStore, secrets_store: SecretsStore
) -> UserSecrets | None:
"""
We are moving `secrets_store` (a field from `Settings` object) to its own dedicated store
This function moves the values from Settings to UserSecrets, and deletes the values in Settings
@@ -34,7 +35,9 @@ async def invalidate_legacy_secrets_store(
"""
if len(settings.secrets_store.provider_tokens.items()) > 0:
user_secrets = UserSecrets(provider_tokens=settings.secrets_store.provider_tokens)
user_secrets = UserSecrets(
provider_tokens=settings.secrets_store.provider_tokens
)
await secrets_store.store(user_secrets)
# Invalidate old tokens via settings store serializer
@@ -44,9 +47,8 @@ async def invalidate_legacy_secrets_store(
await settings_store.store(invalidated_secrets_settings)
return user_secrets
return None
return None
async def check_provider_tokens(provider_info: POSTProviderModel) -> str:
@@ -55,9 +57,7 @@ async def check_provider_tokens(provider_info: POSTProviderModel) -> str:
# Determine whether tokens are valid
for token_type, token_value in provider_info.provider_tokens.items():
if token_value.token:
confirmed_token_type = await validate_provider_token(
token_value.token
)
confirmed_token_type = await validate_provider_token(token_value.token)
if not confirmed_token_type or confirmed_token_type != token_type:
return f'Invalid token. Please make sure it is a valid {token_type.value} token.'
@@ -66,8 +66,8 @@ async def check_provider_tokens(provider_info: POSTProviderModel) -> str:
@app.post('/add-git-providers')
async def store_provider_tokens(
provider_info: POSTProviderModel,
secrets_store: SecretsStore = Depends(get_secrets_store)
provider_info: POSTProviderModel,
secrets_store: SecretsStore = Depends(get_secrets_store),
) -> JSONResponse:
provider_err_msg = await check_provider_tokens(provider_info)
if provider_err_msg:
@@ -75,32 +75,31 @@ async def store_provider_tokens(
status_code=status.HTTP_401_UNAUTHORIZED,
content={'error': provider_err_msg},
)
try:
user_secrets = await secrets_store.load()
if user_secrets:
if provider_info.provider_tokens:
existing_providers = [
provider
for provider in user_secrets.provider_tokens
provider for provider in user_secrets.provider_tokens
]
# Merge incoming settings store with the existing one
for provider, token_value in list(provider_info.provider_tokens.items()):
for provider, token_value in list(
provider_info.provider_tokens.items()
):
if provider in existing_providers and not token_value.token:
existing_token = (
user_secrets.provider_tokens.get(provider)
)
existing_token = user_secrets.provider_tokens.get(provider)
if existing_token and existing_token.token:
provider_info.provider_tokens[provider] = existing_token
else: # nothing passed in means keep current settings
provider_info.provider_tokens = dict(user_secrets.provider_tokens)
updated_secrets = user_secrets.model_copy(update={"provider_tokens":provider_info.provider_tokens})
updated_secrets = user_secrets.model_copy(
update={'provider_tokens': provider_info.provider_tokens}
)
await secrets_store.store(updated_secrets)
return JSONResponse(
@@ -117,14 +116,12 @@ async def store_provider_tokens(
@app.post('/unset-provider-tokens', response_model=dict[str, str])
async def unset_provider_tokens(
secrets_store: SecretsStore = Depends(get_secrets_store)
secrets_store: SecretsStore = Depends(get_secrets_store),
) -> JSONResponse:
try:
user_secrets = await secrets_store.load()
if user_secrets:
updated_secrets = user_secrets.model_copy(
update={'provider_tokens': {}}
)
updated_secrets = user_secrets.model_copy(update={'provider_tokens': {}})
await secrets_store.store(updated_secrets)
return JSONResponse(
@@ -140,14 +137,11 @@ async def unset_provider_tokens(
)
# =================================================
# SECTION: Handle custom secrets
# =================================================
@app.get('/secrets', response_model=GETCustomSecrets)
async def load_custom_secrets_names(
user_secrets: UserSecrets | None = Depends(get_user_secrets),
@@ -158,10 +152,10 @@ async def load_custom_secrets_names(
status_code=status.HTTP_404_NOT_FOUND,
content={'error': 'User secrets not found'},
)
custom_secrets = list(user_secrets.custom_secrets.keys())
return GETCustomSecrets(custom_secrets=custom_secrets)
except Exception as e:
logger.warning(f'Invalid token: {e}')
return JSONResponse(
@@ -186,9 +180,9 @@ async def create_custom_secret(
status_code=status.HTTP_400_BAD_REQUEST,
content={'message': f'Secret {secret_name} already exists'},
)
custom_secrets[secret_name] = secret_value
# Create a new UserSecrets that preserves provider tokens
updated_user_secrets = UserSecrets(
custom_secrets=custom_secrets,
@@ -208,10 +202,11 @@ async def create_custom_secret(
content={'error': 'Something went wrong creating secret'},
)
@app.put('/secrets/{secret_id}', response_model=dict[str, str])
async def update_custom_secret(
secret_id: str,
incoming_secret: POSTCustomSecrets,
secret_id: str,
incoming_secret: POSTCustomSecrets,
secrets_store: SecretsStore = Depends(get_secrets_store),
) -> JSONResponse:
try:
@@ -289,4 +284,3 @@ async def delete_custom_secret(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
content={'error': 'Something went wrong deleting secret'},
)

View File

@@ -6,8 +6,6 @@ from openhands.integrations.provider import (
PROVIDER_TOKEN_TYPE,
ProviderType,
)
from openhands.server.routes.secrets import invalidate_legacy_secrets_store
from openhands.server.settings import (
GETSettingsModel,
@@ -18,8 +16,8 @@ from openhands.server.user_auth import (
get_secrets_store,
get_user_settings_store,
)
from openhands.storage.settings.secret_store import SecretsStore
from openhands.storage.data_models.settings import Settings
from openhands.storage.settings.secret_store import SecretsStore
from openhands.storage.settings.settings_store import SettingsStore
app = APIRouter(prefix='/api')
@@ -29,24 +27,27 @@ app = APIRouter(prefix='/api')
async def load_settings(
provider_tokens: PROVIDER_TOKEN_TYPE | None = Depends(get_provider_tokens),
settings_store: SettingsStore = Depends(get_user_settings_store),
secrets_store: SecretsStore = Depends(get_secrets_store)
secrets_store: SecretsStore = Depends(get_secrets_store),
) -> GETSettingsModel | JSONResponse:
settings = await settings_store.load()
try:
if not settings:
return JSONResponse(
status_code=status.HTTP_404_NOT_FOUND,
content={'error': 'Settings not found'},
)
# On initial load, user secrets may not be populated with values migrated from settings store
user_secrets = await invalidate_legacy_secrets_store(settings, settings_store, secrets_store)
# If invalidation is successful, then the returned user secrets holds the most recent values
git_providers = user_secrets.provider_tokens if user_secrets else provider_tokens
provider_tokens_set: dict[ProviderType, str | None] = {}
# On initial load, user secrets may not be populated with values migrated from settings store
user_secrets = await invalidate_legacy_secrets_store(
settings, settings_store, secrets_store
)
# If invalidation is successful, then the returned user secrets holds the most recent values
git_providers = (
user_secrets.provider_tokens if user_secrets else provider_tokens
)
provider_tokens_set: dict[ProviderType, str | None] = {}
if git_providers:
for provider_type, provider_token in git_providers.items():
if provider_token.token or provider_token.user_id:

View File

@@ -4,9 +4,9 @@ from pydantic import SecretStr
from openhands.integrations.provider import PROVIDER_TOKEN_TYPE
from openhands.integrations.service_types import ProviderType
from openhands.server.settings import Settings
from openhands.server.user_auth.user_auth import AuthType, get_user_auth
from openhands.storage.data_models.user_secrets import UserSecrets
from openhands.storage.settings.secret_store import SecretsStore
from openhands.server.user_auth.user_auth import AuthType, get_user_auth
from openhands.storage.settings.settings_store import SettingsStore

View File

@@ -69,7 +69,6 @@ class DefaultUserAuth(UserAuth):
self._user_secrets = user_secrets
return user_secrets
async def get_provider_tokens(self) -> PROVIDER_TOKEN_TYPE | None:
secrets_store = await self.get_user_secrets()
provider_tokens = getattr(secrets_store, 'provider_tokens', None)

View File

@@ -60,7 +60,7 @@ class UserAuth(ABC):
@abstractmethod
async def get_user_secrets(self) -> UserSecrets | None:
"""Get the user's secrets"""
def get_auth_type(self) -> AuthType | None:
return None

View File

@@ -94,9 +94,7 @@ class Settings(BaseModel):
"""Custom serializer for secrets store."""
"""Force invalidate secret store"""
return {
'provider_tokens': {}
}
return {'provider_tokens': {}}
@staticmethod
def from_config() -> Settings | None:

View File

@@ -1,5 +1,6 @@
from types import MappingProxyType
from typing import Any
from pydantic import (
BaseModel,
ConfigDict,
@@ -10,7 +11,13 @@ from pydantic import (
model_validator,
)
from pydantic.json import pydantic_encoder
from openhands.integrations.provider import CUSTOM_SECRETS_TYPE, PROVIDER_TOKEN_TYPE, PROVIDER_TOKEN_TYPE_WITH_JSON_SCHEMA, ProviderToken
from openhands.integrations.provider import (
CUSTOM_SECRETS_TYPE,
PROVIDER_TOKEN_TYPE,
PROVIDER_TOKEN_TYPE_WITH_JSON_SCHEMA,
ProviderToken,
)
from openhands.integrations.service_types import ProviderType
@@ -29,7 +36,6 @@ class UserSecrets(BaseModel):
arbitrary_types_allowed=True,
)
@field_serializer('provider_tokens')
def provider_tokens_serializer(
self, provider_tokens: PROVIDER_TOKEN_TYPE, info: SerializationInfo

View File

@@ -34,4 +34,4 @@ class FileSecretsStore(SecretsStore):
cls, config: AppConfig, user_id: str | None
) -> FileSecretsStore:
file_store = get_file_store(config.file_store, config.file_store_path)
return FileSecretsStore(file_store)
return FileSecretsStore(file_store)

View File

@@ -1,12 +1,11 @@
from __future__ import annotations
from abc import ABC, abstractmethod
from openhands.core.config.app_config import AppConfig
from openhands.storage.data_models.user_secrets import UserSecrets
class SecretsStore(ABC):
"""Storage for secrets. May or may not support multiple users depending on the environment."""
@@ -20,7 +19,5 @@ class SecretsStore(ABC):
@classmethod
@abstractmethod
async def get_instance(
cls, config: AppConfig, user_id: str | None
) -> SecretsStore:
"""Get a store for the user represented by the token given."""
async def get_instance(cls, config: AppConfig, user_id: str | None) -> SecretsStore:
"""Get a store for the user represented by the token given."""