mirror of
https://github.com/OpenHands/OpenHands.git
synced 2026-03-22 05:37:20 +08:00
feat: add created_at__gte filter to search_app_conversation_start_tasks (#11740)
Co-authored-by: openhands <openhands@all-hands.dev>
This commit is contained in:
@@ -111,11 +111,11 @@ class V1ConversationService {
|
||||
* Search for start tasks (ongoing tasks that haven't completed yet)
|
||||
* Use this to find tasks that were started but the user navigated away
|
||||
*
|
||||
* Note: Backend only supports filtering by limit. To filter by repository/trigger,
|
||||
* Note: Backend supports filtering by limit and created_at__gte. To filter by repository/trigger,
|
||||
* filter the results client-side after fetching.
|
||||
*
|
||||
* @param limit Maximum number of tasks to return (max 100)
|
||||
* @returns Array of start tasks
|
||||
* @returns Array of start tasks from the last 20 minutes
|
||||
*/
|
||||
static async searchStartTasks(
|
||||
limit: number = 100,
|
||||
@@ -123,6 +123,10 @@ class V1ConversationService {
|
||||
const params = new URLSearchParams();
|
||||
params.append("limit", limit.toString());
|
||||
|
||||
// Only get tasks from the last 20 minutes
|
||||
const twentyMinutesAgo = new Date(Date.now() - 20 * 60 * 1000);
|
||||
params.append("created_at__gte", twentyMinutesAgo.toISOString());
|
||||
|
||||
const { data } = await openHands.get<V1AppConversationStartTaskPage>(
|
||||
`/api/v1/app-conversations/start-tasks/search?${params.toString()}`,
|
||||
);
|
||||
|
||||
@@ -207,6 +207,10 @@ async def search_app_conversation_start_tasks(
|
||||
UUID | None,
|
||||
Query(title='Filter by conversation ID equal to this value'),
|
||||
] = None,
|
||||
created_at__gte: Annotated[
|
||||
datetime | None,
|
||||
Query(title='Filter by created_at greater than or equal to this datetime'),
|
||||
] = None,
|
||||
sort_order: Annotated[
|
||||
AppConversationStartTaskSortOrder,
|
||||
Query(title='Sort order for the results'),
|
||||
@@ -233,6 +237,7 @@ async def search_app_conversation_start_tasks(
|
||||
return (
|
||||
await app_conversation_start_task_service.search_app_conversation_start_tasks(
|
||||
conversation_id__eq=conversation_id__eq,
|
||||
created_at__gte=created_at__gte,
|
||||
sort_order=sort_order,
|
||||
page_id=page_id,
|
||||
limit=limit,
|
||||
@@ -246,6 +251,10 @@ async def count_app_conversation_start_tasks(
|
||||
UUID | None,
|
||||
Query(title='Filter by conversation ID equal to this value'),
|
||||
] = None,
|
||||
created_at__gte: Annotated[
|
||||
datetime | None,
|
||||
Query(title='Filter by created_at greater than or equal to this datetime'),
|
||||
] = None,
|
||||
app_conversation_start_task_service: AppConversationStartTaskService = (
|
||||
app_conversation_start_task_service_dependency
|
||||
),
|
||||
@@ -253,6 +262,7 @@ async def count_app_conversation_start_tasks(
|
||||
"""Count conversation start tasks matching the given filters."""
|
||||
return await app_conversation_start_task_service.count_app_conversation_start_tasks(
|
||||
conversation_id__eq=conversation_id__eq,
|
||||
created_at__gte=created_at__gte,
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import asyncio
|
||||
from abc import ABC, abstractmethod
|
||||
from datetime import datetime
|
||||
from uuid import UUID
|
||||
|
||||
from openhands.app_server.app_conversation.app_conversation_models import (
|
||||
@@ -18,6 +19,7 @@ class AppConversationStartTaskService(ABC):
|
||||
async def search_app_conversation_start_tasks(
|
||||
self,
|
||||
conversation_id__eq: UUID | None = None,
|
||||
created_at__gte: datetime | None = None,
|
||||
sort_order: AppConversationStartTaskSortOrder = AppConversationStartTaskSortOrder.CREATED_AT_DESC,
|
||||
page_id: str | None = None,
|
||||
limit: int = 100,
|
||||
@@ -28,6 +30,7 @@ class AppConversationStartTaskService(ABC):
|
||||
async def count_app_conversation_start_tasks(
|
||||
self,
|
||||
conversation_id__eq: UUID | None = None,
|
||||
created_at__gte: datetime | None = None,
|
||||
) -> int:
|
||||
"""Count conversation start tasks."""
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from dataclasses import dataclass
|
||||
from datetime import datetime
|
||||
from typing import AsyncGenerator
|
||||
from uuid import UUID
|
||||
|
||||
@@ -75,6 +76,7 @@ class SQLAppConversationStartTaskService(AppConversationStartTaskService):
|
||||
async def search_app_conversation_start_tasks(
|
||||
self,
|
||||
conversation_id__eq: UUID | None = None,
|
||||
created_at__gte: datetime | None = None,
|
||||
sort_order: AppConversationStartTaskSortOrder = AppConversationStartTaskSortOrder.CREATED_AT_DESC,
|
||||
page_id: str | None = None,
|
||||
limit: int = 100,
|
||||
@@ -95,6 +97,12 @@ class SQLAppConversationStartTaskService(AppConversationStartTaskService):
|
||||
== conversation_id__eq
|
||||
)
|
||||
|
||||
# Apply created_at__gte filter
|
||||
if created_at__gte is not None:
|
||||
query = query.where(
|
||||
StoredAppConversationStartTask.created_at >= created_at__gte
|
||||
)
|
||||
|
||||
# Add sort order
|
||||
if sort_order == AppConversationStartTaskSortOrder.CREATED_AT:
|
||||
query = query.order_by(StoredAppConversationStartTask.created_at)
|
||||
@@ -139,6 +147,7 @@ class SQLAppConversationStartTaskService(AppConversationStartTaskService):
|
||||
async def count_app_conversation_start_tasks(
|
||||
self,
|
||||
conversation_id__eq: UUID | None = None,
|
||||
created_at__gte: datetime | None = None,
|
||||
) -> int:
|
||||
"""Count conversation start tasks."""
|
||||
query = select(func.count(StoredAppConversationStartTask.id))
|
||||
@@ -156,6 +165,12 @@ class SQLAppConversationStartTaskService(AppConversationStartTaskService):
|
||||
== conversation_id__eq
|
||||
)
|
||||
|
||||
# Apply created_at__gte filter
|
||||
if created_at__gte is not None:
|
||||
query = query.where(
|
||||
StoredAppConversationStartTask.created_at >= created_at__gte
|
||||
)
|
||||
|
||||
result = await self.session.execute(query)
|
||||
count = result.scalar()
|
||||
return count or 0
|
||||
|
||||
@@ -639,3 +639,145 @@ class TestSQLAppConversationStartTaskService:
|
||||
|
||||
user2_count = await user2_service.count_app_conversation_start_tasks()
|
||||
assert user2_count == 1
|
||||
|
||||
async def test_search_app_conversation_start_tasks_with_created_at_gte_filter(
|
||||
self,
|
||||
service: SQLAppConversationStartTaskService,
|
||||
sample_request: AppConversationStartRequest,
|
||||
):
|
||||
"""Test search with created_at__gte filter."""
|
||||
from datetime import timedelta
|
||||
|
||||
from openhands.agent_server.models import utc_now
|
||||
|
||||
# Create tasks with different creation times
|
||||
base_time = utc_now()
|
||||
|
||||
# Task 1: created 2 hours ago
|
||||
task1 = AppConversationStartTask(
|
||||
id=uuid4(),
|
||||
created_by_user_id='user1',
|
||||
status=AppConversationStartTaskStatus.WORKING,
|
||||
request=sample_request,
|
||||
)
|
||||
task1.created_at = base_time - timedelta(hours=2)
|
||||
await service.save_app_conversation_start_task(task1)
|
||||
|
||||
# Task 2: created 1 hour ago
|
||||
task2 = AppConversationStartTask(
|
||||
id=uuid4(),
|
||||
created_by_user_id='user1',
|
||||
status=AppConversationStartTaskStatus.READY,
|
||||
request=sample_request,
|
||||
)
|
||||
task2.created_at = base_time - timedelta(hours=1)
|
||||
await service.save_app_conversation_start_task(task2)
|
||||
|
||||
# Task 3: created 30 minutes ago
|
||||
task3 = AppConversationStartTask(
|
||||
id=uuid4(),
|
||||
created_by_user_id='user1',
|
||||
status=AppConversationStartTaskStatus.WORKING,
|
||||
request=sample_request,
|
||||
)
|
||||
task3.created_at = base_time - timedelta(minutes=30)
|
||||
await service.save_app_conversation_start_task(task3)
|
||||
|
||||
# Search for tasks created in the last 90 minutes
|
||||
filter_time = base_time - timedelta(minutes=90)
|
||||
result = await service.search_app_conversation_start_tasks(
|
||||
created_at__gte=filter_time
|
||||
)
|
||||
|
||||
# Should return task2 and task3 (created within last 90 minutes)
|
||||
assert len(result.items) == 2
|
||||
task_ids = [task.id for task in result.items]
|
||||
assert task2.id in task_ids
|
||||
assert task3.id in task_ids
|
||||
assert task1.id not in task_ids
|
||||
|
||||
# Test count with the same filter
|
||||
count = await service.count_app_conversation_start_tasks(
|
||||
created_at__gte=filter_time
|
||||
)
|
||||
assert count == 2
|
||||
|
||||
# Search for tasks created in the last 45 minutes
|
||||
filter_time_recent = base_time - timedelta(minutes=45)
|
||||
result_recent = await service.search_app_conversation_start_tasks(
|
||||
created_at__gte=filter_time_recent
|
||||
)
|
||||
|
||||
# Should return only task3
|
||||
assert len(result_recent.items) == 1
|
||||
assert result_recent.items[0].id == task3.id
|
||||
|
||||
# Test count with recent filter
|
||||
count_recent = await service.count_app_conversation_start_tasks(
|
||||
created_at__gte=filter_time_recent
|
||||
)
|
||||
assert count_recent == 1
|
||||
|
||||
async def test_search_app_conversation_start_tasks_combined_filters(
|
||||
self,
|
||||
service: SQLAppConversationStartTaskService,
|
||||
sample_request: AppConversationStartRequest,
|
||||
):
|
||||
"""Test search with both conversation_id and created_at__gte filters."""
|
||||
from datetime import timedelta
|
||||
|
||||
from openhands.agent_server.models import utc_now
|
||||
|
||||
conversation_id1 = uuid4()
|
||||
conversation_id2 = uuid4()
|
||||
base_time = utc_now()
|
||||
|
||||
# Task 1: conversation_id1, created 2 hours ago
|
||||
task1 = AppConversationStartTask(
|
||||
id=uuid4(),
|
||||
created_by_user_id='user1',
|
||||
status=AppConversationStartTaskStatus.WORKING,
|
||||
app_conversation_id=conversation_id1,
|
||||
request=sample_request,
|
||||
)
|
||||
task1.created_at = base_time - timedelta(hours=2)
|
||||
await service.save_app_conversation_start_task(task1)
|
||||
|
||||
# Task 2: conversation_id1, created 30 minutes ago
|
||||
task2 = AppConversationStartTask(
|
||||
id=uuid4(),
|
||||
created_by_user_id='user1',
|
||||
status=AppConversationStartTaskStatus.READY,
|
||||
app_conversation_id=conversation_id1,
|
||||
request=sample_request,
|
||||
)
|
||||
task2.created_at = base_time - timedelta(minutes=30)
|
||||
await service.save_app_conversation_start_task(task2)
|
||||
|
||||
# Task 3: conversation_id2, created 30 minutes ago
|
||||
task3 = AppConversationStartTask(
|
||||
id=uuid4(),
|
||||
created_by_user_id='user1',
|
||||
status=AppConversationStartTaskStatus.WORKING,
|
||||
app_conversation_id=conversation_id2,
|
||||
request=sample_request,
|
||||
)
|
||||
task3.created_at = base_time - timedelta(minutes=30)
|
||||
await service.save_app_conversation_start_task(task3)
|
||||
|
||||
# Search for tasks with conversation_id1 created in the last hour
|
||||
filter_time = base_time - timedelta(hours=1)
|
||||
result = await service.search_app_conversation_start_tasks(
|
||||
conversation_id__eq=conversation_id1, created_at__gte=filter_time
|
||||
)
|
||||
|
||||
# Should return only task2 (conversation_id1 and created within last hour)
|
||||
assert len(result.items) == 1
|
||||
assert result.items[0].id == task2.id
|
||||
assert result.items[0].app_conversation_id == conversation_id1
|
||||
|
||||
# Test count with combined filters
|
||||
count = await service.count_app_conversation_start_tasks(
|
||||
conversation_id__eq=conversation_id1, created_at__gte=filter_time
|
||||
)
|
||||
assert count == 1
|
||||
|
||||
Reference in New Issue
Block a user