diff --git a/openhands/server/routes/conversation.py b/openhands/server/routes/conversation.py index 09a495b5bf..94dc1f2429 100644 --- a/openhands/server/routes/conversation.py +++ b/openhands/server/routes/conversation.py @@ -147,7 +147,7 @@ async def get_remote_runtime_config( return JSONResponse(content=config) -@app.get('/vscode-url') +@app.get('/vscode-url', deprecated=True) async def get_vscode_url( conversation: ServerConversation = Depends(get_conversation), ) -> JSONResponse: @@ -160,6 +160,9 @@ async def get_vscode_url( Returns: JSONResponse: A JSON response indicating the success of the operation. + + For V1 conversations, the VSCode URL is available in the sandbox's ``exposed_urls`` + field. Use ``GET /api/v1/sandboxes?id={sandbox_id}`` to retrieve sandbox information and use the name VSCODE. """ try: runtime: Runtime = conversation.runtime @@ -179,7 +182,7 @@ async def get_vscode_url( ) -@app.get('/web-hosts') +@app.get('/web-hosts', deprecated=True) async def get_hosts( conversation: ServerConversation = Depends(get_conversation), ) -> JSONResponse: @@ -192,6 +195,9 @@ async def get_hosts( Returns: JSONResponse: A JSON response indicating the success of the operation. + + For V1 conversations, web hosts are available in the sandbox's ``exposed_urls`` + field. Use ``GET /api/v1/sandboxes?id={sandbox_id}`` to retrieve sandbox information and use the name AGENT_SERVER. """ try: runtime: Runtime = conversation.runtime @@ -209,7 +215,7 @@ async def get_hosts( ) -@app.get('/events') +@app.get('/events', deprecated=True) async def search_events( conversation_id: str, start_id: int = 0, @@ -239,6 +245,10 @@ async def search_events( Raises: HTTPException: If conversation is not found or access is denied ValueError: If limit is less than 1 or greater than 100 + + Use the V1 endpoint ``GET /api/v1/events/search?conversation_id__eq={conversation_id}`` + instead, which provides enhanced filtering by event kind, timestamp ranges, + and improved pagination. """ if limit < 0 or limit > 100: raise HTTPException( @@ -275,10 +285,15 @@ async def search_events( } -@app.post('/events') +@app.post('/events', deprecated=True) async def add_event( request: Request, conversation: ServerConversation = Depends(get_conversation) ): + """Add an event to a conversation. + + For V1 conversations, events are managed through the sandbox webhook system. + Use ``POST /api/v1/webhooks/events/{conversation_id}`` for event callbacks. + """ data = await request.json() await conversation_manager.send_event_to_conversation(conversation.sid, data) return JSONResponse({'success': True}) @@ -342,7 +357,7 @@ class MicroagentResponse(BaseModel): tools: list[str] = [] -@app.get('/microagents') +@app.get('/microagents', deprecated=True) async def get_microagents( conversation: ServerConversation = Depends(get_conversation), ) -> JSONResponse: @@ -352,6 +367,9 @@ async def get_microagents( Returns: JSONResponse: A JSON response containing the list of microagents. + + Use the V1 endpoint ``GET /api/v1/app-conversations/{conversation_id}/skills`` instead, + which provides skill information including triggers and content for V1 conversations. """ try: # Get the agent session for this conversation diff --git a/openhands/server/routes/manage_conversations.py b/openhands/server/routes/manage_conversations.py index 69f7c747b0..bce8db6f97 100644 --- a/openhands/server/routes/manage_conversations.py +++ b/openhands/server/routes/manage_conversations.py @@ -229,7 +229,7 @@ class ProvidersSetModel(BaseModel): providers_set: list[ProviderType] | None = None -@app.post('/conversations') +@app.post('/conversations', deprecated=True) async def new_conversation( data: InitSessionRequest, user_id: str = Depends(get_user_id), @@ -241,6 +241,9 @@ async def new_conversation( After successful initialization, the client should connect to the WebSocket using the returned conversation ID. + + Use the V1 endpoint ``POST /api/v1/app-conversations`` instead, which provides + improved conversation management with sandbox lifecycle support. """ logger.info(f'initializing_new_conversation:{data}') repository = data.repository @@ -318,7 +321,7 @@ async def new_conversation( ) -@app.get('/conversations') +@app.get('/conversations', deprecated=True) async def search_conversations( page_id: str | None = None, limit: int = 20, @@ -333,6 +336,11 @@ async def search_conversations( conversation_store: ConversationStore = Depends(get_conversation_store), app_conversation_service: AppConversationService = app_conversation_service_dependency, ) -> ConversationInfoResultSet: + """Search and list conversations with pagination support. + + Use the V1 endpoint ``GET /api/v1/app-conversations/search`` instead, which provides + enhanced filtering, sorting, and pagination capabilities. + """ # Parse combined page_id to extract separate page_ids for each source v0_page_id = None v1_page_id = None @@ -457,12 +465,17 @@ async def search_conversations( return ConversationInfoResultSet(results=final_results, next_page_id=next_page_id) -@app.get('/conversations/{conversation_id}') +@app.get('/conversations/{conversation_id}', deprecated=True) async def get_conversation( conversation_id: str = Depends(validate_conversation_id), conversation_store: ConversationStore = Depends(get_conversation_store), app_conversation_service: AppConversationService = app_conversation_service_dependency, ) -> ConversationInfo | None: + """Get a single conversation by ID. + + Use the V1 endpoint ``GET /api/v1/app-conversations?ids={conversation_id}`` instead, + which supports batch retrieval of conversations by their IDs. + """ try: # Shim to add V1 conversations try: @@ -492,7 +505,7 @@ async def get_conversation( return None -@app.delete('/conversations/{conversation_id}') +@app.delete('/conversations/{conversation_id}', deprecated=True) async def delete_conversation( request: Request, conversation_id: str = Depends(validate_conversation_id), @@ -503,6 +516,11 @@ async def delete_conversation( db_session: AsyncSession = db_session_dependency, httpx_client: httpx.AsyncClient = httpx_client_dependency, ) -> bool: + """Delete a conversation by ID. + + For V1 conversations, use ``DELETE /api/v1/sandboxes/{sandbox_id}`` to delete the + associated sandbox, which will clean up the conversation resources. + """ set_db_session_keep_open(request.state, True) set_httpx_client_keep_open(request.state, True) @@ -715,7 +733,7 @@ async def _get_conversation_info( return None -@app.post('/conversations/{conversation_id}/start') +@app.post('/conversations/{conversation_id}/start', deprecated=True) async def start_conversation( providers_set: ProvidersSetModel, conversation_id: str = Depends(validate_conversation_id), @@ -729,6 +747,10 @@ async def start_conversation( This endpoint calls the conversation_manager's maybe_start_agent_loop method to start a conversation. If the conversation is already running, it will return the existing agent loop info. + + Use the V1 endpoint ``POST /api/v1/app-conversations`` instead, which combines + conversation creation and starting into a single operation with integrated + sandbox lifecycle management. """ logger.info( f'Starting conversation: {conversation_id}', @@ -792,7 +814,7 @@ async def start_conversation( ) -@app.post('/conversations/{conversation_id}/stop') +@app.post('/conversations/{conversation_id}/stop', deprecated=True) async def stop_conversation( conversation_id: str = Depends(validate_conversation_id), user_id: str = Depends(get_user_id), @@ -801,6 +823,10 @@ async def stop_conversation( This endpoint calls the conversation_manager's close_session method to stop a conversation. + + Use the V1 endpoint ``POST /api/v1/sandboxes/{sandbox_id}/pause`` instead to pause + the sandbox execution, or ``DELETE /api/v1/sandboxes/{sandbox_id}`` to fully stop + and remove the sandbox. """ logger.info(f'Stopping conversation: {conversation_id}') @@ -1070,7 +1096,7 @@ async def _update_v0_conversation( return True -@app.patch('/conversations/{conversation_id}') +@app.patch('/conversations/{conversation_id}', deprecated=True) async def update_conversation( data: UpdateConversationRequest, conversation_id: str = Depends(validate_conversation_id), @@ -1098,6 +1124,10 @@ async def update_conversation( Raises: HTTPException: If conversation is not found or user lacks permission + + This endpoint is part of the legacy V0 API and will be removed in a future release. + Use the V1 endpoint ``PATCH /api/v1/app-conversations/{conversation_id}`` instead, + which provides the same functionality for updating conversation metadata. """ logger.info( f'Updating conversation {conversation_id} with title: {data.title}',