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 <openhands@all-hands.dev>
This commit is contained in:
openhands
2025-10-29 19:16:34 +00:00
parent df03a56888
commit 20366ba973
3 changed files with 52 additions and 1 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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 = (