[SKY-6973] [1/3] Browser Profiles - database and s3 storage layer (#3899)
This commit is contained in:
@@ -9,7 +9,7 @@ from sqlalchemy.ext.asyncio import AsyncEngine, async_sessionmaker, create_async
|
||||
|
||||
from skyvern.config import settings
|
||||
from skyvern.constants import DEFAULT_SCRIPT_RUN_ID
|
||||
from skyvern.exceptions import WorkflowParameterNotFound, WorkflowRunNotFound
|
||||
from skyvern.exceptions import BrowserProfileNotFound, WorkflowParameterNotFound, WorkflowRunNotFound
|
||||
from skyvern.forge.sdk.artifact.models import Artifact, ArtifactType
|
||||
from skyvern.forge.sdk.db.enums import OrganizationAuthTokenType, TaskType
|
||||
from skyvern.forge.sdk.db.exceptions import NotFoundError
|
||||
@@ -23,6 +23,7 @@ from skyvern.forge.sdk.db.models import (
|
||||
BitwardenLoginCredentialParameterModel,
|
||||
BitwardenSensitiveInformationParameterModel,
|
||||
BlockRunModel,
|
||||
BrowserProfileModel,
|
||||
CredentialModel,
|
||||
CredentialParameterModel,
|
||||
DebugSessionModel,
|
||||
@@ -77,6 +78,7 @@ from skyvern.forge.sdk.encrypt.base import EncryptMethod
|
||||
from skyvern.forge.sdk.log_artifacts import save_workflow_run_logs
|
||||
from skyvern.forge.sdk.models import Step, StepStatus
|
||||
from skyvern.forge.sdk.schemas.ai_suggestions import AISuggestion
|
||||
from skyvern.forge.sdk.schemas.browser_profiles import BrowserProfile
|
||||
from skyvern.forge.sdk.schemas.credentials import Credential, CredentialType, CredentialVaultType
|
||||
from skyvern.forge.sdk.schemas.debug_sessions import BlockRun, DebugSession, DebugSessionRun
|
||||
from skyvern.forge.sdk.schemas.organization_bitwarden_collections import OrganizationBitwardenCollection
|
||||
@@ -3500,6 +3502,89 @@ class AgentDB:
|
||||
for workflow_run_block in workflow_run_blocks
|
||||
]
|
||||
|
||||
async def create_browser_profile(
|
||||
self,
|
||||
organization_id: str,
|
||||
name: str,
|
||||
description: str | None = None,
|
||||
) -> BrowserProfile:
|
||||
try:
|
||||
async with self.Session() as session:
|
||||
browser_profile = BrowserProfileModel(
|
||||
organization_id=organization_id,
|
||||
name=name,
|
||||
description=description,
|
||||
)
|
||||
session.add(browser_profile)
|
||||
await session.commit()
|
||||
await session.refresh(browser_profile)
|
||||
return BrowserProfile.model_validate(browser_profile)
|
||||
except SQLAlchemyError:
|
||||
LOG.error("SQLAlchemyError in create_browser_profile", exc_info=True)
|
||||
raise
|
||||
|
||||
async def get_browser_profile(
|
||||
self,
|
||||
profile_id: str,
|
||||
organization_id: str,
|
||||
include_deleted: bool = False,
|
||||
) -> BrowserProfile | None:
|
||||
try:
|
||||
async with self.Session() as session:
|
||||
query = (
|
||||
select(BrowserProfileModel)
|
||||
.filter_by(browser_profile_id=profile_id)
|
||||
.filter_by(organization_id=organization_id)
|
||||
)
|
||||
if not include_deleted:
|
||||
query = query.filter(BrowserProfileModel.deleted_at.is_(None))
|
||||
|
||||
browser_profile = (await session.scalars(query)).first()
|
||||
if not browser_profile:
|
||||
return None
|
||||
return BrowserProfile.model_validate(browser_profile)
|
||||
except SQLAlchemyError:
|
||||
LOG.error("SQLAlchemyError in get_browser_profile", exc_info=True)
|
||||
raise
|
||||
|
||||
async def list_browser_profiles(
|
||||
self,
|
||||
organization_id: str,
|
||||
include_deleted: bool = False,
|
||||
) -> list[BrowserProfile]:
|
||||
try:
|
||||
async with self.Session() as session:
|
||||
query = select(BrowserProfileModel).filter_by(organization_id=organization_id)
|
||||
if not include_deleted:
|
||||
query = query.filter(BrowserProfileModel.deleted_at.is_(None))
|
||||
browser_profiles = await session.scalars(query.order_by(asc(BrowserProfileModel.created_at)))
|
||||
return [BrowserProfile.model_validate(profile) for profile in browser_profiles.all()]
|
||||
except SQLAlchemyError:
|
||||
LOG.error("SQLAlchemyError in list_browser_profiles", exc_info=True)
|
||||
raise
|
||||
|
||||
async def delete_browser_profile(
|
||||
self,
|
||||
profile_id: str,
|
||||
organization_id: str,
|
||||
) -> None:
|
||||
try:
|
||||
async with self.Session() as session:
|
||||
query = (
|
||||
select(BrowserProfileModel)
|
||||
.filter_by(browser_profile_id=profile_id)
|
||||
.filter_by(organization_id=organization_id)
|
||||
.filter(BrowserProfileModel.deleted_at.is_(None))
|
||||
)
|
||||
browser_profile = (await session.scalars(query)).first()
|
||||
if not browser_profile:
|
||||
raise BrowserProfileNotFound(profile_id=profile_id, organization_id=organization_id)
|
||||
browser_profile.deleted_at = datetime.utcnow()
|
||||
await session.commit()
|
||||
except SQLAlchemyError:
|
||||
LOG.error("SQLAlchemyError in delete_browser_profile", exc_info=True)
|
||||
raise
|
||||
|
||||
async def get_active_persistent_browser_sessions(
|
||||
self,
|
||||
organization_id: str,
|
||||
|
||||
@@ -39,6 +39,7 @@ CREDENTIAL_AZURE_VAULT_PARAMETER_PREFIX = "azcp"
|
||||
CREDENTIAL_PARAMETER_PREFIX = "cp"
|
||||
CREDENTIAL_PREFIX = "cred"
|
||||
DEBUG_SESSION_PREFIX = "ds"
|
||||
BROWSER_PROFILE_PREFIX = "bp"
|
||||
ORGANIZATION_BITWARDEN_COLLECTION_PREFIX = "obc"
|
||||
TASK_V2_ID = "tsk_v2"
|
||||
THOUGHT_ID = "ot"
|
||||
@@ -194,6 +195,11 @@ def generate_persistent_browser_session_id() -> str:
|
||||
return f"{PERSISTENT_BROWSER_SESSION_ID}_{int_id}"
|
||||
|
||||
|
||||
def generate_browser_profile_id() -> str:
|
||||
int_id = generate_id()
|
||||
return f"{BROWSER_PROFILE_PREFIX}_{int_id}"
|
||||
|
||||
|
||||
def generate_task_run_id() -> str:
|
||||
int_id = generate_id()
|
||||
return f"{TASK_RUN_PREFIX}_{int_id}"
|
||||
|
||||
@@ -28,6 +28,7 @@ from skyvern.forge.sdk.db.id import (
|
||||
generate_bitwarden_credit_card_data_parameter_id,
|
||||
generate_bitwarden_login_credential_parameter_id,
|
||||
generate_bitwarden_sensitive_information_parameter_id,
|
||||
generate_browser_profile_id,
|
||||
generate_credential_id,
|
||||
generate_credential_parameter_id,
|
||||
generate_debug_session_id,
|
||||
@@ -784,6 +785,23 @@ class PersistentBrowserSessionModel(Base):
|
||||
deleted_at = Column(DateTime, nullable=True)
|
||||
|
||||
|
||||
class BrowserProfileModel(Base):
|
||||
__tablename__ = "browser_profiles"
|
||||
__table_args__ = (
|
||||
Index("idx_browser_profiles_org", "organization_id"),
|
||||
Index("idx_browser_profiles_org_name", "organization_id", "name"),
|
||||
UniqueConstraint("organization_id", "name", name="uc_org_browser_profile_name"),
|
||||
)
|
||||
|
||||
browser_profile_id = Column(String, primary_key=True, default=generate_browser_profile_id)
|
||||
organization_id = Column(String, nullable=False)
|
||||
name = Column(String, nullable=False)
|
||||
description = Column(String, nullable=True)
|
||||
created_at = Column(DateTime, default=datetime.datetime.utcnow, nullable=False)
|
||||
modified_at = Column(DateTime, default=datetime.datetime.utcnow, onupdate=datetime.datetime.utcnow, nullable=False)
|
||||
deleted_at = Column(DateTime, nullable=True)
|
||||
|
||||
|
||||
class TaskRunModel(Base):
|
||||
__tablename__ = "task_runs"
|
||||
__table_args__ = (
|
||||
|
||||
Reference in New Issue
Block a user