Rename old router to legacy_base_router (#2048)
Co-authored-by: Suchintan Singh <suchintansingh@gmail.com>
This commit is contained in:
@@ -46,7 +46,7 @@ class RunTask(SkyvernTaskBaseTool):
|
|||||||
if url is not None:
|
if url is not None:
|
||||||
task_request.url = url
|
task_request.url = url
|
||||||
|
|
||||||
return await self.agent.run_task_v1(task_request=task_request, timeout_seconds=self.run_task_timeout_seconds)
|
return await self.agent.run_task(task_request=task_request, timeout_seconds=self.run_task_timeout_seconds)
|
||||||
|
|
||||||
async def _arun_task_v2(self, user_prompt: str, url: str | None = None) -> TaskV2:
|
async def _arun_task_v2(self, user_prompt: str, url: str | None = None) -> TaskV2:
|
||||||
task_request = TaskV2Request(user_prompt=user_prompt, url=url)
|
task_request = TaskV2Request(user_prompt=user_prompt, url=url)
|
||||||
@@ -72,7 +72,7 @@ class DispatchTask(SkyvernTaskBaseTool):
|
|||||||
if url is not None:
|
if url is not None:
|
||||||
task_request.url = url
|
task_request.url = url
|
||||||
|
|
||||||
return await self.agent.create_task_v1(task_request=task_request)
|
return await self.agent.create_task(task_request=task_request)
|
||||||
|
|
||||||
async def _arun_task_v2(self, user_prompt: str, url: str | None = None) -> TaskV2:
|
async def _arun_task_v2(self, user_prompt: str, url: str | None = None) -> TaskV2:
|
||||||
task_request = TaskV2Request(user_prompt=user_prompt, url=url)
|
task_request = TaskV2Request(user_prompt=user_prompt, url=url)
|
||||||
|
|||||||
@@ -104,7 +104,7 @@ class SkyvernTaskToolSpec(BaseToolSpec):
|
|||||||
if url is not None:
|
if url is not None:
|
||||||
task_request.url = url
|
task_request.url = url
|
||||||
|
|
||||||
return await self.agent.run_task_v1(task_request=task_request, timeout_seconds=self.run_task_timeout_seconds)
|
return await self.agent.run_task(task_request=task_request, timeout_seconds=self.run_task_timeout_seconds)
|
||||||
|
|
||||||
async def dispatch_task_v1(self, user_prompt: str, url: Optional[str] = None) -> CreateTaskResponse:
|
async def dispatch_task_v1(self, user_prompt: str, url: Optional[str] = None) -> CreateTaskResponse:
|
||||||
task_generation = await self._generate_v1_task_request(user_prompt=user_prompt)
|
task_generation = await self._generate_v1_task_request(user_prompt=user_prompt)
|
||||||
@@ -112,7 +112,7 @@ class SkyvernTaskToolSpec(BaseToolSpec):
|
|||||||
if url is not None:
|
if url is not None:
|
||||||
task_request.url = url
|
task_request.url = url
|
||||||
|
|
||||||
return await self.agent.create_task_v1(task_request=task_request)
|
return await self.agent.create_task(task_request=task_request)
|
||||||
|
|
||||||
async def get_task_v1(self, task_id: str) -> TaskResponse | None:
|
async def get_task_v1(self, task_id: str) -> TaskResponse | None:
|
||||||
return await self.agent.get_task(task_id=task_id)
|
return await self.agent.get_task(task_id=task_id)
|
||||||
|
|||||||
@@ -1,16 +1,9 @@
|
|||||||
import asyncio
|
import asyncio
|
||||||
import os
|
|
||||||
import subprocess
|
|
||||||
from typing import Any, cast
|
|
||||||
|
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
|
|
||||||
from skyvern.agent.client import SkyvernClient
|
|
||||||
from skyvern.agent.constants import DEFAULT_AGENT_HEARTBEAT_INTERVAL, DEFAULT_AGENT_TIMEOUT
|
|
||||||
from skyvern.config import settings
|
|
||||||
from skyvern.forge import app
|
from skyvern.forge import app
|
||||||
from skyvern.forge.sdk.core import security, skyvern_context
|
from skyvern.forge.sdk.core import security, skyvern_context
|
||||||
from skyvern.forge.sdk.core.hashing import generate_url_hash
|
|
||||||
from skyvern.forge.sdk.core.skyvern_context import SkyvernContext
|
from skyvern.forge.sdk.core.skyvern_context import SkyvernContext
|
||||||
from skyvern.forge.sdk.db.enums import OrganizationAuthTokenType
|
from skyvern.forge.sdk.db.enums import OrganizationAuthTokenType
|
||||||
from skyvern.forge.sdk.schemas.organizations import Organization
|
from skyvern.forge.sdk.schemas.organizations import Organization
|
||||||
@@ -18,58 +11,14 @@ from skyvern.forge.sdk.schemas.task_v2 import TaskV2, TaskV2Request, TaskV2Statu
|
|||||||
from skyvern.forge.sdk.schemas.tasks import CreateTaskResponse, Task, TaskRequest, TaskResponse, TaskStatus
|
from skyvern.forge.sdk.schemas.tasks import CreateTaskResponse, Task, TaskRequest, TaskResponse, TaskStatus
|
||||||
from skyvern.forge.sdk.services.org_auth_token_service import API_KEY_LIFETIME
|
from skyvern.forge.sdk.services.org_auth_token_service import API_KEY_LIFETIME
|
||||||
from skyvern.forge.sdk.workflow.models.workflow import WorkflowRunStatus
|
from skyvern.forge.sdk.workflow.models.workflow import WorkflowRunStatus
|
||||||
from skyvern.schemas.runs import ProxyLocation, RunEngine, RunResponse, RunType, TaskRunResponse
|
from skyvern.services import task_v2_service
|
||||||
from skyvern.services import run_service, task_v1_service, task_v2_service
|
|
||||||
from skyvern.utils import migrate_db
|
from skyvern.utils import migrate_db
|
||||||
|
|
||||||
|
|
||||||
class SkyvernAgent:
|
class SkyvernAgent:
|
||||||
def __init__(
|
def __init__(self) -> None:
|
||||||
self,
|
load_dotenv(".env")
|
||||||
base_url: str | None = None,
|
migrate_db()
|
||||||
api_key: str | None = None,
|
|
||||||
cdp_url: str | None = None,
|
|
||||||
browser_path: str | None = None,
|
|
||||||
browser_type: str | None = None,
|
|
||||||
) -> None:
|
|
||||||
self.skyvern_client: SkyvernClient | None = None
|
|
||||||
if base_url is None and api_key is None:
|
|
||||||
# TODO: run at the root wherever the code is initiated
|
|
||||||
load_dotenv(".env")
|
|
||||||
migrate_db()
|
|
||||||
# TODO: will this change the already imported settings?
|
|
||||||
# TODO: maybe refresh the settings
|
|
||||||
|
|
||||||
self.cdp_url = cdp_url
|
|
||||||
if browser_path:
|
|
||||||
# TODO validate browser_path
|
|
||||||
# Supported Browsers: Google Chrome, Brave Browser, Microsoft Edge, Firefox
|
|
||||||
if "Chrome" in browser_path or "Brave" in browser_path or "Edge" in browser_path:
|
|
||||||
result = subprocess.Popen(
|
|
||||||
["/Applications/Google Chrome.app/Contents/MacOS/Google Chrome", "--remote-debugging-port=9222"]
|
|
||||||
)
|
|
||||||
if result.returncode != 0:
|
|
||||||
raise Exception(f"Failed to open browser. browser_path: {browser_path}")
|
|
||||||
|
|
||||||
self.cdp_url = "http://127.0.0.1:9222"
|
|
||||||
settings.BROWSER_TYPE = "cdp-connect"
|
|
||||||
settings.BROWSER_REMOTE_DEBUGGING_URL = self.cdp_url
|
|
||||||
else:
|
|
||||||
raise ValueError(
|
|
||||||
f"Unsupported browser or invalid path: {browser_path}. "
|
|
||||||
"Here's a list of supported browsers Skyvern can connect to: Google Chrome, Brave Browser, Microsoft Edge, Firefox."
|
|
||||||
)
|
|
||||||
elif base_url is None and api_key is None:
|
|
||||||
if not browser_type:
|
|
||||||
if "BROWSER_TYPE" not in os.environ:
|
|
||||||
raise Exception("browser type is missing")
|
|
||||||
browser_type = os.environ["BROWSER_TYPE"]
|
|
||||||
|
|
||||||
settings.BROWSER_TYPE = browser_type
|
|
||||||
elif base_url and api_key:
|
|
||||||
self.client = SkyvernClient(base_url=base_url, api_key=api_key)
|
|
||||||
else:
|
|
||||||
raise ValueError("base_url and api_key must be both provided")
|
|
||||||
|
|
||||||
async def _get_organization(self) -> Organization:
|
async def _get_organization(self) -> Organization:
|
||||||
organization = await app.DATABASE.get_organization_by_domain("skyvern.local")
|
organization = await app.DATABASE.get_organization_by_domain("skyvern.local")
|
||||||
@@ -92,7 +41,7 @@ class SkyvernAgent:
|
|||||||
)
|
)
|
||||||
return organization
|
return organization
|
||||||
|
|
||||||
async def _run_task(self, organization: Organization, task: Task, max_steps: int | None = None) -> None:
|
async def _run_task(self, organization: Organization, task: Task) -> None:
|
||||||
org_auth_token = await app.DATABASE.get_valid_org_auth_token(
|
org_auth_token = await app.DATABASE.get_valid_org_auth_token(
|
||||||
organization_id=organization.organization_id,
|
organization_id=organization.organization_id,
|
||||||
token_type=OrganizationAuthTokenType.api,
|
token_type=OrganizationAuthTokenType.api,
|
||||||
@@ -109,23 +58,13 @@ class SkyvernAgent:
|
|||||||
status=TaskStatus.running,
|
status=TaskStatus.running,
|
||||||
organization_id=organization.organization_id,
|
organization_id=organization.organization_id,
|
||||||
)
|
)
|
||||||
try:
|
|
||||||
skyvern_context.set(
|
|
||||||
SkyvernContext(
|
|
||||||
organization_id=organization.organization_id,
|
|
||||||
task_id=task.task_id,
|
|
||||||
max_steps_override=max_steps,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
step, _, _ = await app.agent.execute_step(
|
step, _, _ = await app.agent.execute_step(
|
||||||
organization=organization,
|
organization=organization,
|
||||||
task=updated_task,
|
task=updated_task,
|
||||||
step=step,
|
step=step,
|
||||||
api_key=org_auth_token.token if org_auth_token else None,
|
api_key=org_auth_token.token if org_auth_token else None,
|
||||||
)
|
)
|
||||||
finally:
|
|
||||||
skyvern_context.reset()
|
|
||||||
|
|
||||||
async def _run_task_v2(self, organization: Organization, task_v2: TaskV2) -> None:
|
async def _run_task_v2(self, organization: Organization, task_v2: TaskV2) -> None:
|
||||||
# mark task v2 as queued
|
# mark task v2 as queued
|
||||||
@@ -146,15 +85,22 @@ class SkyvernAgent:
|
|||||||
task_v2_id=task_v2.observer_cruise_id,
|
task_v2_id=task_v2.observer_cruise_id,
|
||||||
)
|
)
|
||||||
|
|
||||||
async def create_task_v1(
|
async def create_task(
|
||||||
self,
|
self,
|
||||||
task_request: TaskRequest,
|
task_request: TaskRequest,
|
||||||
) -> CreateTaskResponse:
|
) -> CreateTaskResponse:
|
||||||
organization = await self._get_organization()
|
organization = await self._get_organization()
|
||||||
|
|
||||||
created_task = await app.agent.create_task(task_request, organization.organization_id)
|
created_task = await app.agent.create_task(task_request, organization.organization_id)
|
||||||
|
skyvern_context.set(
|
||||||
|
SkyvernContext(
|
||||||
|
organization_id=organization.organization_id,
|
||||||
|
task_id=created_task.task_id,
|
||||||
|
max_steps_override=created_task.max_steps_per_run,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
asyncio.create_task(self._run_task(organization, created_task, max_steps=task_request.max_steps_per_run))
|
asyncio.create_task(self._run_task(organization, created_task))
|
||||||
return CreateTaskResponse(task_id=created_task.task_id)
|
return CreateTaskResponse(task_id=created_task.task_id)
|
||||||
|
|
||||||
async def get_task(
|
async def get_task(
|
||||||
@@ -192,12 +138,12 @@ class SkyvernAgent:
|
|||||||
task=task, last_step=latest_step, failure_reason=failure_reason, need_browser_log=True
|
task=task, last_step=latest_step, failure_reason=failure_reason, need_browser_log=True
|
||||||
)
|
)
|
||||||
|
|
||||||
async def run_task_v1(
|
async def run_task(
|
||||||
self,
|
self,
|
||||||
task_request: TaskRequest,
|
task_request: TaskRequest,
|
||||||
timeout_seconds: int = 600,
|
timeout_seconds: int = 600,
|
||||||
) -> TaskResponse:
|
) -> TaskResponse:
|
||||||
created_task = await self.create_task_v1(task_request)
|
created_task = await self.create_task(task_request)
|
||||||
|
|
||||||
async with asyncio.timeout(timeout_seconds):
|
async with asyncio.timeout(timeout_seconds):
|
||||||
while True:
|
while True:
|
||||||
@@ -241,148 +187,3 @@ class SkyvernAgent:
|
|||||||
if refreshed_task_v2.status.is_final():
|
if refreshed_task_v2.status.is_final():
|
||||||
return refreshed_task_v2
|
return refreshed_task_v2
|
||||||
await asyncio.sleep(1)
|
await asyncio.sleep(1)
|
||||||
|
|
||||||
############### officially supported interfaces ###############
|
|
||||||
async def get_run(self, run_id: str) -> RunResponse | None:
|
|
||||||
if not self.client:
|
|
||||||
organization = await self._get_organization()
|
|
||||||
return await run_service.get_run_response(run_id, organization_id=organization.organization_id)
|
|
||||||
|
|
||||||
return await self.client.get_run(run_id)
|
|
||||||
|
|
||||||
async def run_task(
|
|
||||||
self,
|
|
||||||
prompt: str,
|
|
||||||
engine: RunEngine = RunEngine.skyvern_v1,
|
|
||||||
url: str | None = None,
|
|
||||||
webhook_url: str | None = None,
|
|
||||||
totp_identifier: str | None = None,
|
|
||||||
totp_url: str | None = None,
|
|
||||||
title: str | None = None,
|
|
||||||
error_code_mapping: dict[str, str] | None = None,
|
|
||||||
data_extraction_schema: dict[str, Any] | None = None,
|
|
||||||
proxy_location: ProxyLocation | None = None,
|
|
||||||
max_steps: int | None = None,
|
|
||||||
wait_for_completion: bool = True,
|
|
||||||
timeout: float = DEFAULT_AGENT_TIMEOUT,
|
|
||||||
browser_session_id: str | None = None,
|
|
||||||
) -> TaskRunResponse:
|
|
||||||
if not self.client:
|
|
||||||
if engine == RunEngine.skyvern_v1:
|
|
||||||
data_extraction_goal = None
|
|
||||||
data_extraction_schema = data_extraction_schema
|
|
||||||
navigation_goal = prompt
|
|
||||||
navigation_payload = None
|
|
||||||
organization = await self._get_organization()
|
|
||||||
if not url:
|
|
||||||
task_generation = await task_v1_service.generate_task(
|
|
||||||
user_prompt=prompt,
|
|
||||||
organization=organization,
|
|
||||||
)
|
|
||||||
url = task_generation.url
|
|
||||||
navigation_goal = task_generation.navigation_goal or prompt
|
|
||||||
navigation_payload = task_generation.navigation_payload
|
|
||||||
data_extraction_goal = task_generation.data_extraction_goal
|
|
||||||
data_extraction_schema = data_extraction_schema or task_generation.extracted_information_schema
|
|
||||||
|
|
||||||
task_request = TaskRequest(
|
|
||||||
title=title,
|
|
||||||
url=url,
|
|
||||||
navigation_goal=navigation_goal,
|
|
||||||
navigation_payload=navigation_payload,
|
|
||||||
data_extraction_goal=data_extraction_goal,
|
|
||||||
extracted_information_schema=data_extraction_schema,
|
|
||||||
error_code_mapping=error_code_mapping,
|
|
||||||
proxy_location=proxy_location,
|
|
||||||
)
|
|
||||||
|
|
||||||
if wait_for_completion:
|
|
||||||
created_task = await app.agent.create_task(task_request, organization.organization_id)
|
|
||||||
url_hash = generate_url_hash(task_request.url)
|
|
||||||
await app.DATABASE.create_task_run(
|
|
||||||
task_run_type=RunType.task_v1,
|
|
||||||
organization_id=organization.organization_id,
|
|
||||||
run_id=created_task.task_id,
|
|
||||||
title=task_request.title,
|
|
||||||
url=task_request.url,
|
|
||||||
url_hash=url_hash,
|
|
||||||
)
|
|
||||||
try:
|
|
||||||
await self._run_task(organization, created_task)
|
|
||||||
run_obj = await self.get_run(run_id=created_task.task_id)
|
|
||||||
return cast(TaskRunResponse, run_obj)
|
|
||||||
except Exception:
|
|
||||||
# TODO: better error handling and logging
|
|
||||||
run_obj = await self.get_run(run_id=created_task.task_id)
|
|
||||||
return cast(TaskRunResponse, run_obj)
|
|
||||||
else:
|
|
||||||
create_task_resp = await self.create_task_v1(task_request)
|
|
||||||
run_obj = await self.get_run(run_id=create_task_resp.task_id)
|
|
||||||
return cast(TaskRunResponse, run_obj)
|
|
||||||
elif engine == RunEngine.skyvern_v2:
|
|
||||||
# initialize task v2
|
|
||||||
organization = await self._get_organization()
|
|
||||||
|
|
||||||
task_v2 = await task_v2_service.initialize_task_v2(
|
|
||||||
organization=organization,
|
|
||||||
user_prompt=prompt,
|
|
||||||
user_url=url,
|
|
||||||
totp_identifier=totp_identifier,
|
|
||||||
totp_verification_url=totp_url,
|
|
||||||
webhook_callback_url=webhook_url,
|
|
||||||
proxy_location=proxy_location,
|
|
||||||
publish_workflow=False,
|
|
||||||
extracted_information_schema=data_extraction_schema,
|
|
||||||
error_code_mapping=error_code_mapping,
|
|
||||||
create_task_run=True,
|
|
||||||
)
|
|
||||||
|
|
||||||
if wait_for_completion:
|
|
||||||
await self._run_task_v2(organization, task_v2)
|
|
||||||
run_obj = await self.get_run(run_id=task_v2.observer_cruise_id)
|
|
||||||
return cast(TaskRunResponse, run_obj)
|
|
||||||
else:
|
|
||||||
asyncio.create_task(self._run_task_v2(organization, task_v2))
|
|
||||||
run_obj = await self.get_run(run_id=task_v2.observer_cruise_id)
|
|
||||||
return cast(TaskRunResponse, run_obj)
|
|
||||||
else:
|
|
||||||
raise ValueError("Local mode is not supported for this method")
|
|
||||||
|
|
||||||
task_run = await self.client.run_task(
|
|
||||||
prompt=prompt,
|
|
||||||
engine=engine,
|
|
||||||
url=url,
|
|
||||||
webhook_url=webhook_url,
|
|
||||||
totp_identifier=totp_identifier,
|
|
||||||
totp_url=totp_url,
|
|
||||||
title=title,
|
|
||||||
error_code_mapping=error_code_mapping,
|
|
||||||
proxy_location=proxy_location,
|
|
||||||
max_steps=max_steps,
|
|
||||||
)
|
|
||||||
|
|
||||||
if wait_for_completion:
|
|
||||||
async with asyncio.timeout(timeout):
|
|
||||||
while True:
|
|
||||||
task_run = await self.client.get_run(task_run.run_id)
|
|
||||||
if task_run.status.is_final():
|
|
||||||
return task_run
|
|
||||||
await asyncio.sleep(DEFAULT_AGENT_HEARTBEAT_INTERVAL)
|
|
||||||
return task_run
|
|
||||||
|
|
||||||
async def run_workflow(
|
|
||||||
self,
|
|
||||||
workflow_id: str,
|
|
||||||
parameters: dict[str, Any],
|
|
||||||
webhook_url: str | None = None,
|
|
||||||
totp_identifier: str | None = None,
|
|
||||||
totp_url: str | None = None,
|
|
||||||
title: str | None = None,
|
|
||||||
error_code_mapping: dict[str, str] | None = None,
|
|
||||||
proxy_location: ProxyLocation | None = None,
|
|
||||||
max_steps: int | None = None,
|
|
||||||
wait_for_completion: bool = True,
|
|
||||||
timeout: float = DEFAULT_AGENT_TIMEOUT,
|
|
||||||
browser_session_id: str | None = None,
|
|
||||||
) -> None:
|
|
||||||
raise NotImplementedError("Running workflows is currently not supported with skyvern SDK.")
|
|
||||||
|
|||||||
@@ -1,2 +0,0 @@
|
|||||||
DEFAULT_AGENT_TIMEOUT = 1800 # 30 minutes
|
|
||||||
DEFAULT_AGENT_HEARTBEAT_INTERVAL = 10 # 10 seconds
|
|
||||||
@@ -18,9 +18,7 @@ from skyvern.forge import app as forge_app
|
|||||||
from skyvern.forge.sdk.core import skyvern_context
|
from skyvern.forge.sdk.core import skyvern_context
|
||||||
from skyvern.forge.sdk.core.skyvern_context import SkyvernContext
|
from skyvern.forge.sdk.core.skyvern_context import SkyvernContext
|
||||||
from skyvern.forge.sdk.db.exceptions import NotFoundError
|
from skyvern.forge.sdk.db.exceptions import NotFoundError
|
||||||
from skyvern.forge.sdk.routes.agent_protocol import base_router, official_api_router, v2_router
|
from skyvern.forge.sdk.routes.routers import base_router, legacy_base_router, legacy_v2_router
|
||||||
from skyvern.forge.sdk.routes.streaming import websocket_router
|
|
||||||
from skyvern.forge.sdk.routes.totp import totp_router
|
|
||||||
|
|
||||||
LOG = structlog.get_logger()
|
LOG = structlog.get_logger()
|
||||||
|
|
||||||
@@ -66,11 +64,9 @@ def get_agent_app() -> FastAPI:
|
|||||||
allow_headers=["*"],
|
allow_headers=["*"],
|
||||||
)
|
)
|
||||||
|
|
||||||
app.include_router(official_api_router, prefix="/v1")
|
app.include_router(base_router, prefix="/v1")
|
||||||
app.include_router(base_router, prefix="/api/v1")
|
app.include_router(legacy_base_router, prefix="/api/v1")
|
||||||
app.include_router(v2_router, prefix="/api/v2")
|
app.include_router(legacy_v2_router, prefix="/api/v2")
|
||||||
app.include_router(websocket_router, prefix="/api/v1/stream")
|
|
||||||
app.include_router(totp_router, prefix="/api/v1/totp")
|
|
||||||
app.openapi = custom_openapi
|
app.openapi = custom_openapi
|
||||||
|
|
||||||
app.add_middleware(
|
app.add_middleware(
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
from skyvern.forge.sdk.routes import agent_protocol # noqa: F401
|
||||||
|
from skyvern.forge.sdk.routes import browser_sessions # noqa: F401
|
||||||
|
from skyvern.forge.sdk.routes import streaming # noqa: F401
|
||||||
|
from skyvern.forge.sdk.routes import totp # noqa: F401
|
||||||
|
|||||||
@@ -6,18 +6,7 @@ from typing import Annotated, Any
|
|||||||
|
|
||||||
import structlog
|
import structlog
|
||||||
import yaml
|
import yaml
|
||||||
from fastapi import (
|
from fastapi import BackgroundTasks, Depends, Header, HTTPException, Query, Request, Response, UploadFile, status
|
||||||
APIRouter,
|
|
||||||
BackgroundTasks,
|
|
||||||
Depends,
|
|
||||||
Header,
|
|
||||||
HTTPException,
|
|
||||||
Query,
|
|
||||||
Request,
|
|
||||||
Response,
|
|
||||||
UploadFile,
|
|
||||||
status,
|
|
||||||
)
|
|
||||||
from fastapi.responses import ORJSONResponse
|
from fastapi.responses import ORJSONResponse
|
||||||
|
|
||||||
from skyvern import analytics
|
from skyvern import analytics
|
||||||
@@ -33,6 +22,7 @@ from skyvern.forge.sdk.core.security import generate_skyvern_signature
|
|||||||
from skyvern.forge.sdk.db.enums import OrganizationAuthTokenType
|
from skyvern.forge.sdk.db.enums import OrganizationAuthTokenType
|
||||||
from skyvern.forge.sdk.executor.factory import AsyncExecutorFactory
|
from skyvern.forge.sdk.executor.factory import AsyncExecutorFactory
|
||||||
from skyvern.forge.sdk.models import Step
|
from skyvern.forge.sdk.models import Step
|
||||||
|
from skyvern.forge.sdk.routes.routers import base_router, legacy_base_router, legacy_v2_router
|
||||||
from skyvern.forge.sdk.schemas.ai_suggestions import AISuggestionBase, AISuggestionRequest
|
from skyvern.forge.sdk.schemas.ai_suggestions import AISuggestionBase, AISuggestionRequest
|
||||||
from skyvern.forge.sdk.schemas.organizations import (
|
from skyvern.forge.sdk.schemas.organizations import (
|
||||||
GetOrganizationAPIKeysResponse,
|
GetOrganizationAPIKeysResponse,
|
||||||
@@ -73,11 +63,6 @@ from skyvern.forge.sdk.workflow.models.yaml import WorkflowCreateYAMLRequest
|
|||||||
from skyvern.schemas.runs import RunEngine, RunResponse, RunType, TaskRunRequest, TaskRunResponse
|
from skyvern.schemas.runs import RunEngine, RunResponse, RunType, TaskRunRequest, TaskRunResponse
|
||||||
from skyvern.services import run_service, task_v1_service, task_v2_service
|
from skyvern.services import run_service, task_v1_service, task_v2_service
|
||||||
from skyvern.webeye.actions.actions import Action
|
from skyvern.webeye.actions.actions import Action
|
||||||
from skyvern.webeye.schemas import BrowserSessionResponse
|
|
||||||
|
|
||||||
official_api_router = APIRouter()
|
|
||||||
base_router = APIRouter()
|
|
||||||
v2_router = APIRouter()
|
|
||||||
|
|
||||||
LOG = structlog.get_logger()
|
LOG = structlog.get_logger()
|
||||||
|
|
||||||
@@ -103,15 +88,16 @@ class AISuggestionType(str, Enum):
|
|||||||
DATA_SCHEMA = "data_schema"
|
DATA_SCHEMA = "data_schema"
|
||||||
|
|
||||||
|
|
||||||
@base_router.post(
|
@legacy_base_router.post(
|
||||||
"/webhook",
|
"/webhook",
|
||||||
tags=["server"],
|
tags=["server"],
|
||||||
openapi_extra={
|
openapi_extra={
|
||||||
"x-fern-sdk-group-name": "server",
|
"x-fern-sdk-group-name": "server",
|
||||||
"x-fern-sdk-method-name": "webhook",
|
"x-fern-sdk-method-name": "webhook",
|
||||||
},
|
},
|
||||||
|
include_in_schema=False,
|
||||||
)
|
)
|
||||||
@base_router.post("/webhook/", include_in_schema=False)
|
@legacy_base_router.post("/webhook/", include_in_schema=False)
|
||||||
async def webhook(
|
async def webhook(
|
||||||
request: Request,
|
request: Request,
|
||||||
x_skyvern_signature: Annotated[str | None, Header()] = None,
|
x_skyvern_signature: Annotated[str | None, Header()] = None,
|
||||||
@@ -148,7 +134,7 @@ async def webhook(
|
|||||||
return Response(content="webhook validation", status_code=200)
|
return Response(content="webhook validation", status_code=200)
|
||||||
|
|
||||||
|
|
||||||
@base_router.get(
|
@legacy_base_router.get(
|
||||||
"/heartbeat",
|
"/heartbeat",
|
||||||
tags=["server"],
|
tags=["server"],
|
||||||
openapi_extra={
|
openapi_extra={
|
||||||
@@ -156,7 +142,7 @@ async def webhook(
|
|||||||
"x-fern-sdk-method-name": "heartbeat",
|
"x-fern-sdk-method-name": "heartbeat",
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@base_router.get("/heartbeat/", include_in_schema=False)
|
@legacy_base_router.get("/heartbeat/", include_in_schema=False)
|
||||||
async def heartbeat() -> Response:
|
async def heartbeat() -> Response:
|
||||||
"""
|
"""
|
||||||
Check if the server is running.
|
Check if the server is running.
|
||||||
@@ -164,7 +150,7 @@ async def heartbeat() -> Response:
|
|||||||
return Response(content="Server is running.", status_code=200)
|
return Response(content="Server is running.", status_code=200)
|
||||||
|
|
||||||
|
|
||||||
@base_router.post(
|
@legacy_base_router.post(
|
||||||
"/tasks",
|
"/tasks",
|
||||||
tags=["agent"],
|
tags=["agent"],
|
||||||
response_model=CreateTaskResponse,
|
response_model=CreateTaskResponse,
|
||||||
@@ -173,7 +159,7 @@ async def heartbeat() -> Response:
|
|||||||
"x-fern-sdk-method-name": "run_task_v1",
|
"x-fern-sdk-method-name": "run_task_v1",
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@base_router.post(
|
@legacy_base_router.post(
|
||||||
"/tasks/",
|
"/tasks/",
|
||||||
response_model=CreateTaskResponse,
|
response_model=CreateTaskResponse,
|
||||||
include_in_schema=False,
|
include_in_schema=False,
|
||||||
@@ -200,7 +186,7 @@ async def run_task_v1(
|
|||||||
return CreateTaskResponse(task_id=created_task.task_id)
|
return CreateTaskResponse(task_id=created_task.task_id)
|
||||||
|
|
||||||
|
|
||||||
@base_router.get(
|
@legacy_base_router.get(
|
||||||
"/tasks/{task_id}",
|
"/tasks/{task_id}",
|
||||||
tags=["agent"],
|
tags=["agent"],
|
||||||
response_model=TaskResponse,
|
response_model=TaskResponse,
|
||||||
@@ -209,7 +195,7 @@ async def run_task_v1(
|
|||||||
"x-fern-sdk-method-name": "get_task_v1",
|
"x-fern-sdk-method-name": "get_task_v1",
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@base_router.get("/tasks/{task_id}/", response_model=TaskResponse, include_in_schema=False)
|
@legacy_base_router.get("/tasks/{task_id}/", response_model=TaskResponse, include_in_schema=False)
|
||||||
async def get_task_v1(
|
async def get_task_v1(
|
||||||
task_id: str,
|
task_id: str,
|
||||||
current_org: Organization = Depends(org_auth_service.get_current_org),
|
current_org: Organization = Depends(org_auth_service.get_current_org),
|
||||||
@@ -248,7 +234,7 @@ async def get_task_v1(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@base_router.post(
|
@legacy_base_router.post(
|
||||||
"/tasks/{task_id}/cancel",
|
"/tasks/{task_id}/cancel",
|
||||||
tags=["agent"],
|
tags=["agent"],
|
||||||
openapi_extra={
|
openapi_extra={
|
||||||
@@ -256,7 +242,7 @@ async def get_task_v1(
|
|||||||
"x-fern-sdk-method-name": "cancel_task",
|
"x-fern-sdk-method-name": "cancel_task",
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@base_router.post("/tasks/{task_id}/cancel/", include_in_schema=False)
|
@legacy_base_router.post("/tasks/{task_id}/cancel/", include_in_schema=False)
|
||||||
async def cancel_task(
|
async def cancel_task(
|
||||||
task_id: str,
|
task_id: str,
|
||||||
current_org: Organization = Depends(org_auth_service.get_current_org),
|
current_org: Organization = Depends(org_auth_service.get_current_org),
|
||||||
@@ -276,7 +262,7 @@ async def cancel_task(
|
|||||||
await app.agent.execute_task_webhook(task=task, last_step=latest_step, api_key=x_api_key)
|
await app.agent.execute_task_webhook(task=task, last_step=latest_step, api_key=x_api_key)
|
||||||
|
|
||||||
|
|
||||||
@base_router.post(
|
@legacy_base_router.post(
|
||||||
"/workflows/runs/{workflow_run_id}/cancel",
|
"/workflows/runs/{workflow_run_id}/cancel",
|
||||||
tags=["agent"],
|
tags=["agent"],
|
||||||
openapi_extra={
|
openapi_extra={
|
||||||
@@ -284,7 +270,7 @@ async def cancel_task(
|
|||||||
"x-fern-sdk-method-name": "cancel_workflow_run",
|
"x-fern-sdk-method-name": "cancel_workflow_run",
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@base_router.post("/workflows/runs/{workflow_run_id}/cancel/", include_in_schema=False)
|
@legacy_base_router.post("/workflows/runs/{workflow_run_id}/cancel/", include_in_schema=False)
|
||||||
async def cancel_workflow_run(
|
async def cancel_workflow_run(
|
||||||
workflow_run_id: str,
|
workflow_run_id: str,
|
||||||
current_org: Organization = Depends(org_auth_service.get_current_org),
|
current_org: Organization = Depends(org_auth_service.get_current_org),
|
||||||
@@ -316,7 +302,7 @@ async def cancel_workflow_run(
|
|||||||
await app.WORKFLOW_SERVICE.execute_workflow_webhook(workflow_run, api_key=x_api_key)
|
await app.WORKFLOW_SERVICE.execute_workflow_webhook(workflow_run, api_key=x_api_key)
|
||||||
|
|
||||||
|
|
||||||
@base_router.post(
|
@legacy_base_router.post(
|
||||||
"/tasks/{task_id}/retry_webhook",
|
"/tasks/{task_id}/retry_webhook",
|
||||||
tags=["agent"],
|
tags=["agent"],
|
||||||
response_model=TaskResponse,
|
response_model=TaskResponse,
|
||||||
@@ -325,7 +311,7 @@ async def cancel_workflow_run(
|
|||||||
"x-fern-sdk-method-name": "retry_webhook",
|
"x-fern-sdk-method-name": "retry_webhook",
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@base_router.post(
|
@legacy_base_router.post(
|
||||||
"/tasks/{task_id}/retry_webhook/",
|
"/tasks/{task_id}/retry_webhook/",
|
||||||
response_model=TaskResponse,
|
response_model=TaskResponse,
|
||||||
include_in_schema=False,
|
include_in_schema=False,
|
||||||
@@ -354,7 +340,7 @@ async def retry_webhook(
|
|||||||
return await app.agent.build_task_response(task=task_obj, last_step=latest_step)
|
return await app.agent.build_task_response(task=task_obj, last_step=latest_step)
|
||||||
|
|
||||||
|
|
||||||
@base_router.get(
|
@legacy_base_router.get(
|
||||||
"/tasks",
|
"/tasks",
|
||||||
tags=["agent"],
|
tags=["agent"],
|
||||||
response_model=list[Task],
|
response_model=list[Task],
|
||||||
@@ -363,7 +349,7 @@ async def retry_webhook(
|
|||||||
"x-fern-sdk-method-name": "get_tasks",
|
"x-fern-sdk-method-name": "get_tasks",
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@base_router.get(
|
@legacy_base_router.get(
|
||||||
"/tasks/",
|
"/tasks/",
|
||||||
response_model=list[Task],
|
response_model=list[Task],
|
||||||
include_in_schema=False,
|
include_in_schema=False,
|
||||||
@@ -411,7 +397,7 @@ async def get_tasks(
|
|||||||
return ORJSONResponse([(await app.agent.build_task_response(task=task)).model_dump() for task in tasks])
|
return ORJSONResponse([(await app.agent.build_task_response(task=task)).model_dump() for task in tasks])
|
||||||
|
|
||||||
|
|
||||||
@base_router.get(
|
@legacy_base_router.get(
|
||||||
"/runs",
|
"/runs",
|
||||||
tags=["agent"],
|
tags=["agent"],
|
||||||
response_model=list[WorkflowRun | Task],
|
response_model=list[WorkflowRun | Task],
|
||||||
@@ -420,7 +406,7 @@ async def get_tasks(
|
|||||||
"x-fern-sdk-method-name": "get_runs",
|
"x-fern-sdk-method-name": "get_runs",
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@base_router.get(
|
@legacy_base_router.get(
|
||||||
"/runs/",
|
"/runs/",
|
||||||
response_model=list[WorkflowRun | Task],
|
response_model=list[WorkflowRun | Task],
|
||||||
include_in_schema=False,
|
include_in_schema=False,
|
||||||
@@ -441,7 +427,7 @@ async def get_runs(
|
|||||||
return ORJSONResponse([run.model_dump() for run in runs])
|
return ORJSONResponse([run.model_dump() for run in runs])
|
||||||
|
|
||||||
|
|
||||||
@official_api_router.get(
|
@base_router.get(
|
||||||
"/runs/{run_id}",
|
"/runs/{run_id}",
|
||||||
tags=["agent"],
|
tags=["agent"],
|
||||||
response_model=RunResponse,
|
response_model=RunResponse,
|
||||||
@@ -450,7 +436,7 @@ async def get_runs(
|
|||||||
"x-fern-sdk-method-name": "get_run",
|
"x-fern-sdk-method-name": "get_run",
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@official_api_router.get(
|
@base_router.get(
|
||||||
"/runs/{run_id}/",
|
"/runs/{run_id}/",
|
||||||
response_model=RunResponse,
|
response_model=RunResponse,
|
||||||
include_in_schema=False,
|
include_in_schema=False,
|
||||||
@@ -468,7 +454,7 @@ async def get_run(
|
|||||||
return run_response
|
return run_response
|
||||||
|
|
||||||
|
|
||||||
@base_router.get(
|
@legacy_base_router.get(
|
||||||
"/tasks/{task_id}/steps",
|
"/tasks/{task_id}/steps",
|
||||||
tags=["agent"],
|
tags=["agent"],
|
||||||
response_model=list[Step],
|
response_model=list[Step],
|
||||||
@@ -477,7 +463,7 @@ async def get_run(
|
|||||||
"x-fern-sdk-method-name": "get_steps",
|
"x-fern-sdk-method-name": "get_steps",
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@base_router.get(
|
@legacy_base_router.get(
|
||||||
"/tasks/{task_id}/steps/",
|
"/tasks/{task_id}/steps/",
|
||||||
response_model=list[Step],
|
response_model=list[Step],
|
||||||
include_in_schema=False,
|
include_in_schema=False,
|
||||||
@@ -496,7 +482,7 @@ async def get_steps(
|
|||||||
return ORJSONResponse([step.model_dump(exclude_none=True) for step in steps])
|
return ORJSONResponse([step.model_dump(exclude_none=True) for step in steps])
|
||||||
|
|
||||||
|
|
||||||
@base_router.get(
|
@legacy_base_router.get(
|
||||||
"/{entity_type}/{entity_id}/artifacts",
|
"/{entity_type}/{entity_id}/artifacts",
|
||||||
tags=["agent"],
|
tags=["agent"],
|
||||||
response_model=list[Artifact],
|
response_model=list[Artifact],
|
||||||
@@ -505,7 +491,7 @@ async def get_steps(
|
|||||||
"x-fern-sdk-method-name": "get_artifacts",
|
"x-fern-sdk-method-name": "get_artifacts",
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@base_router.get(
|
@legacy_base_router.get(
|
||||||
"/{entity_type}/{entity_id}/artifacts/",
|
"/{entity_type}/{entity_id}/artifacts/",
|
||||||
response_model=list[Artifact],
|
response_model=list[Artifact],
|
||||||
include_in_schema=False,
|
include_in_schema=False,
|
||||||
@@ -560,7 +546,7 @@ async def get_artifacts(
|
|||||||
return ORJSONResponse([artifact.model_dump() for artifact in artifacts])
|
return ORJSONResponse([artifact.model_dump() for artifact in artifacts])
|
||||||
|
|
||||||
|
|
||||||
@base_router.get(
|
@legacy_base_router.get(
|
||||||
"/tasks/{task_id}/steps/{step_id}/artifacts",
|
"/tasks/{task_id}/steps/{step_id}/artifacts",
|
||||||
tags=["agent"],
|
tags=["agent"],
|
||||||
response_model=list[Artifact],
|
response_model=list[Artifact],
|
||||||
@@ -569,7 +555,7 @@ async def get_artifacts(
|
|||||||
"x-fern-sdk-method-name": "get_step_artifacts",
|
"x-fern-sdk-method-name": "get_step_artifacts",
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@base_router.get(
|
@legacy_base_router.get(
|
||||||
"/tasks/{task_id}/steps/{step_id}/artifacts/",
|
"/tasks/{task_id}/steps/{step_id}/artifacts/",
|
||||||
response_model=list[Artifact],
|
response_model=list[Artifact],
|
||||||
include_in_schema=False,
|
include_in_schema=False,
|
||||||
@@ -605,7 +591,7 @@ async def get_step_artifacts(
|
|||||||
return ORJSONResponse([artifact.model_dump() for artifact in artifacts])
|
return ORJSONResponse([artifact.model_dump() for artifact in artifacts])
|
||||||
|
|
||||||
|
|
||||||
@base_router.get(
|
@legacy_base_router.get(
|
||||||
"/tasks/{task_id}/actions",
|
"/tasks/{task_id}/actions",
|
||||||
response_model=list[Action],
|
response_model=list[Action],
|
||||||
tags=["agent"],
|
tags=["agent"],
|
||||||
@@ -614,7 +600,7 @@ async def get_step_artifacts(
|
|||||||
"x-fern-sdk-method-name": "get_actions",
|
"x-fern-sdk-method-name": "get_actions",
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@base_router.get(
|
@legacy_base_router.get(
|
||||||
"/tasks/{task_id}/actions/",
|
"/tasks/{task_id}/actions/",
|
||||||
response_model=list[Action],
|
response_model=list[Action],
|
||||||
include_in_schema=False,
|
include_in_schema=False,
|
||||||
@@ -628,7 +614,7 @@ async def get_actions(
|
|||||||
return actions
|
return actions
|
||||||
|
|
||||||
|
|
||||||
@base_router.post(
|
@legacy_base_router.post(
|
||||||
"/workflows/{workflow_id}/run",
|
"/workflows/{workflow_id}/run",
|
||||||
response_model=RunWorkflowResponse,
|
response_model=RunWorkflowResponse,
|
||||||
tags=["agent"],
|
tags=["agent"],
|
||||||
@@ -637,7 +623,7 @@ async def get_actions(
|
|||||||
"x-fern-sdk-method-name": "run_workflow",
|
"x-fern-sdk-method-name": "run_workflow",
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@base_router.post(
|
@legacy_base_router.post(
|
||||||
"/workflows/{workflow_id}/run/",
|
"/workflows/{workflow_id}/run/",
|
||||||
response_model=RunWorkflowResponse,
|
response_model=RunWorkflowResponse,
|
||||||
include_in_schema=False,
|
include_in_schema=False,
|
||||||
@@ -703,7 +689,7 @@ async def run_workflow(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@base_router.get(
|
@legacy_base_router.get(
|
||||||
"/workflows/runs",
|
"/workflows/runs",
|
||||||
response_model=list[WorkflowRun],
|
response_model=list[WorkflowRun],
|
||||||
tags=["agent"],
|
tags=["agent"],
|
||||||
@@ -712,7 +698,7 @@ async def run_workflow(
|
|||||||
"x-fern-sdk-method-name": "get_workflow_runs",
|
"x-fern-sdk-method-name": "get_workflow_runs",
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@base_router.get(
|
@legacy_base_router.get(
|
||||||
"/workflows/runs/",
|
"/workflows/runs/",
|
||||||
response_model=list[WorkflowRun],
|
response_model=list[WorkflowRun],
|
||||||
include_in_schema=False,
|
include_in_schema=False,
|
||||||
@@ -732,7 +718,7 @@ async def get_workflow_runs(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@base_router.get(
|
@legacy_base_router.get(
|
||||||
"/workflows/{workflow_id}/runs",
|
"/workflows/{workflow_id}/runs",
|
||||||
response_model=list[WorkflowRun],
|
response_model=list[WorkflowRun],
|
||||||
tags=["agent"],
|
tags=["agent"],
|
||||||
@@ -741,7 +727,7 @@ async def get_workflow_runs(
|
|||||||
"x-fern-sdk-method-name": "get_workflow_runs_by_id",
|
"x-fern-sdk-method-name": "get_workflow_runs_by_id",
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@base_router.get(
|
@legacy_base_router.get(
|
||||||
"/workflows/{workflow_id}/runs/",
|
"/workflows/{workflow_id}/runs/",
|
||||||
response_model=list[WorkflowRun],
|
response_model=list[WorkflowRun],
|
||||||
include_in_schema=False,
|
include_in_schema=False,
|
||||||
@@ -763,7 +749,7 @@ async def get_workflow_runs_by_id(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@base_router.get(
|
@legacy_base_router.get(
|
||||||
"/workflows/{workflow_id}/runs/{workflow_run_id}",
|
"/workflows/{workflow_id}/runs/{workflow_run_id}",
|
||||||
tags=["agent"],
|
tags=["agent"],
|
||||||
openapi_extra={
|
openapi_extra={
|
||||||
@@ -771,7 +757,7 @@ async def get_workflow_runs_by_id(
|
|||||||
"x-fern-sdk-method-name": "get_workflow_run_with_workflow_id",
|
"x-fern-sdk-method-name": "get_workflow_run_with_workflow_id",
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@base_router.get(
|
@legacy_base_router.get(
|
||||||
"/workflows/{workflow_id}/runs/{workflow_run_id}/",
|
"/workflows/{workflow_id}/runs/{workflow_run_id}/",
|
||||||
include_in_schema=False,
|
include_in_schema=False,
|
||||||
)
|
)
|
||||||
@@ -797,7 +783,7 @@ async def get_workflow_run_with_workflow_id(
|
|||||||
return return_dict
|
return return_dict
|
||||||
|
|
||||||
|
|
||||||
@base_router.get(
|
@legacy_base_router.get(
|
||||||
"/workflows/{workflow_id}/runs/{workflow_run_id}/timeline",
|
"/workflows/{workflow_id}/runs/{workflow_run_id}/timeline",
|
||||||
tags=["agent"],
|
tags=["agent"],
|
||||||
openapi_extra={
|
openapi_extra={
|
||||||
@@ -805,7 +791,7 @@ async def get_workflow_run_with_workflow_id(
|
|||||||
"x-fern-sdk-method-name": "get_workflow_run_timeline",
|
"x-fern-sdk-method-name": "get_workflow_run_timeline",
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@base_router.get(
|
@legacy_base_router.get(
|
||||||
"/workflows/{workflow_id}/runs/{workflow_run_id}/timeline/",
|
"/workflows/{workflow_id}/runs/{workflow_run_id}/timeline/",
|
||||||
include_in_schema=False,
|
include_in_schema=False,
|
||||||
)
|
)
|
||||||
@@ -818,7 +804,7 @@ async def get_workflow_run_timeline(
|
|||||||
return await _flatten_workflow_run_timeline(current_org.organization_id, workflow_run_id)
|
return await _flatten_workflow_run_timeline(current_org.organization_id, workflow_run_id)
|
||||||
|
|
||||||
|
|
||||||
@base_router.get(
|
@legacy_base_router.get(
|
||||||
"/workflows/runs/{workflow_run_id}",
|
"/workflows/runs/{workflow_run_id}",
|
||||||
response_model=WorkflowRunResponse,
|
response_model=WorkflowRunResponse,
|
||||||
tags=["agent"],
|
tags=["agent"],
|
||||||
@@ -827,7 +813,7 @@ async def get_workflow_run_timeline(
|
|||||||
"x-fern-sdk-method-name": "get_workflow_run",
|
"x-fern-sdk-method-name": "get_workflow_run",
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@base_router.get(
|
@legacy_base_router.get(
|
||||||
"/workflows/runs/{workflow_run_id}/",
|
"/workflows/runs/{workflow_run_id}/",
|
||||||
response_model=WorkflowRunResponse,
|
response_model=WorkflowRunResponse,
|
||||||
include_in_schema=False,
|
include_in_schema=False,
|
||||||
@@ -843,7 +829,7 @@ async def get_workflow_run(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@base_router.post(
|
@legacy_base_router.post(
|
||||||
"/workflows",
|
"/workflows",
|
||||||
openapi_extra={
|
openapi_extra={
|
||||||
"requestBody": {
|
"requestBody": {
|
||||||
@@ -856,7 +842,7 @@ async def get_workflow_run(
|
|||||||
response_model=Workflow,
|
response_model=Workflow,
|
||||||
tags=["agent"],
|
tags=["agent"],
|
||||||
)
|
)
|
||||||
@base_router.post(
|
@legacy_base_router.post(
|
||||||
"/workflows/",
|
"/workflows/",
|
||||||
openapi_extra={
|
openapi_extra={
|
||||||
"requestBody": {
|
"requestBody": {
|
||||||
@@ -890,7 +876,7 @@ async def create_workflow(
|
|||||||
raise FailedToCreateWorkflow(str(e))
|
raise FailedToCreateWorkflow(str(e))
|
||||||
|
|
||||||
|
|
||||||
@base_router.put(
|
@legacy_base_router.put(
|
||||||
"/workflows/{workflow_permanent_id}",
|
"/workflows/{workflow_permanent_id}",
|
||||||
openapi_extra={
|
openapi_extra={
|
||||||
"requestBody": {
|
"requestBody": {
|
||||||
@@ -903,7 +889,7 @@ async def create_workflow(
|
|||||||
response_model=Workflow,
|
response_model=Workflow,
|
||||||
tags=["agent"],
|
tags=["agent"],
|
||||||
)
|
)
|
||||||
@base_router.put(
|
@legacy_base_router.put(
|
||||||
"/workflows/{workflow_permanent_id}/",
|
"/workflows/{workflow_permanent_id}/",
|
||||||
openapi_extra={
|
openapi_extra={
|
||||||
"requestBody": {
|
"requestBody": {
|
||||||
@@ -945,7 +931,7 @@ async def update_workflow(
|
|||||||
raise FailedToUpdateWorkflow(workflow_permanent_id, f"<{type(e).__name__}: {str(e)}>")
|
raise FailedToUpdateWorkflow(workflow_permanent_id, f"<{type(e).__name__}: {str(e)}>")
|
||||||
|
|
||||||
|
|
||||||
@base_router.delete(
|
@legacy_base_router.delete(
|
||||||
"/workflows/{workflow_permanent_id}",
|
"/workflows/{workflow_permanent_id}",
|
||||||
tags=["agent"],
|
tags=["agent"],
|
||||||
openapi_extra={
|
openapi_extra={
|
||||||
@@ -953,7 +939,7 @@ async def update_workflow(
|
|||||||
"x-fern-sdk-method-name": "delete_workflow",
|
"x-fern-sdk-method-name": "delete_workflow",
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@base_router.delete("/workflows/{workflow_permanent_id}/", include_in_schema=False)
|
@legacy_base_router.delete("/workflows/{workflow_permanent_id}/", include_in_schema=False)
|
||||||
async def delete_workflow(
|
async def delete_workflow(
|
||||||
workflow_permanent_id: str,
|
workflow_permanent_id: str,
|
||||||
current_org: Organization = Depends(org_auth_service.get_current_org),
|
current_org: Organization = Depends(org_auth_service.get_current_org),
|
||||||
@@ -962,7 +948,7 @@ async def delete_workflow(
|
|||||||
await app.WORKFLOW_SERVICE.delete_workflow_by_permanent_id(workflow_permanent_id, current_org.organization_id)
|
await app.WORKFLOW_SERVICE.delete_workflow_by_permanent_id(workflow_permanent_id, current_org.organization_id)
|
||||||
|
|
||||||
|
|
||||||
@base_router.get(
|
@legacy_base_router.get(
|
||||||
"/workflows",
|
"/workflows",
|
||||||
response_model=list[Workflow],
|
response_model=list[Workflow],
|
||||||
tags=["agent"],
|
tags=["agent"],
|
||||||
@@ -971,7 +957,7 @@ async def delete_workflow(
|
|||||||
"x-fern-sdk-method-name": "get_workflows",
|
"x-fern-sdk-method-name": "get_workflows",
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@base_router.get(
|
@legacy_base_router.get(
|
||||||
"/workflows/",
|
"/workflows/",
|
||||||
response_model=list[Workflow],
|
response_model=list[Workflow],
|
||||||
include_in_schema=False,
|
include_in_schema=False,
|
||||||
@@ -1020,7 +1006,7 @@ async def get_workflows(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@base_router.get(
|
@legacy_base_router.get(
|
||||||
"/workflows/templates",
|
"/workflows/templates",
|
||||||
response_model=list[Workflow],
|
response_model=list[Workflow],
|
||||||
tags=["agent"],
|
tags=["agent"],
|
||||||
@@ -1029,7 +1015,7 @@ async def get_workflows(
|
|||||||
"x-fern-sdk-method-name": "get_workflow_templates",
|
"x-fern-sdk-method-name": "get_workflow_templates",
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@base_router.get(
|
@legacy_base_router.get(
|
||||||
"/workflows/templates/",
|
"/workflows/templates/",
|
||||||
response_model=list[Workflow],
|
response_model=list[Workflow],
|
||||||
include_in_schema=False,
|
include_in_schema=False,
|
||||||
@@ -1048,7 +1034,7 @@ async def get_workflow_templates() -> list[Workflow]:
|
|||||||
return workflows
|
return workflows
|
||||||
|
|
||||||
|
|
||||||
@base_router.get(
|
@legacy_base_router.get(
|
||||||
"/workflows/{workflow_permanent_id}",
|
"/workflows/{workflow_permanent_id}",
|
||||||
response_model=Workflow,
|
response_model=Workflow,
|
||||||
tags=["agent"],
|
tags=["agent"],
|
||||||
@@ -1057,7 +1043,7 @@ async def get_workflow_templates() -> list[Workflow]:
|
|||||||
"x-fern-sdk-method-name": "get_workflow",
|
"x-fern-sdk-method-name": "get_workflow",
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@base_router.get("/workflows/{workflow_permanent_id}/", response_model=Workflow, include_in_schema=False)
|
@legacy_base_router.get("/workflows/{workflow_permanent_id}/", response_model=Workflow, include_in_schema=False)
|
||||||
async def get_workflow(
|
async def get_workflow(
|
||||||
workflow_permanent_id: str,
|
workflow_permanent_id: str,
|
||||||
version: int | None = None,
|
version: int | None = None,
|
||||||
@@ -1076,7 +1062,7 @@ async def get_workflow(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@base_router.post(
|
@legacy_base_router.post(
|
||||||
"/suggest/{ai_suggestion_type}",
|
"/suggest/{ai_suggestion_type}",
|
||||||
include_in_schema=False,
|
include_in_schema=False,
|
||||||
tags=["agent"],
|
tags=["agent"],
|
||||||
@@ -1085,7 +1071,7 @@ async def get_workflow(
|
|||||||
"x-fern-sdk-method-name": "suggest",
|
"x-fern-sdk-method-name": "suggest",
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@base_router.post("/suggest/{ai_suggestion_type}/", include_in_schema=False)
|
@legacy_base_router.post("/suggest/{ai_suggestion_type}/", include_in_schema=False)
|
||||||
async def suggest(
|
async def suggest(
|
||||||
ai_suggestion_type: AISuggestionType,
|
ai_suggestion_type: AISuggestionType,
|
||||||
data: AISuggestionRequest,
|
data: AISuggestionRequest,
|
||||||
@@ -1114,7 +1100,7 @@ async def suggest(
|
|||||||
raise HTTPException(status_code=400, detail="Failed to suggest data schema. Please try again later.")
|
raise HTTPException(status_code=400, detail="Failed to suggest data schema. Please try again later.")
|
||||||
|
|
||||||
|
|
||||||
@base_router.post(
|
@legacy_base_router.post(
|
||||||
"/generate/task",
|
"/generate/task",
|
||||||
tags=["agent"],
|
tags=["agent"],
|
||||||
openapi_extra={
|
openapi_extra={
|
||||||
@@ -1122,7 +1108,7 @@ async def suggest(
|
|||||||
"x-fern-sdk-method-name": "generate_task",
|
"x-fern-sdk-method-name": "generate_task",
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@base_router.post("/generate/task/", include_in_schema=False)
|
@legacy_base_router.post("/generate/task/", include_in_schema=False)
|
||||||
async def generate_task(
|
async def generate_task(
|
||||||
data: GenerateTaskRequest,
|
data: GenerateTaskRequest,
|
||||||
current_org: Organization = Depends(org_auth_service.get_current_org),
|
current_org: Organization = Depends(org_auth_service.get_current_org),
|
||||||
@@ -1134,7 +1120,7 @@ async def generate_task(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@base_router.put(
|
@legacy_base_router.put(
|
||||||
"/organizations",
|
"/organizations",
|
||||||
tags=["server"],
|
tags=["server"],
|
||||||
openapi_extra={
|
openapi_extra={
|
||||||
@@ -1142,7 +1128,7 @@ async def generate_task(
|
|||||||
"x-fern-sdk-method-name": "update_organization",
|
"x-fern-sdk-method-name": "update_organization",
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@base_router.put(
|
@legacy_base_router.put(
|
||||||
"/organizations",
|
"/organizations",
|
||||||
include_in_schema=False,
|
include_in_schema=False,
|
||||||
)
|
)
|
||||||
@@ -1156,7 +1142,7 @@ async def update_organization(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@base_router.get(
|
@legacy_base_router.get(
|
||||||
"/organizations",
|
"/organizations",
|
||||||
tags=["server"],
|
tags=["server"],
|
||||||
openapi_extra={
|
openapi_extra={
|
||||||
@@ -1164,7 +1150,7 @@ async def update_organization(
|
|||||||
"x-fern-sdk-method-name": "get_organizations",
|
"x-fern-sdk-method-name": "get_organizations",
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@base_router.get(
|
@legacy_base_router.get(
|
||||||
"/organizations/",
|
"/organizations/",
|
||||||
include_in_schema=False,
|
include_in_schema=False,
|
||||||
)
|
)
|
||||||
@@ -1174,7 +1160,7 @@ async def get_organizations(
|
|||||||
return GetOrganizationsResponse(organizations=[current_org])
|
return GetOrganizationsResponse(organizations=[current_org])
|
||||||
|
|
||||||
|
|
||||||
@base_router.get(
|
@legacy_base_router.get(
|
||||||
"/organizations/{organization_id}/apikeys/",
|
"/organizations/{organization_id}/apikeys/",
|
||||||
tags=["server"],
|
tags=["server"],
|
||||||
openapi_extra={
|
openapi_extra={
|
||||||
@@ -1182,7 +1168,7 @@ async def get_organizations(
|
|||||||
"x-fern-sdk-method-name": "get_api_keys",
|
"x-fern-sdk-method-name": "get_api_keys",
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@base_router.get(
|
@legacy_base_router.get(
|
||||||
"/organizations/{organization_id}/apikeys",
|
"/organizations/{organization_id}/apikeys",
|
||||||
include_in_schema=False,
|
include_in_schema=False,
|
||||||
)
|
)
|
||||||
@@ -1215,7 +1201,7 @@ async def _validate_file_size(file: UploadFile) -> UploadFile:
|
|||||||
return file
|
return file
|
||||||
|
|
||||||
|
|
||||||
@base_router.post(
|
@legacy_base_router.post(
|
||||||
"/upload_file",
|
"/upload_file",
|
||||||
tags=["server"],
|
tags=["server"],
|
||||||
openapi_extra={
|
openapi_extra={
|
||||||
@@ -1223,7 +1209,7 @@ async def _validate_file_size(file: UploadFile) -> UploadFile:
|
|||||||
"x-fern-sdk-method-name": "upload_file",
|
"x-fern-sdk-method-name": "upload_file",
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@base_router.post(
|
@legacy_base_router.post(
|
||||||
"/upload_file/",
|
"/upload_file/",
|
||||||
include_in_schema=False,
|
include_in_schema=False,
|
||||||
)
|
)
|
||||||
@@ -1268,7 +1254,7 @@ async def upload_file(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@v2_router.post(
|
@legacy_v2_router.post(
|
||||||
"/tasks",
|
"/tasks",
|
||||||
tags=["agent"],
|
tags=["agent"],
|
||||||
openapi_extra={
|
openapi_extra={
|
||||||
@@ -1276,7 +1262,7 @@ async def upload_file(
|
|||||||
"x-fern-sdk-method-name": "run_task_v2",
|
"x-fern-sdk-method-name": "run_task_v2",
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@v2_router.post(
|
@legacy_v2_router.post(
|
||||||
"/tasks/",
|
"/tasks/",
|
||||||
include_in_schema=False,
|
include_in_schema=False,
|
||||||
)
|
)
|
||||||
@@ -1327,7 +1313,7 @@ async def run_task_v2(
|
|||||||
return task_v2.model_dump(by_alias=True)
|
return task_v2.model_dump(by_alias=True)
|
||||||
|
|
||||||
|
|
||||||
@v2_router.get(
|
@legacy_v2_router.get(
|
||||||
"/tasks/{task_id}",
|
"/tasks/{task_id}",
|
||||||
tags=["agent"],
|
tags=["agent"],
|
||||||
openapi_extra={
|
openapi_extra={
|
||||||
@@ -1335,7 +1321,7 @@ async def run_task_v2(
|
|||||||
"x-fern-sdk-method-name": "get_task_v2",
|
"x-fern-sdk-method-name": "get_task_v2",
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@v2_router.get(
|
@legacy_v2_router.get(
|
||||||
"/tasks/{task_id}/",
|
"/tasks/{task_id}/",
|
||||||
include_in_schema=False,
|
include_in_schema=False,
|
||||||
)
|
)
|
||||||
@@ -1349,102 +1335,6 @@ async def get_task_v2(
|
|||||||
return task_v2.model_dump(by_alias=True)
|
return task_v2.model_dump(by_alias=True)
|
||||||
|
|
||||||
|
|
||||||
@base_router.get(
|
|
||||||
"/browser_sessions/{browser_session_id}",
|
|
||||||
response_model=BrowserSessionResponse,
|
|
||||||
tags=["session"],
|
|
||||||
openapi_extra={
|
|
||||||
"x-fern-sdk-group-name": "session",
|
|
||||||
"x-fern-sdk-method-name": "get_browser_session",
|
|
||||||
},
|
|
||||||
)
|
|
||||||
@base_router.get(
|
|
||||||
"/browser_sessions/{browser_session_id}/",
|
|
||||||
response_model=BrowserSessionResponse,
|
|
||||||
include_in_schema=False,
|
|
||||||
)
|
|
||||||
async def get_browser_session(
|
|
||||||
browser_session_id: str,
|
|
||||||
current_org: Organization = Depends(org_auth_service.get_current_org),
|
|
||||||
) -> BrowserSessionResponse:
|
|
||||||
analytics.capture("skyvern-oss-agent-workflow-run-get")
|
|
||||||
browser_session = await app.PERSISTENT_SESSIONS_MANAGER.get_session(
|
|
||||||
browser_session_id,
|
|
||||||
current_org.organization_id,
|
|
||||||
)
|
|
||||||
if not browser_session:
|
|
||||||
raise HTTPException(status_code=404, detail=f"Browser session {browser_session_id} not found")
|
|
||||||
return BrowserSessionResponse.from_browser_session(browser_session)
|
|
||||||
|
|
||||||
|
|
||||||
@base_router.get(
|
|
||||||
"/browser_sessions",
|
|
||||||
response_model=list[BrowserSessionResponse],
|
|
||||||
tags=["session"],
|
|
||||||
openapi_extra={
|
|
||||||
"x-fern-sdk-group-name": "session",
|
|
||||||
"x-fern-sdk-method-name": "get_browser_sessions",
|
|
||||||
},
|
|
||||||
)
|
|
||||||
@base_router.get(
|
|
||||||
"/browser_sessions/",
|
|
||||||
response_model=list[BrowserSessionResponse],
|
|
||||||
include_in_schema=False,
|
|
||||||
)
|
|
||||||
async def get_browser_sessions(
|
|
||||||
current_org: Organization = Depends(org_auth_service.get_current_org),
|
|
||||||
) -> list[BrowserSessionResponse]:
|
|
||||||
"""Get all active browser sessions for the organization"""
|
|
||||||
analytics.capture("skyvern-oss-agent-browser-sessions-get")
|
|
||||||
browser_sessions = await app.PERSISTENT_SESSIONS_MANAGER.get_active_sessions(current_org.organization_id)
|
|
||||||
return [BrowserSessionResponse.from_browser_session(browser_session) for browser_session in browser_sessions]
|
|
||||||
|
|
||||||
|
|
||||||
@base_router.post(
|
|
||||||
"/browser_sessions",
|
|
||||||
response_model=BrowserSessionResponse,
|
|
||||||
tags=["session"],
|
|
||||||
openapi_extra={
|
|
||||||
"x-fern-sdk-group-name": "session",
|
|
||||||
"x-fern-sdk-method-name": "create_browser_session",
|
|
||||||
},
|
|
||||||
)
|
|
||||||
@base_router.post(
|
|
||||||
"/browser_sessions/",
|
|
||||||
response_model=BrowserSessionResponse,
|
|
||||||
include_in_schema=False,
|
|
||||||
)
|
|
||||||
async def create_browser_session(
|
|
||||||
current_org: Organization = Depends(org_auth_service.get_current_org),
|
|
||||||
) -> BrowserSessionResponse:
|
|
||||||
browser_session = await app.PERSISTENT_SESSIONS_MANAGER.create_session(current_org.organization_id)
|
|
||||||
return BrowserSessionResponse.from_browser_session(browser_session)
|
|
||||||
|
|
||||||
|
|
||||||
@base_router.post(
|
|
||||||
"/browser_sessions/{session_id}/close",
|
|
||||||
tags=["session"],
|
|
||||||
openapi_extra={
|
|
||||||
"x-fern-sdk-group-name": "session",
|
|
||||||
"x-fern-sdk-method-name": "close_browser_session",
|
|
||||||
},
|
|
||||||
)
|
|
||||||
@base_router.post(
|
|
||||||
"/browser_sessions/{session_id}/close/",
|
|
||||||
include_in_schema=False,
|
|
||||||
)
|
|
||||||
async def close_browser_session(
|
|
||||||
session_id: str,
|
|
||||||
current_org: Organization = Depends(org_auth_service.get_current_org),
|
|
||||||
) -> ORJSONResponse:
|
|
||||||
await app.PERSISTENT_SESSIONS_MANAGER.close_session(current_org.organization_id, session_id)
|
|
||||||
return ORJSONResponse(
|
|
||||||
content={"message": "Browser session closed"},
|
|
||||||
status_code=200,
|
|
||||||
media_type="application/json",
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
async def _flatten_workflow_run_timeline(organization_id: str, workflow_run_id: str) -> list[WorkflowRunTimeline]:
|
async def _flatten_workflow_run_timeline(organization_id: str, workflow_run_id: str) -> list[WorkflowRunTimeline]:
|
||||||
"""
|
"""
|
||||||
Get the timeline workflow runs including the nested workflow runs in a flattened list
|
Get the timeline workflow runs including the nested workflow runs in a flattened list
|
||||||
@@ -1494,15 +1384,21 @@ async def _flatten_workflow_run_timeline(organization_id: str, workflow_run_id:
|
|||||||
return final_workflow_run_block_timeline
|
return final_workflow_run_block_timeline
|
||||||
|
|
||||||
|
|
||||||
@official_api_router.post(
|
@base_router.post(
|
||||||
"/tasks",
|
"/tasks",
|
||||||
tags=["agent"],
|
tags=["Agent"],
|
||||||
openapi_extra={
|
openapi_extra={
|
||||||
"x-fern-sdk-group-name": "agent",
|
"x-fern-sdk-group-name": "agent",
|
||||||
"x-fern-sdk-method-name": "run_task",
|
"x-fern-sdk-method-name": "run_task",
|
||||||
},
|
},
|
||||||
|
description="Run a task",
|
||||||
|
summary="Run a task",
|
||||||
|
responses={
|
||||||
|
200: {"description": "Successfully run task"},
|
||||||
|
400: {"description": "Invalid agent engine"},
|
||||||
|
},
|
||||||
)
|
)
|
||||||
@official_api_router.post("/tasks/", include_in_schema=False)
|
@base_router.post("/tasks/", include_in_schema=False)
|
||||||
async def run_task(
|
async def run_task(
|
||||||
request: Request,
|
request: Request,
|
||||||
background_tasks: BackgroundTasks,
|
background_tasks: BackgroundTasks,
|
||||||
|
|||||||
165
skyvern/forge/sdk/routes/browser_sessions.py
Normal file
165
skyvern/forge/sdk/routes/browser_sessions.py
Normal file
@@ -0,0 +1,165 @@
|
|||||||
|
from fastapi import Depends, HTTPException
|
||||||
|
from fastapi.responses import ORJSONResponse
|
||||||
|
|
||||||
|
from skyvern import analytics
|
||||||
|
from skyvern.forge import app
|
||||||
|
from skyvern.forge.sdk.routes.routers import base_router, legacy_base_router
|
||||||
|
from skyvern.forge.sdk.schemas.organizations import Organization
|
||||||
|
from skyvern.forge.sdk.services import org_auth_service
|
||||||
|
from skyvern.webeye.schemas import BrowserSessionResponse
|
||||||
|
|
||||||
|
|
||||||
|
@base_router.get(
|
||||||
|
"/browser_sessions/{browser_session_id}",
|
||||||
|
response_model=BrowserSessionResponse,
|
||||||
|
tags=["Browser Sessions"],
|
||||||
|
openapi_extra={
|
||||||
|
"x-fern-sdk-group-name": "browser_session",
|
||||||
|
"x-fern-sdk-method-name": "get_browser_session",
|
||||||
|
},
|
||||||
|
description="Get details about a specific browser session by ID",
|
||||||
|
summary="Get browser session details",
|
||||||
|
responses={
|
||||||
|
200: {"description": "Successfully retrieved browser session details"},
|
||||||
|
404: {"description": "Browser session not found"},
|
||||||
|
401: {"description": "Unauthorized - Invalid or missing authentication"},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
@legacy_base_router.get(
|
||||||
|
"/browser_sessions/{browser_session_id}",
|
||||||
|
response_model=BrowserSessionResponse,
|
||||||
|
tags=["session"],
|
||||||
|
openapi_extra={
|
||||||
|
"x-fern-sdk-group-name": "session",
|
||||||
|
"x-fern-sdk-method-name": "get_browser_session",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
@legacy_base_router.get(
|
||||||
|
"/browser_sessions/{browser_session_id}/",
|
||||||
|
response_model=BrowserSessionResponse,
|
||||||
|
include_in_schema=False,
|
||||||
|
)
|
||||||
|
async def get_browser_session(
|
||||||
|
browser_session_id: str,
|
||||||
|
current_org: Organization = Depends(org_auth_service.get_current_org),
|
||||||
|
) -> BrowserSessionResponse:
|
||||||
|
analytics.capture("skyvern-oss-agent-browser-session-get")
|
||||||
|
browser_session = await app.PERSISTENT_SESSIONS_MANAGER.get_session(
|
||||||
|
browser_session_id,
|
||||||
|
current_org.organization_id,
|
||||||
|
)
|
||||||
|
if not browser_session:
|
||||||
|
raise HTTPException(status_code=404, detail=f"Browser session {browser_session_id} not found")
|
||||||
|
return BrowserSessionResponse.from_browser_session(browser_session)
|
||||||
|
|
||||||
|
|
||||||
|
@base_router.get(
|
||||||
|
"/browser_sessions",
|
||||||
|
response_model=list[BrowserSessionResponse],
|
||||||
|
tags=["Browser Sessions"],
|
||||||
|
openapi_extra={
|
||||||
|
"x-fern-sdk-group-name": "browser_session",
|
||||||
|
"x-fern-sdk-method-name": "get_browser_sessions",
|
||||||
|
},
|
||||||
|
description="Get all active browser sessions for the organization",
|
||||||
|
summary="Get all active browser sessions",
|
||||||
|
responses={
|
||||||
|
200: {"description": "Successfully retrieved all active browser sessions"},
|
||||||
|
401: {"description": "Unauthorized - Invalid or missing authentication"},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
@legacy_base_router.get(
|
||||||
|
"/browser_sessions",
|
||||||
|
response_model=list[BrowserSessionResponse],
|
||||||
|
tags=["session"],
|
||||||
|
openapi_extra={
|
||||||
|
"x-fern-sdk-group-name": "browser_sessions",
|
||||||
|
"x-fern-sdk-method-name": "get_browser_sessions",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
@legacy_base_router.get(
|
||||||
|
"/browser_sessions/",
|
||||||
|
response_model=list[BrowserSessionResponse],
|
||||||
|
include_in_schema=False,
|
||||||
|
)
|
||||||
|
async def get_browser_sessions(
|
||||||
|
current_org: Organization = Depends(org_auth_service.get_current_org),
|
||||||
|
) -> list[BrowserSessionResponse]:
|
||||||
|
"""Get all active browser sessions for the organization"""
|
||||||
|
analytics.capture("skyvern-oss-agent-browser-sessions-get")
|
||||||
|
browser_sessions = await app.PERSISTENT_SESSIONS_MANAGER.get_active_sessions(current_org.organization_id)
|
||||||
|
return [BrowserSessionResponse.from_browser_session(browser_session) for browser_session in browser_sessions]
|
||||||
|
|
||||||
|
|
||||||
|
@base_router.post(
|
||||||
|
"/browser_sessions",
|
||||||
|
response_model=BrowserSessionResponse,
|
||||||
|
tags=["Browser Sessions"],
|
||||||
|
openapi_extra={
|
||||||
|
"x-fern-sdk-group-name": "browser_session",
|
||||||
|
"x-fern-sdk-method-name": "create_browser_session",
|
||||||
|
},
|
||||||
|
description="Create a new browser session",
|
||||||
|
summary="Create a new browser session",
|
||||||
|
responses={
|
||||||
|
200: {"description": "Successfully created browser session"},
|
||||||
|
401: {"description": "Unauthorized - Invalid or missing authentication"},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
@legacy_base_router.post(
|
||||||
|
"/browser_sessions",
|
||||||
|
response_model=BrowserSessionResponse,
|
||||||
|
tags=["Browser Sessions"],
|
||||||
|
openapi_extra={
|
||||||
|
"x-fern-sdk-group-name": "session",
|
||||||
|
"x-fern-sdk-method-name": "create_browser_session",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
@legacy_base_router.post(
|
||||||
|
"/browser_sessions/",
|
||||||
|
response_model=BrowserSessionResponse,
|
||||||
|
include_in_schema=False,
|
||||||
|
)
|
||||||
|
async def create_browser_session(
|
||||||
|
current_org: Organization = Depends(org_auth_service.get_current_org),
|
||||||
|
) -> BrowserSessionResponse:
|
||||||
|
browser_session = await app.PERSISTENT_SESSIONS_MANAGER.create_session(current_org.organization_id)
|
||||||
|
return BrowserSessionResponse.from_browser_session(browser_session)
|
||||||
|
|
||||||
|
|
||||||
|
@base_router.post(
|
||||||
|
"/browser_sessions/{browser_session_id}/close",
|
||||||
|
tags=["Browser Sessions"],
|
||||||
|
openapi_extra={
|
||||||
|
"x-fern-sdk-group-name": "browser_session",
|
||||||
|
"x-fern-sdk-method-name": "close_browser_session",
|
||||||
|
},
|
||||||
|
description="Close a browser session",
|
||||||
|
summary="Close a browser session",
|
||||||
|
responses={
|
||||||
|
200: {"description": "Successfully closed browser session"},
|
||||||
|
401: {"description": "Unauthorized - Invalid or missing authentication"},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
@legacy_base_router.post(
|
||||||
|
"/browser_sessions/{browser_session_id}/close",
|
||||||
|
tags=["Browser Sessions"],
|
||||||
|
openapi_extra={
|
||||||
|
"x-fern-sdk-group-name": "browser_session",
|
||||||
|
"x-fern-sdk-method-name": "close_browser_session",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
@legacy_base_router.post(
|
||||||
|
"/browser_sessions/{browser_session_id}/close/",
|
||||||
|
include_in_schema=False,
|
||||||
|
)
|
||||||
|
async def close_browser_session(
|
||||||
|
browser_session_id: str,
|
||||||
|
current_org: Organization = Depends(org_auth_service.get_current_org),
|
||||||
|
) -> ORJSONResponse:
|
||||||
|
await app.PERSISTENT_SESSIONS_MANAGER.close_session(current_org.organization_id, browser_session_id)
|
||||||
|
return ORJSONResponse(
|
||||||
|
content={"message": "Browser session closed"},
|
||||||
|
status_code=200,
|
||||||
|
media_type="application/json",
|
||||||
|
)
|
||||||
5
skyvern/forge/sdk/routes/routers.py
Normal file
5
skyvern/forge/sdk/routes/routers.py
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
from fastapi import APIRouter
|
||||||
|
|
||||||
|
base_router = APIRouter()
|
||||||
|
legacy_base_router = APIRouter(include_in_schema=False)
|
||||||
|
legacy_v2_router = APIRouter(include_in_schema=False)
|
||||||
@@ -3,21 +3,21 @@ import base64
|
|||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
import structlog
|
import structlog
|
||||||
from fastapi import APIRouter, WebSocket, WebSocketDisconnect
|
from fastapi import WebSocket, WebSocketDisconnect
|
||||||
from pydantic import ValidationError
|
from pydantic import ValidationError
|
||||||
from websockets.exceptions import ConnectionClosedOK
|
from websockets.exceptions import ConnectionClosedOK
|
||||||
|
|
||||||
from skyvern.forge import app
|
from skyvern.forge import app
|
||||||
|
from skyvern.forge.sdk.routes.routers import legacy_base_router
|
||||||
from skyvern.forge.sdk.schemas.tasks import TaskStatus
|
from skyvern.forge.sdk.schemas.tasks import TaskStatus
|
||||||
from skyvern.forge.sdk.services.org_auth_service import get_current_org
|
from skyvern.forge.sdk.services.org_auth_service import get_current_org
|
||||||
from skyvern.forge.sdk.workflow.models.workflow import WorkflowRunStatus
|
from skyvern.forge.sdk.workflow.models.workflow import WorkflowRunStatus
|
||||||
|
|
||||||
LOG = structlog.get_logger()
|
LOG = structlog.get_logger()
|
||||||
websocket_router = APIRouter()
|
|
||||||
STREAMING_TIMEOUT = 300
|
STREAMING_TIMEOUT = 300
|
||||||
|
|
||||||
|
|
||||||
@websocket_router.websocket("/tasks/{task_id}")
|
@legacy_base_router.websocket("/stream/tasks/{task_id}")
|
||||||
async def task_stream(
|
async def task_stream(
|
||||||
websocket: WebSocket,
|
websocket: WebSocket,
|
||||||
task_id: str,
|
task_id: str,
|
||||||
@@ -119,7 +119,7 @@ async def task_stream(
|
|||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
@websocket_router.websocket("/workflow_runs/{workflow_run_id}")
|
@legacy_base_router.websocket("/stream/workflow_runs/{workflow_run_id}")
|
||||||
async def workflow_run_streaming(
|
async def workflow_run_streaming(
|
||||||
websocket: WebSocket,
|
websocket: WebSocket,
|
||||||
workflow_run_id: str,
|
workflow_run_id: str,
|
||||||
|
|||||||
@@ -1,25 +1,25 @@
|
|||||||
import structlog
|
import structlog
|
||||||
from fastapi import APIRouter, Depends, HTTPException
|
from fastapi import Depends, HTTPException
|
||||||
|
|
||||||
from skyvern.forge import app
|
from skyvern.forge import app
|
||||||
from skyvern.forge.prompts import prompt_engine
|
from skyvern.forge.prompts import prompt_engine
|
||||||
|
from skyvern.forge.sdk.routes.routers import legacy_base_router
|
||||||
from skyvern.forge.sdk.schemas.organizations import Organization
|
from skyvern.forge.sdk.schemas.organizations import Organization
|
||||||
from skyvern.forge.sdk.schemas.totp_codes import TOTPCode, TOTPCodeCreate
|
from skyvern.forge.sdk.schemas.totp_codes import TOTPCode, TOTPCodeCreate
|
||||||
from skyvern.forge.sdk.services import org_auth_service
|
from skyvern.forge.sdk.services import org_auth_service
|
||||||
|
|
||||||
LOG = structlog.get_logger()
|
LOG = structlog.get_logger()
|
||||||
totp_router = APIRouter()
|
|
||||||
|
|
||||||
|
|
||||||
@totp_router.post(
|
@legacy_base_router.post(
|
||||||
"",
|
"/totp",
|
||||||
tags=["agent"],
|
tags=["agent"],
|
||||||
openapi_extra={
|
openapi_extra={
|
||||||
"x-fern-sdk-group-name": "agent",
|
"x-fern-sdk-group-name": "agent",
|
||||||
"x-fern-sdk-method-name": "send_totp_code",
|
"x-fern-sdk-method-name": "send_totp_code",
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@totp_router.post("/", include_in_schema=False)
|
@legacy_base_router.post("/totp/", include_in_schema=False)
|
||||||
async def send_totp_code(
|
async def send_totp_code(
|
||||||
data: TOTPCodeCreate, curr_org: Organization = Depends(org_auth_service.get_current_org)
|
data: TOTPCodeCreate, curr_org: Organization = Depends(org_auth_service.get_current_org)
|
||||||
) -> TOTPCode:
|
) -> TOTPCode:
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ from enum import StrEnum
|
|||||||
from typing import Any, List
|
from typing import Any, List
|
||||||
|
|
||||||
from pydantic import BaseModel, field_validator
|
from pydantic import BaseModel, field_validator
|
||||||
|
from typing_extensions import deprecated
|
||||||
|
|
||||||
from skyvern.forge.sdk.schemas.files import FileInfo
|
from skyvern.forge.sdk.schemas.files import FileInfo
|
||||||
from skyvern.forge.sdk.schemas.task_v2 import TaskV2
|
from skyvern.forge.sdk.schemas.task_v2 import TaskV2
|
||||||
@@ -13,6 +14,7 @@ from skyvern.schemas.runs import ProxyLocation
|
|||||||
from skyvern.utils.url_validators import validate_url
|
from skyvern.utils.url_validators import validate_url
|
||||||
|
|
||||||
|
|
||||||
|
@deprecated("Use WorkflowRunRequest instead")
|
||||||
class WorkflowRequestBody(BaseModel):
|
class WorkflowRequestBody(BaseModel):
|
||||||
data: dict[str, Any] | None = None
|
data: dict[str, Any] | None = None
|
||||||
proxy_location: ProxyLocation | None = None
|
proxy_location: ProxyLocation | None = None
|
||||||
@@ -29,6 +31,7 @@ class WorkflowRequestBody(BaseModel):
|
|||||||
return validate_url(url)
|
return validate_url(url)
|
||||||
|
|
||||||
|
|
||||||
|
@deprecated("Use WorkflowRunResponse instead")
|
||||||
class RunWorkflowResponse(BaseModel):
|
class RunWorkflowResponse(BaseModel):
|
||||||
workflow_id: str
|
workflow_id: str
|
||||||
workflow_run_id: str
|
workflow_run_id: str
|
||||||
|
|||||||
@@ -9,12 +9,12 @@ from skyvern.utils.url_validators import validate_url
|
|||||||
|
|
||||||
|
|
||||||
class ProxyLocation(StrEnum):
|
class ProxyLocation(StrEnum):
|
||||||
|
RESIDENTIAL = "RESIDENTIAL"
|
||||||
US_CA = "US-CA"
|
US_CA = "US-CA"
|
||||||
US_NY = "US-NY"
|
US_NY = "US-NY"
|
||||||
US_TX = "US-TX"
|
US_TX = "US-TX"
|
||||||
US_FL = "US-FL"
|
US_FL = "US-FL"
|
||||||
US_WA = "US-WA"
|
US_WA = "US-WA"
|
||||||
RESIDENTIAL = "RESIDENTIAL"
|
|
||||||
RESIDENTIAL_ES = "RESIDENTIAL_ES"
|
RESIDENTIAL_ES = "RESIDENTIAL_ES"
|
||||||
RESIDENTIAL_IE = "RESIDENTIAL_IE"
|
RESIDENTIAL_IE = "RESIDENTIAL_IE"
|
||||||
RESIDENTIAL_GB = "RESIDENTIAL_GB"
|
RESIDENTIAL_GB = "RESIDENTIAL_GB"
|
||||||
@@ -113,23 +113,56 @@ class RunStatus(StrEnum):
|
|||||||
|
|
||||||
|
|
||||||
class TaskRunRequest(BaseModel):
|
class TaskRunRequest(BaseModel):
|
||||||
prompt: str
|
prompt: str = Field(description="The goal or task description for Skyvern to accomplish")
|
||||||
url: str | None = None
|
url: str | None = Field(
|
||||||
title: str | None = None
|
default=None,
|
||||||
engine: RunEngine = RunEngine.skyvern_v2
|
description="The starting URL for the task. If not provided, Skyvern will attempt to determine an appropriate URL",
|
||||||
proxy_location: ProxyLocation | None = None
|
)
|
||||||
data_extraction_schema: dict | list | str | None = None
|
title: str | None = Field(default=None, description="Optional title for the task")
|
||||||
error_code_mapping: dict[str, str] | None = None
|
engine: RunEngine = Field(
|
||||||
max_steps: int | None = None
|
default=RunEngine.skyvern_v2, description="The Skyvern engine version to use for this task"
|
||||||
webhook_url: str | None = None
|
)
|
||||||
totp_identifier: str | None = None
|
proxy_location: ProxyLocation = Field(
|
||||||
totp_url: str | None = None
|
default=ProxyLocation.RESIDENTIAL, description="Geographic Proxy location to route the browser traffic through"
|
||||||
browser_session_id: str | None = None
|
)
|
||||||
publish_workflow: bool = False
|
data_extraction_schema: dict | list | str | None = Field(
|
||||||
|
default=None, description="Schema defining what data should be extracted from the webpage"
|
||||||
|
)
|
||||||
|
error_code_mapping: dict[str, str] | None = Field(
|
||||||
|
default=None, description="Custom mapping of error codes to error messages if Skyvern encounters an error"
|
||||||
|
)
|
||||||
|
max_steps: int | None = Field(
|
||||||
|
default=None, description="Maximum number of steps the task can take before timing out"
|
||||||
|
)
|
||||||
|
webhook_url: str | None = Field(
|
||||||
|
default=None, description="URL to send task status updates to after a run is finished"
|
||||||
|
)
|
||||||
|
totp_identifier: str | None = Field(
|
||||||
|
default=None,
|
||||||
|
description="Identifier for TOTP (Time-based One-Time Password) authentication if codes are being pushed to Skyvern",
|
||||||
|
)
|
||||||
|
totp_url: str | None = Field(
|
||||||
|
default=None,
|
||||||
|
description="URL for TOTP authentication setup if Skyvern should be polling endpoint for 2FA codes",
|
||||||
|
)
|
||||||
|
browser_session_id: str | None = Field(
|
||||||
|
default=None,
|
||||||
|
description="ID of an existing browser session to reuse, having it continue from the current screen state",
|
||||||
|
)
|
||||||
|
publish_workflow: bool = Field(default=False, description="Whether to publish this task as a reusable workflow. ")
|
||||||
|
|
||||||
@field_validator("url", "webhook_url", "totp_url")
|
@field_validator("url", "webhook_url", "totp_url")
|
||||||
@classmethod
|
@classmethod
|
||||||
def validate_urls(cls, url: str | None) -> str | None:
|
def validate_urls(cls, url: str | None) -> str | None:
|
||||||
|
"""
|
||||||
|
Validates that URLs provided to Skyvern are properly formatted.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
url: The URL for Skyvern to validate
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The validated URL or None if no URL was provided
|
||||||
|
"""
|
||||||
if url is None:
|
if url is None:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@@ -137,13 +170,27 @@ class TaskRunRequest(BaseModel):
|
|||||||
|
|
||||||
|
|
||||||
class WorkflowRunRequest(BaseModel):
|
class WorkflowRunRequest(BaseModel):
|
||||||
title: str | None = None
|
workflow_id: str = Field(description="ID of the workflow to run")
|
||||||
parameters: dict[str, Any] | None = None
|
title: str | None = Field(default=None, description="Optional title for this workflow run")
|
||||||
proxy_location: ProxyLocation | None = None
|
parameters: dict[str, Any] = Field(default={}, description="Parameters to pass to the workflow")
|
||||||
webhook_url: str | None = None
|
proxy_location: ProxyLocation = Field(
|
||||||
totp_url: str | None = None
|
default=ProxyLocation.RESIDENTIAL, description="Location of proxy to use for this workflow run"
|
||||||
totp_identifier: str | None = None
|
)
|
||||||
browser_session_id: str | None = None
|
webhook_url: str | None = Field(
|
||||||
|
default=None, description="URL to send workflow status updates to after a run is finished"
|
||||||
|
)
|
||||||
|
totp_url: str | None = Field(
|
||||||
|
default=None,
|
||||||
|
description="URL for TOTP authentication setup if Skyvern should be polling endpoint for 2FA codes",
|
||||||
|
)
|
||||||
|
totp_identifier: str | None = Field(
|
||||||
|
default=None,
|
||||||
|
description="Identifier for TOTP (Time-based One-Time Password) authentication if codes are being pushed to Skyvern",
|
||||||
|
)
|
||||||
|
browser_session_id: str | None = Field(
|
||||||
|
default=None,
|
||||||
|
description="ID of an existing browser session to reuse, having it continue from the current screen state",
|
||||||
|
)
|
||||||
|
|
||||||
@field_validator("webhook_url", "totp_url")
|
@field_validator("webhook_url", "totp_url")
|
||||||
@classmethod
|
@classmethod
|
||||||
@@ -154,22 +201,30 @@ class WorkflowRunRequest(BaseModel):
|
|||||||
|
|
||||||
|
|
||||||
class BaseRunResponse(BaseModel):
|
class BaseRunResponse(BaseModel):
|
||||||
run_id: str
|
run_id: str = Field(description="Unique identifier for this run")
|
||||||
status: RunStatus
|
status: RunStatus = Field(description="Current status of the run")
|
||||||
output: dict | list | str | None = None
|
output: dict | list | str | None = Field(
|
||||||
failure_reason: str | None = None
|
default=None, description="Output data from the run, if any. Format depends on the schema in the input"
|
||||||
created_at: datetime
|
)
|
||||||
modified_at: datetime
|
failure_reason: str | None = Field(default=None, description="Reason for failure if the run failed")
|
||||||
|
created_at: datetime = Field(description="Timestamp when this run was created")
|
||||||
|
modified_at: datetime = Field(description="Timestamp when this run was last modified")
|
||||||
|
|
||||||
|
|
||||||
class TaskRunResponse(BaseRunResponse):
|
class TaskRunResponse(BaseRunResponse):
|
||||||
run_type: Literal[RunType.task_v1, RunType.task_v2]
|
run_type: Literal[RunType.task_v1, RunType.task_v2] = Field(
|
||||||
run_request: TaskRunRequest | None = None
|
description="Type of task run - either task_v1 or task_v2"
|
||||||
|
)
|
||||||
|
run_request: TaskRunRequest | None = Field(
|
||||||
|
default=None, description="The original request parameters used to start this task run"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class WorkflowRunResponse(BaseRunResponse):
|
class WorkflowRunResponse(BaseRunResponse):
|
||||||
run_type: Literal[RunType.workflow_run]
|
run_type: Literal[RunType.workflow_run] = Field(description="Type of run - always workflow_run for workflow runs")
|
||||||
run_request: WorkflowRunRequest | None = None
|
run_request: WorkflowRunRequest | None = Field(
|
||||||
|
default=None, description="The original request parameters used to start this workflow run"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
RunResponse = Annotated[Union[TaskRunResponse, WorkflowRunResponse], Field(discriminator="run_type")]
|
RunResponse = Annotated[Union[TaskRunResponse, WorkflowRunResponse], Field(discriminator="run_type")]
|
||||||
|
|||||||
@@ -101,16 +101,16 @@ class PersistentSessionsManager:
|
|||||||
await self.database.mark_persistent_browser_session_deleted(session_id, organization_id)
|
await self.database.mark_persistent_browser_session_deleted(session_id, organization_id)
|
||||||
self._browser_sessions.pop(session_id, None)
|
self._browser_sessions.pop(session_id, None)
|
||||||
|
|
||||||
async def close_session(self, organization_id: str, session_id: str) -> None:
|
async def close_session(self, organization_id: str, browser_session_id: str) -> None:
|
||||||
"""Close a specific browser session."""
|
"""Close a specific browser session."""
|
||||||
browser_session = self._browser_sessions.get(session_id)
|
browser_session = self._browser_sessions.get(browser_session_id)
|
||||||
if browser_session:
|
if browser_session:
|
||||||
LOG.info(
|
LOG.info(
|
||||||
"Closing browser session",
|
"Closing browser session",
|
||||||
organization_id=organization_id,
|
organization_id=organization_id,
|
||||||
session_id=session_id,
|
session_id=browser_session_id,
|
||||||
)
|
)
|
||||||
self._browser_sessions.pop(session_id, None)
|
self._browser_sessions.pop(browser_session_id, None)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
await browser_session.browser_state.close()
|
await browser_session.browser_state.close()
|
||||||
@@ -118,23 +118,23 @@ class PersistentSessionsManager:
|
|||||||
LOG.info(
|
LOG.info(
|
||||||
"Browser context already closed",
|
"Browser context already closed",
|
||||||
organization_id=organization_id,
|
organization_id=organization_id,
|
||||||
session_id=session_id,
|
session_id=browser_session_id,
|
||||||
)
|
)
|
||||||
except Exception:
|
except Exception:
|
||||||
LOG.warning(
|
LOG.warning(
|
||||||
"Error while closing browser session",
|
"Error while closing browser session",
|
||||||
organization_id=organization_id,
|
organization_id=organization_id,
|
||||||
session_id=session_id,
|
session_id=browser_session_id,
|
||||||
exc_info=True,
|
exc_info=True,
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
LOG.info(
|
LOG.info(
|
||||||
"Browser session not found in memory, marking as deleted in database",
|
"Browser session not found in memory, marking as deleted in database",
|
||||||
organization_id=organization_id,
|
organization_id=organization_id,
|
||||||
session_id=session_id,
|
session_id=browser_session_id,
|
||||||
)
|
)
|
||||||
|
|
||||||
await self.database.mark_persistent_browser_session_deleted(session_id, organization_id)
|
await self.database.mark_persistent_browser_session_deleted(browser_session_id, organization_id)
|
||||||
|
|
||||||
async def close_all_sessions(self, organization_id: str) -> None:
|
async def close_all_sessions(self, organization_id: str) -> None:
|
||||||
"""Close all browser sessions for an organization."""
|
"""Close all browser sessions for an organization."""
|
||||||
|
|||||||
@@ -2,24 +2,37 @@ from __future__ import annotations
|
|||||||
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel, Field
|
||||||
|
|
||||||
from skyvern.forge.sdk.schemas.persistent_browser_sessions import PersistentBrowserSession
|
from skyvern.forge.sdk.schemas.persistent_browser_sessions import PersistentBrowserSession
|
||||||
|
|
||||||
|
|
||||||
class BrowserSessionResponse(BaseModel):
|
class BrowserSessionResponse(BaseModel):
|
||||||
session_id: str
|
"""Response model for browser session information."""
|
||||||
organization_id: str
|
|
||||||
runnable_type: str | None = None
|
browser_session_id: str = Field(description="Unique identifier for the browser session")
|
||||||
runnable_id: str | None = None
|
organization_id: str = Field(description="ID of the organization that owns this session")
|
||||||
created_at: datetime
|
runnable_type: str | None = Field(
|
||||||
modified_at: datetime
|
None, description="Type of runnable associated with this session (workflow, task etc)"
|
||||||
deleted_at: datetime | None = None
|
)
|
||||||
|
runnable_id: str | None = Field(None, description="ID of the associated runnable")
|
||||||
|
created_at: datetime = Field(description="Timestamp when the session was created")
|
||||||
|
modified_at: datetime = Field(description="Timestamp when the session was last modified")
|
||||||
|
deleted_at: datetime | None = Field(None, description="Timestamp when the session was deleted, if applicable")
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_browser_session(cls, browser_session: PersistentBrowserSession) -> BrowserSessionResponse:
|
def from_browser_session(cls, browser_session: PersistentBrowserSession) -> BrowserSessionResponse:
|
||||||
|
"""
|
||||||
|
Creates a BrowserSessionResponse from a PersistentBrowserSession object.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
browser_session: The persistent browser session to convert
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
BrowserSessionResponse: The converted response object
|
||||||
|
"""
|
||||||
return cls(
|
return cls(
|
||||||
session_id=browser_session.persistent_browser_session_id,
|
browser_session_id=browser_session.persistent_browser_session_id,
|
||||||
organization_id=browser_session.organization_id,
|
organization_id=browser_session.organization_id,
|
||||||
runnable_type=browser_session.runnable_type,
|
runnable_type=browser_session.runnable_type,
|
||||||
runnable_id=browser_session.runnable_id,
|
runnable_id=browser_session.runnable_id,
|
||||||
|
|||||||
Reference in New Issue
Block a user