Add endpoint for browser sessions history (#3548)

This commit is contained in:
Jonathan Dobson
2025-09-29 09:29:47 -04:00
committed by GitHub
parent 56b4d828c1
commit f2d32da914
2 changed files with 79 additions and 3 deletions

View File

@@ -3,7 +3,7 @@ from datetime import datetime, timedelta, timezone
from typing import Any, List, Literal, Sequence, overload from typing import Any, List, Literal, Sequence, overload
import structlog import structlog
from sqlalchemy import and_, asc, delete, distinct, func, or_, pool, select, tuple_, update from sqlalchemy import and_, asc, case, delete, distinct, func, or_, pool, select, tuple_, update
from sqlalchemy.exc import SQLAlchemyError from sqlalchemy.exc import SQLAlchemyError
from sqlalchemy.ext.asyncio import AsyncEngine, async_sessionmaker, create_async_engine from sqlalchemy.ext.asyncio import AsyncEngine, async_sessionmaker, create_async_engine
@@ -878,7 +878,7 @@ class AgentDB:
) -> OrganizationAuthToken | None: ... ) -> OrganizationAuthToken | None: ...
@overload @overload
async def get_valid_org_auth_token( async def get_valid_org_auth_token( # type: ignore
self, self,
organization_id: str, organization_id: str,
token_type: Literal["azure_client_secret_credential"], token_type: Literal["azure_client_secret_credential"],
@@ -3210,6 +3210,50 @@ class AgentDB:
LOG.error("UnexpectedError", exc_info=True) LOG.error("UnexpectedError", exc_info=True)
raise raise
async def get_persistent_browser_sessions_history(
self,
organization_id: str,
page: int = 1,
page_size: int = 10,
lookback_hours: int = 24 * 7,
) -> list[PersistentBrowserSession]:
"""Get persistent browser sessions history for an organization."""
try:
async with self.Session() as session:
open_first = case(
(
and_(
PersistentBrowserSessionModel.started_at.is_not(None),
PersistentBrowserSessionModel.completed_at.is_(None),
),
0, # open
),
else_=1, # not open
)
result = await session.execute(
select(PersistentBrowserSessionModel)
.filter_by(organization_id=organization_id)
.filter_by(deleted_at=None)
.filter(
PersistentBrowserSessionModel.created_at > datetime.utcnow() - timedelta(hours=lookback_hours)
)
.order_by(
open_first.asc(), # open sessions first
PersistentBrowserSessionModel.created_at.desc(), # then newest within each group
)
.offset((page - 1) * page_size)
.limit(page_size)
)
sessions = result.scalars().all()
return [PersistentBrowserSession.model_validate(session) for session in sessions]
except SQLAlchemyError:
LOG.error("SQLAlchemyError", exc_info=True)
raise
except Exception:
LOG.error("UnexpectedError", exc_info=True)
raise
async def get_persistent_browser_session_by_runnable_id( async def get_persistent_browser_session_by_runnable_id(
self, runnable_id: str, organization_id: str | None = None self, runnable_id: str, organization_id: str | None = None
) -> PersistentBrowserSession | None: ) -> PersistentBrowserSession | None:

View File

@@ -1,6 +1,6 @@
import asyncio import asyncio
from fastapi import Depends, HTTPException, Path from fastapi import Depends, HTTPException, Path, Query
from fastapi.responses import ORJSONResponse from fastapi.responses import ORJSONResponse
from skyvern import analytics from skyvern import analytics
@@ -18,6 +18,38 @@ from skyvern.schemas.browser_sessions import CreateBrowserSessionRequest
from skyvern.webeye.schemas import BrowserSessionResponse from skyvern.webeye.schemas import BrowserSessionResponse
@base_router.get(
"/browser_sessions/history",
include_in_schema=False,
)
@base_router.get(
"/browser_sessions/history/",
include_in_schema=False,
)
async def get_browser_sessions_all(
current_org: Organization = Depends(org_auth_service.get_current_org),
page: int = Query(1, ge=1, description="Page number for pagination"),
page_size: int = Query(10, ge=1, le=100, description="Number of items per page"),
) -> list[BrowserSessionResponse]:
"""Get all browser sessions for the organization"""
analytics.capture("skyvern-oss-agent-browser-sessions-get-all")
browser_sessions = await app.DATABASE.get_persistent_browser_sessions_history(
current_org.organization_id,
page=page,
page_size=page_size,
)
responses = await asyncio.gather(
*[
BrowserSessionResponse.from_browser_session(browser_session, app.STORAGE)
for browser_session in browser_sessions
]
)
return responses
@base_router.post( @base_router.post(
"/browser_sessions", "/browser_sessions",
response_model=BrowserSessionResponse, response_model=BrowserSessionResponse,