OpenHands/openhands/app_server/app_conversation/app_conversation_service.py
Tim O'Farrell f292f3a84d
V1 Integration (#11183)
Co-authored-by: openhands <openhands@all-hands.dev>
Co-authored-by: sp.wack <83104063+amanape@users.noreply.github.com>
Co-authored-by: Engel Nyst <enyst@users.noreply.github.com>
2025-10-14 02:16:44 +00:00

101 lines
3.5 KiB
Python

import asyncio
from abc import ABC, abstractmethod
from datetime import datetime
from typing import AsyncGenerator
from uuid import UUID
from openhands.app_server.app_conversation.app_conversation_models import (
AppConversation,
AppConversationPage,
AppConversationSortOrder,
AppConversationStartRequest,
AppConversationStartTask,
)
from openhands.app_server.services.injector import Injector
from openhands.sdk import Workspace
from openhands.sdk.utils.models import DiscriminatedUnionMixin
class AppConversationService(ABC):
"""Service for managing conversations running in sandboxes."""
@abstractmethod
async def search_app_conversations(
self,
title__contains: str | None = None,
created_at__gte: datetime | None = None,
created_at__lt: datetime | None = None,
updated_at__gte: datetime | None = None,
updated_at__lt: datetime | None = None,
sort_order: AppConversationSortOrder = AppConversationSortOrder.CREATED_AT_DESC,
page_id: str | None = None,
limit: int = 100,
) -> AppConversationPage:
"""Search for sandboxed conversations."""
@abstractmethod
async def count_app_conversations(
self,
title__contains: str | None = None,
created_at__gte: datetime | None = None,
created_at__lt: datetime | None = None,
updated_at__gte: datetime | None = None,
updated_at__lt: datetime | None = None,
) -> int:
"""Count sandboxed conversations."""
@abstractmethod
async def get_app_conversation(
self, conversation_id: UUID
) -> AppConversation | None:
"""Get a single sandboxed conversation info. Return None if missing."""
async def batch_get_app_conversations(
self, conversation_ids: list[UUID]
) -> list[AppConversation | None]:
"""Get a batch of sandboxed conversations, returning None for any missing."""
return await asyncio.gather(
*[
self.get_app_conversation(conversation_id)
for conversation_id in conversation_ids
]
)
@abstractmethod
async def start_app_conversation(
self, request: AppConversationStartRequest
) -> AsyncGenerator[AppConversationStartTask, None]:
"""Start a conversation, optionally specifying a sandbox in which to start.
If no sandbox is specified a default may be used or started. This is a convenience
method - the same effect should be achievable by creating / getting a sandbox
id, starting a conversation, attaching a callback, and then running the
conversation.
Yields an instance of AppConversationStartTask as updates occur, which can be used to determine
the progress of the task.
"""
# This is an abstract method - concrete implementations should provide real values
from openhands.app_server.app_conversation.app_conversation_models import (
AppConversationStartRequest,
)
dummy_request = AppConversationStartRequest()
yield AppConversationStartTask(
created_by_user_id='dummy',
request=dummy_request,
)
@abstractmethod
async def run_setup_scripts(
self, task: AppConversationStartTask, workspace: Workspace
) -> AsyncGenerator[AppConversationStartTask, None]:
"""Run the setup scripts for the project and yield status updates"""
yield task
class AppConversationServiceInjector(
DiscriminatedUnionMixin, Injector[AppConversationService], ABC
):
pass