diff --git a/skyvern/forge/sdk/api/llm/config_registry.py b/skyvern/forge/sdk/api/llm/config_registry.py index 9f3fed2a..cbf8bb97 100644 --- a/skyvern/forge/sdk/api/llm/config_registry.py +++ b/skyvern/forge/sdk/api/llm/config_registry.py @@ -1173,44 +1173,6 @@ if settings.ENABLE_VERTEX_AI and settings.VERTEX_CREDENTIALS: ), ), ) - LLMConfigRegistry.register_config( - "VERTEX_GEMINI_2.5_FLASH_PREVIEW_09_2025", - LLMConfig( - "vertex_ai/gemini-2.5-flash-preview-09-2025", - ["VERTEX_CREDENTIALS"], - supports_vision=True, - add_assistant_prefix=False, - max_completion_tokens=65535, - litellm_params=LiteLLMParams( - vertex_credentials=settings.VERTEX_CREDENTIALS, - api_base=f"{api_base}/gemini-2.5-flash-preview-09-2025" if api_base else None, - vertex_location=settings.VERTEX_LOCATION, - thinking={ - "budget_tokens": settings.GEMINI_THINKING_BUDGET, - "type": "enabled" if settings.GEMINI_INCLUDE_THOUGHT else None, - }, - ), - ), - ) - LLMConfigRegistry.register_config( - "VERTEX_GEMINI_2.5_FLASH_LITE_PREVIEW_09_2025", - LLMConfig( - "vertex_ai/gemini-2.5-flash-lite-preview-09-2025", - ["VERTEX_CREDENTIALS"], - supports_vision=True, - add_assistant_prefix=False, - max_completion_tokens=65535, - litellm_params=LiteLLMParams( - vertex_credentials=settings.VERTEX_CREDENTIALS, - api_base=f"{api_base}/gemini-2.5-flash-lite-preview-09-2025" if api_base else None, - vertex_location=settings.VERTEX_LOCATION, - thinking={ - "budget_tokens": settings.GEMINI_THINKING_BUDGET, - "type": "enabled" if settings.GEMINI_INCLUDE_THOUGHT else None, - }, - ), - ), - ) LLMConfigRegistry.register_config( "VERTEX_GEMINI_FLASH_2_0", LLMConfig( diff --git a/skyvern/forge/sdk/db/client.py b/skyvern/forge/sdk/db/client.py index dadb41dc..500f3a17 100644 --- a/skyvern/forge/sdk/db/client.py +++ b/skyvern/forge/sdk/db/client.py @@ -3,7 +3,7 @@ from datetime import datetime, timedelta, timezone from typing import Any, List, Literal, Sequence, overload 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.ext.asyncio import AsyncEngine, async_sessionmaker, create_async_engine @@ -874,14 +874,14 @@ class AgentDB: async def get_valid_org_auth_token( self, organization_id: str, - token_type: Literal[OrganizationAuthTokenType.api, OrganizationAuthTokenType.onepassword_service_account], + token_type: Literal[OrganizationAuthTokenType.api, OrganizationAuthTokenType.onepassword_service_account], # type:ignore ) -> OrganizationAuthToken | None: ... @overload - async def get_valid_org_auth_token( + async def get_valid_org_auth_token( # type: ignore self, organization_id: str, - token_type: Literal[OrganizationAuthTokenType.azure_client_secret_credential], + token_type: Literal[OrganizationAuthTokenType.azure_client_secret_credential], # type:ignore ) -> AzureOrganizationAuthToken | None: ... async def get_valid_org_auth_token( @@ -3207,6 +3207,50 @@ class AgentDB: LOG.error("UnexpectedError", exc_info=True) 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( self, runnable_id: str, organization_id: str | None = None ) -> PersistentBrowserSession | None: diff --git a/skyvern/forge/sdk/routes/browser_sessions.py b/skyvern/forge/sdk/routes/browser_sessions.py index 862f4109..78c00cfa 100644 --- a/skyvern/forge/sdk/routes/browser_sessions.py +++ b/skyvern/forge/sdk/routes/browser_sessions.py @@ -1,6 +1,6 @@ import asyncio -from fastapi import Depends, HTTPException, Path +from fastapi import Depends, HTTPException, Path, Query from fastapi.responses import ORJSONResponse from skyvern import analytics @@ -18,6 +18,38 @@ from skyvern.schemas.browser_sessions import CreateBrowserSessionRequest 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( "/browser_sessions", response_model=BrowserSessionResponse,