From 20366ba9731300b0c8a430a22ebc92fa59efcab5 Mon Sep 17 00:00:00 2001 From: openhands Date: Wed, 29 Oct 2025 19:16:34 +0000 Subject: [PATCH] feat: Enable enterprise SQLAppConversationInfoService override in SAAS mode - Add SaasAppConversationInfoServiceInjector to properly inject enterprise service - Modify base config to use enterprise injector when OPENHANDS_CONFIG_CLS contains 'saas' - Ensure OPENHANDS_CONFIG_CLS is set in saas_server.py for proper SAAS mode detection - Clean up stored_conversation_metadata.py imports and exports This ensures that when launching the enterprise server with uvicorn saas_server:app, the overridden _secure_select() method with user-based filtering is used instead of the base OSS implementation. Co-authored-by: openhands --- enterprise/saas_server.py | 4 +++ .../saas_app_conversation_info_injector.py | 34 +++++++++++++++++++ openhands/app_server/config.py | 15 +++++++- 3 files changed, 52 insertions(+), 1 deletion(-) create mode 100644 enterprise/storage/saas_app_conversation_info_injector.py diff --git a/enterprise/saas_server.py b/enterprise/saas_server.py index 4c3c7c49ba..01a6810bbd 100644 --- a/enterprise/saas_server.py +++ b/enterprise/saas_server.py @@ -4,6 +4,10 @@ from dotenv import load_dotenv load_dotenv() +# Ensure SAAS configuration is used +if not os.getenv('OPENHANDS_CONFIG_CLS'): + os.environ['OPENHANDS_CONFIG_CLS'] = 'server.config.SaaSServerConfig' + import socketio # noqa: E402 from fastapi import Request, status # noqa: E402 from fastapi.middleware.cors import CORSMiddleware # noqa: E402 diff --git a/enterprise/storage/saas_app_conversation_info_injector.py b/enterprise/storage/saas_app_conversation_info_injector.py new file mode 100644 index 0000000000..9ae0c716a3 --- /dev/null +++ b/enterprise/storage/saas_app_conversation_info_injector.py @@ -0,0 +1,34 @@ +"""Enterprise injector for SQLAppConversationInfoService with SAAS filtering.""" + +from typing import AsyncGenerator + +from fastapi import Request + +from openhands.app_server.app_conversation.app_conversation_info_service import ( + AppConversationInfoService, + AppConversationInfoServiceInjector, +) +from openhands.app_server.services.injector import InjectorState + + +class SaasAppConversationInfoServiceInjector(AppConversationInfoServiceInjector): + """Enterprise injector for SQLAppConversationInfoService with SAAS filtering.""" + + async def inject( + self, state: InjectorState, request: Request | None = None + ) -> AsyncGenerator[AppConversationInfoService, None]: + # Define inline to prevent circular lookup + from openhands.app_server.config import ( + get_db_session, + get_user_context, + ) + from storage.stored_conversation_metadata import SQLAppConversationInfoServiceSaas + + async with ( + get_user_context(state, request) as user_context, + get_db_session(state, request) as db_session, + ): + service = SQLAppConversationInfoServiceSaas( + db_session=db_session, user_context=user_context + ) + yield service \ No newline at end of file diff --git a/openhands/app_server/config.py b/openhands/app_server/config.py index 2dd50d7fa7..3c87dff143 100644 --- a/openhands/app_server/config.py +++ b/openhands/app_server/config.py @@ -175,7 +175,20 @@ def config_from_env() -> AppServerConfig: config.sandbox_spec = DockerSandboxSpecServiceInjector() if config.app_conversation_info is None: - config.app_conversation_info = SQLAppConversationInfoServiceInjector() + # Use enterprise injector if running in SAAS mode + if 'saas' in (os.getenv('OPENHANDS_CONFIG_CLS') or '').lower(): + try: + # Import enterprise injector dynamically + from enterprise.storage.saas_app_conversation_info_injector import ( + SaasAppConversationInfoServiceInjector, + ) + + config.app_conversation_info = SaasAppConversationInfoServiceInjector() + except ImportError: + # Fallback to OSS injector if enterprise module is not available + config.app_conversation_info = SQLAppConversationInfoServiceInjector() + else: + config.app_conversation_info = SQLAppConversationInfoServiceInjector() if config.app_conversation_start_task is None: config.app_conversation_start_task = (