shu/removeSettingsManager.get_settings (#1305)

This commit is contained in:
Shuchang Zheng
2024-12-02 15:01:22 -08:00
committed by GitHub
parent 29aa621296
commit 7f6b2c0929
23 changed files with 165 additions and 194 deletions

View File

@@ -6,7 +6,7 @@ from typing import Any, Dict
import typer import typer
from posthog import Posthog from posthog import Posthog
from skyvern.forge.sdk.settings_manager import SettingsManager from skyvern.config import settings
posthog = Posthog( posthog = Posthog(
"phc_bVT2ugnZhMHRWqMvSRHPdeTjaPxQqT3QSsI3r5FlQR5", "phc_bVT2ugnZhMHRWqMvSRHPdeTjaPxQqT3QSsI3r5FlQR5",
@@ -31,7 +31,7 @@ def analytics_metadata() -> Dict[str, Any]:
"machine": platform.machine(), "machine": platform.machine(),
"platform": platform.platform(), "platform": platform.platform(),
"python_version": platform.python_version(), "python_version": platform.python_version(),
"environment": SettingsManager.get_settings().ENV, "environment": settings.ENV,
} }
@@ -40,10 +40,10 @@ def capture(
data: dict[str, Any] | None = None, data: dict[str, Any] | None = None,
) -> None: ) -> None:
# If telemetry is disabled, don't send any data # If telemetry is disabled, don't send any data
if not SettingsManager.get_settings().SKYVERN_TELEMETRY: if not settings.SKYVERN_TELEMETRY:
return return
distinct_id = SettingsManager.get_settings().ANALYTICS_ID distinct_id = settings.ANALYTICS_ID
payload: dict[str, Any] = data or {} payload: dict[str, Any] = data or {}
try: try:

View File

@@ -103,6 +103,7 @@ class Settings(BaseSettings):
ENABLE_AZURE: bool = False ENABLE_AZURE: bool = False
ENABLE_AZURE_GPT4O_MINI: bool = False ENABLE_AZURE_GPT4O_MINI: bool = False
ENABLE_BEDROCK: bool = False ENABLE_BEDROCK: bool = False
ENABLE_GEMINI: bool = False
# OPENAI # OPENAI
OPENAI_API_KEY: str | None = None OPENAI_API_KEY: str | None = None
# ANTHROPIC # ANTHROPIC
@@ -119,6 +120,9 @@ class Settings(BaseSettings):
AZURE_GPT4O_MINI_API_BASE: str | None = None AZURE_GPT4O_MINI_API_BASE: str | None = None
AZURE_GPT4O_MINI_API_VERSION: str | None = None AZURE_GPT4O_MINI_API_VERSION: str | None = None
# GEMINI
GEMINI_API_KEY: str | None = None
# TOTP Settings # TOTP Settings
TOTP_LIFESPAN_MINUTES: int = 10 TOTP_LIFESPAN_MINUTES: int = 10
VERIFICATION_CODE_INITIAL_WAIT_TIME_SECS: int = 40 VERIFICATION_CODE_INITIAL_WAIT_TIME_SECS: int = 40

View File

@@ -3,18 +3,18 @@ import uvicorn
from dotenv import load_dotenv from dotenv import load_dotenv
from skyvern import analytics from skyvern import analytics
from skyvern.forge.sdk.settings_manager import SettingsManager from skyvern.config import settings
LOG = structlog.stdlib.get_logger() LOG = structlog.stdlib.get_logger()
if __name__ == "__main__": if __name__ == "__main__":
analytics.capture("skyvern-oss-run-server") analytics.capture("skyvern-oss-run-server")
port = SettingsManager.get_settings().PORT port = settings.PORT
LOG.info("Agent server starting.", host="0.0.0.0", port=port) LOG.info("Agent server starting.", host="0.0.0.0", port=port)
load_dotenv() load_dotenv()
reload = SettingsManager.get_settings().ENV == "local" reload = settings.ENV == "local"
uvicorn.run( uvicorn.run(
"skyvern.forge.api_app:app", "skyvern.forge.api_app:app",
host="0.0.0.0", host="0.0.0.0",

View File

@@ -14,6 +14,7 @@ from playwright._impl._errors import TargetClosedError
from playwright.async_api import Page from playwright.async_api import Page
from skyvern import analytics from skyvern import analytics
from skyvern.config import settings
from skyvern.constants import ( from skyvern.constants import (
GET_DOWNLOADED_FILES_TIMEOUT, GET_DOWNLOADED_FILES_TIMEOUT,
SAVE_DOWNLOADED_FILES_TIMEOUT, SAVE_DOWNLOADED_FILES_TIMEOUT,
@@ -51,7 +52,6 @@ from skyvern.forge.sdk.core.validators import validate_url
from skyvern.forge.sdk.db.enums import TaskType from skyvern.forge.sdk.db.enums import TaskType
from skyvern.forge.sdk.models import Organization, Step, StepStatus from skyvern.forge.sdk.models import Organization, Step, StepStatus
from skyvern.forge.sdk.schemas.tasks import Task, TaskRequest, TaskStatus from skyvern.forge.sdk.schemas.tasks import Task, TaskRequest, TaskStatus
from skyvern.forge.sdk.settings_manager import SettingsManager
from skyvern.forge.sdk.workflow.context_manager import WorkflowRunContext from skyvern.forge.sdk.workflow.context_manager import WorkflowRunContext
from skyvern.forge.sdk.workflow.models.block import ActionBlock, BaseTaskBlock, ValidationBlock from skyvern.forge.sdk.workflow.models.block import ActionBlock, BaseTaskBlock, ValidationBlock
from skyvern.forge.sdk.workflow.models.workflow import Workflow, WorkflowRun, WorkflowRunStatus from skyvern.forge.sdk.workflow.models.workflow import Workflow, WorkflowRun, WorkflowRunStatus
@@ -84,25 +84,25 @@ class ActionLinkedNode:
class ForgeAgent: class ForgeAgent:
def __init__(self) -> None: def __init__(self) -> None:
if SettingsManager.get_settings().ADDITIONAL_MODULES: if settings.ADDITIONAL_MODULES:
for module in SettingsManager.get_settings().ADDITIONAL_MODULES: for module in settings.ADDITIONAL_MODULES:
LOG.info("Loading additional module", module=module) LOG.info("Loading additional module", module=module)
__import__(module) __import__(module)
LOG.info( LOG.info(
"Additional modules loaded", "Additional modules loaded",
modules=SettingsManager.get_settings().ADDITIONAL_MODULES, modules=settings.ADDITIONAL_MODULES,
) )
LOG.info( LOG.info(
"Initializing ForgeAgent", "Initializing ForgeAgent",
env=SettingsManager.get_settings().ENV, env=settings.ENV,
execute_all_steps=SettingsManager.get_settings().EXECUTE_ALL_STEPS, execute_all_steps=settings.EXECUTE_ALL_STEPS,
browser_type=SettingsManager.get_settings().BROWSER_TYPE, browser_type=settings.BROWSER_TYPE,
max_scraping_retries=SettingsManager.get_settings().MAX_SCRAPING_RETRIES, max_scraping_retries=settings.MAX_SCRAPING_RETRIES,
video_path=SettingsManager.get_settings().VIDEO_PATH, video_path=settings.VIDEO_PATH,
browser_action_timeout_ms=SettingsManager.get_settings().BROWSER_ACTION_TIMEOUT_MS, browser_action_timeout_ms=settings.BROWSER_ACTION_TIMEOUT_MS,
max_steps_per_run=SettingsManager.get_settings().MAX_STEPS_PER_RUN, max_steps_per_run=settings.MAX_STEPS_PER_RUN,
long_running_task_warning_ratio=SettingsManager.get_settings().LONG_RUNNING_TASK_WARNING_RATIO, long_running_task_warning_ratio=settings.LONG_RUNNING_TASK_WARNING_RATIO,
debug_mode=SettingsManager.get_settings().DEBUG_MODE, debug_mode=settings.DEBUG_MODE,
) )
self.async_operation_pool = AsyncOperationPool() self.async_operation_pool = AsyncOperationPool()
@@ -290,7 +290,7 @@ class ForgeAgent:
override_max_steps_per_run override_max_steps_per_run
or task.max_steps_per_run or task.max_steps_per_run
or organization.max_steps_per_run or organization.max_steps_per_run
or SettingsManager.get_settings().MAX_STEPS_PER_RUN or settings.MAX_STEPS_PER_RUN
) )
if max_steps_per_run and task.max_steps_per_run != max_steps_per_run: if max_steps_per_run and task.max_steps_per_run != max_steps_per_run:
await app.DATABASE.update_task( await app.DATABASE.update_task(
@@ -423,7 +423,7 @@ class ForgeAgent:
close_browser_on_completion=close_browser_on_completion, close_browser_on_completion=close_browser_on_completion,
task_block=task_block, task_block=task_block,
) )
elif SettingsManager.get_settings().execute_all_steps() and next_step: elif settings.execute_all_steps() and next_step:
return await self.execute_step( return await self.execute_step(
organization, organization,
task, task,
@@ -437,8 +437,8 @@ class ForgeAgent:
"Step executed but continuous execution is disabled.", "Step executed but continuous execution is disabled.",
task_id=task.task_id, task_id=task.task_id,
step_id=step.step_id, step_id=step.step_id,
is_cloud_env=SettingsManager.get_settings().is_cloud_environment(), is_cloud_env=settings.is_cloud_environment(),
execute_all_steps=SettingsManager.get_settings().execute_all_steps(), execute_all_steps=settings.execute_all_steps(),
next_step_id=next_step.step_id if next_step else None, next_step_id=next_step.step_id if next_step else None,
) )
@@ -1342,7 +1342,7 @@ class ForgeAgent:
# Get action results from the last app.SETTINGS.PROMPT_ACTION_HISTORY_WINDOW steps # Get action results from the last app.SETTINGS.PROMPT_ACTION_HISTORY_WINDOW steps
steps = await app.DATABASE.get_task_steps(task_id=task.task_id, organization_id=task.organization_id) steps = await app.DATABASE.get_task_steps(task_id=task.task_id, organization_id=task.organization_id)
# the last step is always the newly created one and it should be excluded from the history window # the last step is always the newly created one and it should be excluded from the history window
window_steps = steps[-1 - SettingsManager.get_settings().PROMPT_ACTION_HISTORY_WINDOW : -1] window_steps = steps[-1 - settings.PROMPT_ACTION_HISTORY_WINDOW : -1]
actions_and_results: list[tuple[Action, list[ActionResult]]] = [] actions_and_results: list[tuple[Action, list[ActionResult]]] = []
for window_step in window_steps: for window_step in window_steps:
if window_step.output and window_step.output.actions_and_results: if window_step.output and window_step.output.actions_and_results:
@@ -1576,7 +1576,7 @@ class ForgeAgent:
task_id=task.task_id, task_id=task.task_id,
organization_id=task.organization_id, organization_id=task.organization_id,
artifact_types=[ArtifactType.SCREENSHOT_ACTION], artifact_types=[ArtifactType.SCREENSHOT_ACTION],
n=SettingsManager.get_settings().TASK_RESPONSE_ACTION_SCREENSHOT_COUNT, n=settings.TASK_RESPONSE_ACTION_SCREENSHOT_COUNT,
) )
if latest_action_screenshot_artifacts: if latest_action_screenshot_artifacts:
latest_action_screenshot_urls = await app.ARTIFACT_MANAGER.get_share_links( latest_action_screenshot_urls = await app.ARTIFACT_MANAGER.get_share_links(
@@ -1790,7 +1790,7 @@ class ForgeAgent:
organization.max_retries_per_step organization.max_retries_per_step
# we need to check by None because 0 is a valid value for max_retries_per_step # we need to check by None because 0 is a valid value for max_retries_per_step
if organization.max_retries_per_step is not None if organization.max_retries_per_step is not None
else SettingsManager.get_settings().MAX_RETRIES_PER_STEP else settings.MAX_RETRIES_PER_STEP
) )
if step.retry_index >= max_retries_per_step: if step.retry_index >= max_retries_per_step:
LOG.warning( LOG.warning(
@@ -1799,7 +1799,7 @@ class ForgeAgent:
step_id=step.step_id, step_id=step.step_id,
step_order=step.order, step_order=step.order,
step_retry=step.retry_index, step_retry=step.retry_index,
max_retries=SettingsManager.get_settings().MAX_RETRIES_PER_STEP, max_retries=settings.MAX_RETRIES_PER_STEP,
) )
await self.update_task( await self.update_task(
task, task,
@@ -1923,7 +1923,7 @@ class ForgeAgent:
override_max_steps_per_run override_max_steps_per_run
or task.max_steps_per_run or task.max_steps_per_run
or organization.max_steps_per_run or organization.max_steps_per_run
or SettingsManager.get_settings().MAX_STEPS_PER_RUN or settings.MAX_STEPS_PER_RUN
) )
# HACK: action block only have one step to execute without complete action, so we consider the task is completed as long as the step is completed # HACK: action block only have one step to execute without complete action, so we consider the task is completed as long as the step is completed
@@ -1985,14 +1985,12 @@ class ForgeAgent:
organization_id=task.organization_id, organization_id=task.organization_id,
) )
if step.order == int( if step.order == int(max_steps_per_run * settings.LONG_RUNNING_TASK_WARNING_RATIO - 1):
max_steps_per_run * SettingsManager.get_settings().LONG_RUNNING_TASK_WARNING_RATIO - 1
):
LOG.info( LOG.info(
"Long running task warning", "Long running task warning",
order=step.order, order=step.order,
max_steps=max_steps_per_run, max_steps=max_steps_per_run,
warning_ratio=SettingsManager.get_settings().LONG_RUNNING_TASK_WARNING_RATIO, warning_ratio=settings.LONG_RUNNING_TASK_WARNING_RATIO,
) )
return None, None, next_step return None, None, next_step

View File

@@ -11,6 +11,7 @@ from starlette.requests import HTTPConnection, Request
from starlette_context.middleware import RawContextMiddleware from starlette_context.middleware import RawContextMiddleware
from starlette_context.plugins.base import Plugin from starlette_context.plugins.base import Plugin
from skyvern.config import settings
from skyvern.exceptions import SkyvernHTTPException from skyvern.exceptions import SkyvernHTTPException
from skyvern.forge import app as forge_app from skyvern.forge import app as forge_app
from skyvern.forge.sdk.core import skyvern_context from skyvern.forge.sdk.core import skyvern_context
@@ -18,7 +19,6 @@ 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 from skyvern.forge.sdk.routes.agent_protocol import base_router
from skyvern.forge.sdk.routes.streaming import websocket_router from skyvern.forge.sdk.routes.streaming import websocket_router
from skyvern.forge.sdk.settings_manager import SettingsManager
LOG = structlog.get_logger() LOG = structlog.get_logger()
@@ -40,7 +40,7 @@ def get_agent_app() -> FastAPI:
# Add CORS middleware # Add CORS middleware
app.add_middleware( app.add_middleware(
CORSMiddleware, CORSMiddleware,
allow_origins=SettingsManager.get_settings().ALLOWED_ORIGINS, allow_origins=settings.ALLOWED_ORIGINS,
allow_credentials=True, allow_credentials=True,
allow_methods=["*"], allow_methods=["*"],
allow_headers=["*"], allow_headers=["*"],
@@ -90,13 +90,13 @@ def get_agent_app() -> FastAPI:
finally: finally:
skyvern_context.reset() skyvern_context.reset()
if SettingsManager.get_settings().ADDITIONAL_MODULES: if settings.ADDITIONAL_MODULES:
for module in SettingsManager.get_settings().ADDITIONAL_MODULES: for module in settings.ADDITIONAL_MODULES:
LOG.info("Loading additional module to set up api app", module=module) LOG.info("Loading additional module to set up api app", module=module)
__import__(module) __import__(module)
LOG.info( LOG.info(
"Additional modules loaded to set up api app", "Additional modules loaded to set up api app",
modules=SettingsManager.get_settings().ADDITIONAL_MODULES, modules=settings.ADDITIONAL_MODULES,
) )
if forge_app.setup_api_app: if forge_app.setup_api_app:

View File

@@ -2,6 +2,7 @@ from typing import Awaitable, Callable
from fastapi import FastAPI from fastapi import FastAPI
from skyvern.config import settings
from skyvern.forge.agent import ForgeAgent from skyvern.forge.agent import ForgeAgent
from skyvern.forge.agent_functions import AgentFunction from skyvern.forge.agent_functions import AgentFunction
from skyvern.forge.sdk.api.llm.api_handler_factory import LLMAPIHandlerFactory from skyvern.forge.sdk.api.llm.api_handler_factory import LLMAPIHandlerFactory
@@ -12,25 +13,24 @@ from skyvern.forge.sdk.cache.factory import CacheFactory
from skyvern.forge.sdk.db.client import AgentDB from skyvern.forge.sdk.db.client import AgentDB
from skyvern.forge.sdk.experimentation.providers import BaseExperimentationProvider, NoOpExperimentationProvider from skyvern.forge.sdk.experimentation.providers import BaseExperimentationProvider, NoOpExperimentationProvider
from skyvern.forge.sdk.models import Organization from skyvern.forge.sdk.models import Organization
from skyvern.forge.sdk.settings_manager import SettingsManager
from skyvern.forge.sdk.workflow.context_manager import WorkflowContextManager from skyvern.forge.sdk.workflow.context_manager import WorkflowContextManager
from skyvern.forge.sdk.workflow.service import WorkflowService from skyvern.forge.sdk.workflow.service import WorkflowService
from skyvern.webeye.browser_manager import BrowserManager from skyvern.webeye.browser_manager import BrowserManager
from skyvern.webeye.scraper.scraper import ScrapeExcludeFunc from skyvern.webeye.scraper.scraper import ScrapeExcludeFunc
SETTINGS_MANAGER = SettingsManager.get_settings() SETTINGS_MANAGER = settings
DATABASE = AgentDB( DATABASE = AgentDB(
SettingsManager.get_settings().DATABASE_STRING, settings.DATABASE_STRING,
debug_enabled=SettingsManager.get_settings().DEBUG_MODE, debug_enabled=settings.DEBUG_MODE,
) )
if SettingsManager.get_settings().SKYVERN_STORAGE_TYPE == "s3": if settings.SKYVERN_STORAGE_TYPE == "s3":
StorageFactory.set_storage(S3Storage()) StorageFactory.set_storage(S3Storage())
STORAGE = StorageFactory.get_storage() STORAGE = StorageFactory.get_storage()
CACHE = CacheFactory.get_cache() CACHE = CacheFactory.get_cache()
ARTIFACT_MANAGER = ArtifactManager() ARTIFACT_MANAGER = ArtifactManager()
BROWSER_MANAGER = BrowserManager() BROWSER_MANAGER = BrowserManager()
EXPERIMENTATION_PROVIDER: BaseExperimentationProvider = NoOpExperimentationProvider() EXPERIMENTATION_PROVIDER: BaseExperimentationProvider = NoOpExperimentationProvider()
LLM_API_HANDLER = LLMAPIHandlerFactory.get_llm_api_handler(SettingsManager.get_settings().LLM_KEY) LLM_API_HANDLER = LLMAPIHandlerFactory.get_llm_api_handler(settings.LLM_KEY)
SECONDARY_LLM_API_HANDLER = LLMAPIHandlerFactory.get_llm_api_handler( SECONDARY_LLM_API_HANDLER = LLMAPIHandlerFactory.get_llm_api_handler(
SETTINGS_MANAGER.SECONDARY_LLM_KEY if SETTINGS_MANAGER.SECONDARY_LLM_KEY else SETTINGS_MANAGER.LLM_KEY SETTINGS_MANAGER.SECONDARY_LLM_KEY if SETTINGS_MANAGER.SECONDARY_LLM_KEY else SETTINGS_MANAGER.LLM_KEY
) )

View File

@@ -6,7 +6,7 @@ import aioboto3
import structlog import structlog
from aiobotocore.client import AioBaseClient from aiobotocore.client import AioBaseClient
from skyvern.forge.sdk.settings_manager import SettingsManager from skyvern.config import settings
LOG = structlog.get_logger() LOG = structlog.get_logger()
@@ -22,7 +22,7 @@ def execute_with_async_client(client_type: AWSClientType) -> Callable:
self = args[0] self = args[0]
assert isinstance(self, AsyncAWSClient) assert isinstance(self, AsyncAWSClient)
session = aioboto3.Session() session = aioboto3.Session()
async with session.client(client_type, region_name=SettingsManager.get_settings().AWS_REGION) as client: async with session.client(client_type, region_name=settings.AWS_REGION) as client:
return await f(*args, client=client, **kwargs) return await f(*args, client=client, **kwargs)
return wrapper return wrapper
@@ -95,7 +95,7 @@ class AsyncAWSClient:
url = await client.generate_presigned_url( url = await client.generate_presigned_url(
"get_object", "get_object",
Params={"Bucket": parsed_uri.bucket, "Key": parsed_uri.key}, Params={"Bucket": parsed_uri.bucket, "Key": parsed_uri.key},
ExpiresIn=SettingsManager.get_settings().PRESIGNED_URL_EXPIRATION, ExpiresIn=settings.PRESIGNED_URL_EXPIRATION,
) )
presigned_urls.append(url) presigned_urls.append(url)

View File

@@ -12,10 +12,10 @@ import aiohttp
import structlog import structlog
from multidict import CIMultiDictProxy from multidict import CIMultiDictProxy
from skyvern.config import settings
from skyvern.constants import REPO_ROOT_DIR from skyvern.constants import REPO_ROOT_DIR
from skyvern.exceptions import DownloadFileMaxSizeExceeded from skyvern.exceptions import DownloadFileMaxSizeExceeded
from skyvern.forge.sdk.api.aws import AsyncAWSClient from skyvern.forge.sdk.api.aws import AsyncAWSClient
from skyvern.forge.sdk.settings_manager import SettingsManager
LOG = structlog.get_logger() LOG = structlog.get_logger()
@@ -169,7 +169,7 @@ def create_folder_if_not_exist(dir: str) -> None:
def get_skyvern_temp_dir() -> str: def get_skyvern_temp_dir() -> str:
temp_dir = SettingsManager.get_settings().TEMP_PATH temp_dir = settings.TEMP_PATH
create_folder_if_not_exist(temp_dir) create_folder_if_not_exist(temp_dir)
return temp_dir return temp_dir
@@ -178,13 +178,13 @@ def make_temp_directory(
suffix: str | None = None, suffix: str | None = None,
prefix: str | None = None, prefix: str | None = None,
) -> str: ) -> str:
temp_dir = SettingsManager.get_settings().TEMP_PATH temp_dir = settings.TEMP_PATH
create_folder_if_not_exist(temp_dir) create_folder_if_not_exist(temp_dir)
return tempfile.mkdtemp(suffix=suffix, prefix=prefix, dir=temp_dir) return tempfile.mkdtemp(suffix=suffix, prefix=prefix, dir=temp_dir)
def create_named_temporary_file(delete: bool = True) -> tempfile._TemporaryFileWrapper: def create_named_temporary_file(delete: bool = True) -> tempfile._TemporaryFileWrapper:
temp_dir = SettingsManager.get_settings().TEMP_PATH temp_dir = settings.TEMP_PATH
create_folder_if_not_exist(temp_dir) create_folder_if_not_exist(temp_dir)
return tempfile.NamedTemporaryFile(dir=temp_dir, delete=delete) return tempfile.NamedTemporaryFile(dir=temp_dir, delete=delete)

View File

@@ -7,6 +7,7 @@ from typing import Any
import litellm import litellm
import structlog import structlog
from skyvern.config import settings
from skyvern.forge import app from skyvern.forge import app
from skyvern.forge.sdk.api.llm.config_registry import LLMConfigRegistry from skyvern.forge.sdk.api.llm.config_registry import LLMConfigRegistry
from skyvern.forge.sdk.api.llm.exceptions import ( from skyvern.forge.sdk.api.llm.exceptions import (
@@ -19,7 +20,6 @@ from skyvern.forge.sdk.api.llm.models import LLMAPIHandler, LLMConfig, LLMRouter
from skyvern.forge.sdk.api.llm.utils import llm_messages_builder, parse_api_response from skyvern.forge.sdk.api.llm.utils import llm_messages_builder, parse_api_response
from skyvern.forge.sdk.artifact.models import ArtifactType from skyvern.forge.sdk.artifact.models import ArtifactType
from skyvern.forge.sdk.models import Step from skyvern.forge.sdk.models import Step
from skyvern.forge.sdk.settings_manager import SettingsManager
LOG = structlog.get_logger() LOG = structlog.get_logger()
@@ -50,7 +50,7 @@ class LLMAPIHandlerFactory:
allowed_fails=llm_config.allowed_fails, allowed_fails=llm_config.allowed_fails,
allowed_fails_policy=llm_config.allowed_fails_policy, allowed_fails_policy=llm_config.allowed_fails_policy,
cooldown_time=llm_config.cooldown_time, cooldown_time=llm_config.cooldown_time,
set_verbose=(False if SettingsManager.get_settings().is_cloud_environment() else llm_config.set_verbose), set_verbose=(False if settings.is_cloud_environment() else llm_config.set_verbose),
enable_pre_call_checks=True, enable_pre_call_checks=True,
) )
main_model_group = llm_config.main_model_group main_model_group = llm_config.main_model_group
@@ -213,7 +213,7 @@ class LLMAPIHandlerFactory:
response = await litellm.acompletion( response = await litellm.acompletion(
model=llm_config.model_name, model=llm_config.model_name,
messages=messages, messages=messages,
timeout=SettingsManager.get_settings().LLM_CONFIG_TIMEOUT, timeout=settings.LLM_CONFIG_TIMEOUT,
**active_parameters, **active_parameters,
) )
LOG.info("LLM API call successful", llm_key=llm_key, model=llm_config.model_name) LOG.info("LLM API call successful", llm_key=llm_key, model=llm_config.model_name)
@@ -263,7 +263,7 @@ class LLMAPIHandlerFactory:
def get_api_parameters(llm_config: LLMConfig | LLMRouterConfig) -> dict[str, Any]: def get_api_parameters(llm_config: LLMConfig | LLMRouterConfig) -> dict[str, Any]:
return { return {
"max_tokens": llm_config.max_output_tokens, "max_tokens": llm_config.max_output_tokens,
"temperature": SettingsManager.get_settings().LLM_CONFIG_TEMPERATURE, "temperature": settings.LLM_CONFIG_TEMPERATURE,
} }
@classmethod @classmethod

View File

@@ -1,5 +1,6 @@
import structlog import structlog
from skyvern.config import settings
from skyvern.forge.sdk.api.llm.exceptions import ( from skyvern.forge.sdk.api.llm.exceptions import (
DuplicateLLMConfigError, DuplicateLLMConfigError,
InvalidLLMConfigError, InvalidLLMConfigError,
@@ -7,7 +8,6 @@ from skyvern.forge.sdk.api.llm.exceptions import (
NoProviderEnabledError, NoProviderEnabledError,
) )
from skyvern.forge.sdk.api.llm.models import LiteLLMParams, LLMConfig, LLMRouterConfig from skyvern.forge.sdk.api.llm.models import LiteLLMParams, LLMConfig, LLMRouterConfig
from skyvern.forge.sdk.settings_manager import SettingsManager
LOG = structlog.get_logger() LOG = structlog.get_logger()
@@ -46,17 +46,17 @@ class LLMConfigRegistry:
# if none of the LLM providers are enabled, raise an error # if none of the LLM providers are enabled, raise an error
if not any( if not any(
[ [
SettingsManager.get_settings().ENABLE_OPENAI, settings.ENABLE_OPENAI,
SettingsManager.get_settings().ENABLE_ANTHROPIC, settings.ENABLE_ANTHROPIC,
SettingsManager.get_settings().ENABLE_AZURE, settings.ENABLE_AZURE,
SettingsManager.get_settings().ENABLE_AZURE_GPT4O_MINI, settings.ENABLE_AZURE_GPT4O_MINI,
SettingsManager.get_settings().ENABLE_BEDROCK, settings.ENABLE_BEDROCK,
] ]
): ):
raise NoProviderEnabledError() raise NoProviderEnabledError()
if SettingsManager.get_settings().ENABLE_OPENAI: if settings.ENABLE_OPENAI:
LLMConfigRegistry.register_config( LLMConfigRegistry.register_config(
"OPENAI_GPT4_TURBO", "OPENAI_GPT4_TURBO",
LLMConfig( LLMConfig(
@@ -103,7 +103,7 @@ if SettingsManager.get_settings().ENABLE_OPENAI:
) )
if SettingsManager.get_settings().ENABLE_ANTHROPIC: if settings.ENABLE_ANTHROPIC:
LLMConfigRegistry.register_config( LLMConfigRegistry.register_config(
"ANTHROPIC_CLAUDE3", "ANTHROPIC_CLAUDE3",
LLMConfig( LLMConfig(
@@ -151,7 +151,7 @@ if SettingsManager.get_settings().ENABLE_ANTHROPIC:
), ),
) )
if SettingsManager.get_settings().ENABLE_BEDROCK: if settings.ENABLE_BEDROCK:
# Supported through AWS IAM authentication # Supported through AWS IAM authentication
LLMConfigRegistry.register_config( LLMConfigRegistry.register_config(
"BEDROCK_ANTHROPIC_CLAUDE3_OPUS", "BEDROCK_ANTHROPIC_CLAUDE3_OPUS",
@@ -209,11 +209,11 @@ if SettingsManager.get_settings().ENABLE_BEDROCK:
) )
if SettingsManager.get_settings().ENABLE_AZURE: if settings.ENABLE_AZURE:
LLMConfigRegistry.register_config( LLMConfigRegistry.register_config(
"AZURE_OPENAI", "AZURE_OPENAI",
LLMConfig( LLMConfig(
f"azure/{SettingsManager.get_settings().AZURE_DEPLOYMENT}", f"azure/{settings.AZURE_DEPLOYMENT}",
[ [
"AZURE_DEPLOYMENT", "AZURE_DEPLOYMENT",
"AZURE_API_KEY", "AZURE_API_KEY",
@@ -225,11 +225,11 @@ if SettingsManager.get_settings().ENABLE_AZURE:
), ),
) )
if SettingsManager.get_settings().ENABLE_AZURE_GPT4O_MINI: if settings.ENABLE_AZURE_GPT4O_MINI:
LLMConfigRegistry.register_config( LLMConfigRegistry.register_config(
"AZURE_OPENAI_GPT4O_MINI", "AZURE_OPENAI_GPT4O_MINI",
LLMConfig( LLMConfig(
f"azure/{SettingsManager.get_settings().AZURE_GPT4O_MINI_DEPLOYMENT}", f"azure/{settings.AZURE_GPT4O_MINI_DEPLOYMENT}",
[ [
"AZURE_GPT4O_MINI_DEPLOYMENT", "AZURE_GPT4O_MINI_DEPLOYMENT",
"AZURE_GPT4O_MINI_API_KEY", "AZURE_GPT4O_MINI_API_KEY",
@@ -237,9 +237,9 @@ if SettingsManager.get_settings().ENABLE_AZURE_GPT4O_MINI:
"AZURE_GPT4O_MINI_API_VERSION", "AZURE_GPT4O_MINI_API_VERSION",
], ],
litellm_params=LiteLLMParams( litellm_params=LiteLLMParams(
api_base=SettingsManager.get_settings().AZURE_GPT4O_MINI_API_BASE, api_base=settings.AZURE_GPT4O_MINI_API_BASE,
api_key=SettingsManager.get_settings().AZURE_GPT4O_MINI_API_KEY, api_key=settings.AZURE_GPT4O_MINI_API_KEY,
api_version=SettingsManager.get_settings().AZURE_GPT4O_MINI_API_VERSION, api_version=settings.AZURE_GPT4O_MINI_API_VERSION,
model_info={"model_name": "azure/gpt-4o-mini"}, model_info={"model_name": "azure/gpt-4o-mini"},
), ),
supports_vision=True, supports_vision=True,

View File

@@ -6,17 +6,17 @@ from urllib.parse import unquote, urlparse
import structlog import structlog
from skyvern.config import settings
from skyvern.forge.sdk.api.files import get_download_dir, get_skyvern_temp_dir from skyvern.forge.sdk.api.files import get_download_dir, get_skyvern_temp_dir
from skyvern.forge.sdk.artifact.models import Artifact, ArtifactType from skyvern.forge.sdk.artifact.models import Artifact, ArtifactType
from skyvern.forge.sdk.artifact.storage.base import FILE_EXTENTSION_MAP, BaseStorage from skyvern.forge.sdk.artifact.storage.base import FILE_EXTENTSION_MAP, BaseStorage
from skyvern.forge.sdk.models import Step from skyvern.forge.sdk.models import Step
from skyvern.forge.sdk.settings_manager import SettingsManager
LOG = structlog.get_logger() LOG = structlog.get_logger()
class LocalStorage(BaseStorage): class LocalStorage(BaseStorage):
def __init__(self, artifact_path: str = SettingsManager.get_settings().ARTIFACT_STORAGE_PATH) -> None: def __init__(self, artifact_path: str = settings.ARTIFACT_STORAGE_PATH) -> None:
self.artifact_path = artifact_path self.artifact_path = artifact_path
def build_uri(self, artifact_id: str, step: Step, artifact_type: ArtifactType) -> str: def build_uri(self, artifact_id: str, step: Step, artifact_type: ArtifactType) -> str:
@@ -84,9 +84,7 @@ class LocalStorage(BaseStorage):
return None return None
async def store_browser_session(self, organization_id: str, workflow_permanent_id: str, directory: str) -> None: async def store_browser_session(self, organization_id: str, workflow_permanent_id: str, directory: str) -> None:
stored_folder_path = ( stored_folder_path = Path(settings.BROWSER_SESSION_BASE_PATH) / organization_id / workflow_permanent_id
Path(SettingsManager.get_settings().BROWSER_SESSION_BASE_PATH) / organization_id / workflow_permanent_id
)
if directory == str(stored_folder_path): if directory == str(stored_folder_path):
return return
self._create_directories_if_not_exists(stored_folder_path) self._create_directories_if_not_exists(stored_folder_path)
@@ -108,9 +106,7 @@ class LocalStorage(BaseStorage):
shutil.copy2(source_file_path, target_file_path) shutil.copy2(source_file_path, target_file_path)
async def retrieve_browser_session(self, organization_id: str, workflow_permanent_id: str) -> str | None: async def retrieve_browser_session(self, organization_id: str, workflow_permanent_id: str) -> str | None:
stored_folder_path = ( stored_folder_path = Path(settings.BROWSER_SESSION_BASE_PATH) / organization_id / workflow_permanent_id
Path(SettingsManager.get_settings().BROWSER_SESSION_BASE_PATH) / organization_id / workflow_permanent_id
)
if not stored_folder_path.exists(): if not stored_folder_path.exists():
return None return None
return str(stored_folder_path) return str(stored_folder_path)

View File

@@ -5,7 +5,7 @@ from typing import Any, Union
from jose import jwt from jose import jwt
from skyvern.forge.sdk.settings_manager import SettingsManager from skyvern.config import settings
def create_access_token( def create_access_token(
@@ -16,13 +16,13 @@ def create_access_token(
expire = datetime.utcnow() + expires_delta expire = datetime.utcnow() + expires_delta
else: else:
expire = datetime.utcnow() + timedelta( expire = datetime.utcnow() + timedelta(
minutes=SettingsManager.get_settings().ACCESS_TOKEN_EXPIRE_MINUTES, minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES,
) )
to_encode = {"exp": expire, "sub": str(subject)} to_encode = {"exp": expire, "sub": str(subject)}
encoded_jwt = jwt.encode( encoded_jwt = jwt.encode(
to_encode, to_encode,
SettingsManager.get_settings().SECRET_KEY, settings.SECRET_KEY,
algorithm=SettingsManager.get_settings().SIGNATURE_ALGORITHM, algorithm=settings.SIGNATURE_ALGORITHM,
) )
return encoded_jwt return encoded_jwt

View File

@@ -3,8 +3,8 @@ import logging
import structlog import structlog
from structlog.typing import EventDict from structlog.typing import EventDict
from skyvern.config import settings
from skyvern.forge.sdk.core import skyvern_context from skyvern.forge.sdk.core import skyvern_context
from skyvern.forge.sdk.settings_manager import SettingsManager
LOGGING_LEVEL_MAP: dict[str, int] = { LOGGING_LEVEL_MAP: dict[str, int] = {
"DEBUG": logging.DEBUG, "DEBUG": logging.DEBUG,
@@ -34,7 +34,7 @@ def add_kv_pairs_to_msg(logger: logging.Logger, method_name: str, event_dict: Ev
event_dict["workflow_run_id"] = context.workflow_run_id event_dict["workflow_run_id"] = context.workflow_run_id
# Add env to the log # Add env to the log
event_dict["env"] = SettingsManager.get_settings().ENV event_dict["env"] = settings.ENV
if method_name not in ["info", "warning", "error", "critical", "exception"]: if method_name not in ["info", "warning", "error", "critical", "exception"]:
# Only modify the log for these log levels # Only modify the log for these log levels
@@ -59,11 +59,7 @@ def setup_logger() -> None:
Setup the logger with the specified format Setup the logger with the specified format
""" """
# logging.config.dictConfig(logging_config) # logging.config.dictConfig(logging_config)
renderer = ( renderer = structlog.processors.JSONRenderer() if settings.JSON_LOGGING else structlog.dev.ConsoleRenderer()
structlog.processors.JSONRenderer()
if SettingsManager.get_settings().JSON_LOGGING
else structlog.dev.ConsoleRenderer()
)
additional_processors = ( additional_processors = (
[ [
structlog.processors.EventRenamer("msg"), structlog.processors.EventRenamer("msg"),
@@ -78,10 +74,10 @@ def setup_logger() -> None:
} }
), ),
] ]
if SettingsManager.get_settings().JSON_LOGGING if settings.JSON_LOGGING
else [] else []
) )
LOG_LEVEL_VAL = LOGGING_LEVEL_MAP.get(SettingsManager.get_settings().LOG_LEVEL, logging.INFO) LOG_LEVEL_VAL = LOGGING_LEVEL_MAP.get(settings.LOG_LEVEL, logging.INFO)
structlog.configure( structlog.configure(
wrapper_class=structlog.make_filtering_bound_logger(LOG_LEVEL_VAL), wrapper_class=structlog.make_filtering_bound_logger(LOG_LEVEL_VAL),

View File

@@ -22,6 +22,7 @@ from pydantic import BaseModel
from sqlalchemy.exc import OperationalError from sqlalchemy.exc import OperationalError
from skyvern import analytics from skyvern import analytics
from skyvern.config import settings
from skyvern.exceptions import StepNotFound from skyvern.exceptions import StepNotFound
from skyvern.forge import app from skyvern.forge import app
from skyvern.forge.prompts import prompt_engine from skyvern.forge.prompts import prompt_engine
@@ -50,7 +51,6 @@ from skyvern.forge.sdk.schemas.tasks import (
TaskStatus, TaskStatus,
) )
from skyvern.forge.sdk.services import org_auth_service from skyvern.forge.sdk.services import org_auth_service
from skyvern.forge.sdk.settings_manager import SettingsManager
from skyvern.forge.sdk.workflow.exceptions import ( from skyvern.forge.sdk.workflow.exceptions import (
FailedToCreateWorkflow, FailedToCreateWorkflow,
FailedToUpdateWorkflow, FailedToUpdateWorkflow,
@@ -95,7 +95,7 @@ async def webhook(
generated_signature = generate_skyvern_signature( generated_signature = generate_skyvern_signature(
payload.decode("utf-8"), payload.decode("utf-8"),
SettingsManager.get_settings().SKYVERN_API_KEY, settings.SKYVERN_API_KEY,
) )
LOG.info( LOG.info(
@@ -291,7 +291,7 @@ async def get_task(
task_id=task_obj.task_id, task_id=task_obj.task_id,
organization_id=task_obj.organization_id, organization_id=task_obj.organization_id,
artifact_types=[ArtifactType.SCREENSHOT_ACTION], artifact_types=[ArtifactType.SCREENSHOT_ACTION],
n=SettingsManager.get_settings().TASK_RESPONSE_ACTION_SCREENSHOT_COUNT, n=settings.TASK_RESPONSE_ACTION_SCREENSHOT_COUNT,
) )
latest_action_screenshot_urls: list[str] | None = None latest_action_screenshot_urls: list[str] | None = None
if latest_action_screenshot_artifacts: if latest_action_screenshot_artifacts:
@@ -532,7 +532,7 @@ async def get_agent_task_step_artifacts(
step_id, step_id,
organization_id=current_org.organization_id, organization_id=current_org.organization_id,
) )
if SettingsManager.get_settings().ENV != "local" or SettingsManager.get_settings().GENERATE_PRESIGNED_URLS: if settings.ENV != "local" or settings.GENERATE_PRESIGNED_URLS:
signed_urls = await app.ARTIFACT_MANAGER.get_share_links(artifacts) signed_urls = await app.ARTIFACT_MANAGER.get_share_links(artifacts)
if signed_urls: if signed_urls:
for i, artifact in enumerate(artifacts): for i, artifact in enumerate(artifacts):
@@ -863,7 +863,7 @@ async def generate_task(
# check if there's a same user_prompt within the past x Hours # check if there's a same user_prompt within the past x Hours
# in the future, we can use vector db to fetch similar prompts # in the future, we can use vector db to fetch similar prompts
existing_task_generation = await app.DATABASE.get_task_generation_by_prompt_hash( existing_task_generation = await app.DATABASE.get_task_generation_by_prompt_hash(
user_prompt_hash=user_prompt_hash, query_window_hours=SettingsManager.get_settings().PROMPT_CACHE_WINDOW_HOURS user_prompt_hash=user_prompt_hash, query_window_hours=settings.PROMPT_CACHE_WINDOW_HOURS
) )
if existing_task_generation: if existing_task_generation:
new_task_generation = await app.DATABASE.create_task_generation( new_task_generation = await app.DATABASE.create_task_generation(
@@ -898,7 +898,7 @@ async def generate_task(
data_extraction_goal=parsed_task_generation_obj.data_extraction_goal, data_extraction_goal=parsed_task_generation_obj.data_extraction_goal,
extracted_information_schema=parsed_task_generation_obj.extracted_information_schema, extracted_information_schema=parsed_task_generation_obj.extracted_information_schema,
suggested_title=parsed_task_generation_obj.suggested_title, suggested_title=parsed_task_generation_obj.suggested_title,
llm=SettingsManager.get_settings().LLM_KEY, llm=settings.LLM_KEY,
llm_prompt=llm_prompt, llm_prompt=llm_prompt,
llm_response=str(llm_response), llm_response=str(llm_response),
) )

View File

@@ -8,11 +8,11 @@ from jose import jwt
from jose.exceptions import JWTError from jose.exceptions import JWTError
from pydantic import ValidationError from pydantic import ValidationError
from skyvern.config import settings
from skyvern.forge import app from skyvern.forge import app
from skyvern.forge.sdk.core import skyvern_context from skyvern.forge.sdk.core import skyvern_context
from skyvern.forge.sdk.db.client import AgentDB from skyvern.forge.sdk.db.client import AgentDB
from skyvern.forge.sdk.models import Organization, OrganizationAuthTokenType, TokenPayload from skyvern.forge.sdk.models import Organization, OrganizationAuthTokenType, TokenPayload
from skyvern.forge.sdk.settings_manager import SettingsManager
AUTHENTICATION_TTL = 60 * 60 # one hour AUTHENTICATION_TTL = 60 * 60 # one hour
CACHE_SIZE = 128 CACHE_SIZE = 128
@@ -85,7 +85,7 @@ async def _get_current_org_cached(x_api_key: str, db: AgentDB) -> Organization:
try: try:
payload = jwt.decode( payload = jwt.decode(
x_api_key, x_api_key,
SettingsManager.get_settings().SECRET_KEY, settings.SECRET_KEY,
algorithms=[ALGORITHM], algorithms=[ALGORITHM],
) )
api_key_data = TokenPayload(**payload) api_key_data = TokenPayload(**payload)

View File

@@ -46,7 +46,6 @@ from skyvern.forge.sdk.api.files import (
from skyvern.forge.sdk.api.llm.api_handler_factory import LLMAPIHandlerFactory from skyvern.forge.sdk.api.llm.api_handler_factory import LLMAPIHandlerFactory
from skyvern.forge.sdk.db.enums import TaskType from skyvern.forge.sdk.db.enums import TaskType
from skyvern.forge.sdk.schemas.tasks import Task, TaskOutput, TaskStatus from skyvern.forge.sdk.schemas.tasks import Task, TaskOutput, TaskStatus
from skyvern.forge.sdk.settings_manager import SettingsManager
from skyvern.forge.sdk.workflow.context_manager import WorkflowRunContext from skyvern.forge.sdk.workflow.context_manager import WorkflowRunContext
from skyvern.forge.sdk.workflow.exceptions import ( from skyvern.forge.sdk.workflow.exceptions import (
InvalidEmailClientConfiguration, InvalidEmailClientConfiguration,
@@ -376,8 +375,8 @@ class BaseTaskBlock(Block):
) )
else: else:
browser_state = app.BROWSER_MANAGER.get_for_workflow_run(workflow_run_id=workflow_run_id) browser_state = app.BROWSER_MANAGER.get_for_workflow_run(workflow_run_id=workflow_run_id)
if browser_state is None: if browser_state is None:
raise MissingBrowserState(task_id=task.task_id, workflow_run_id=workflow_run_id) raise MissingBrowserState(task_id=task.task_id, workflow_run_id=workflow_run_id)
except FailedToNavigateToUrl as e: except FailedToNavigateToUrl as e:
# Make sure the task is marked as failed in the database before raising the exception # Make sure the task is marked as failed in the database before raising the exception
await app.DATABASE.update_task( await app.DATABASE.update_task(
@@ -982,7 +981,7 @@ class DownloadToS3Block(Block):
uri = None uri = None
try: try:
uri = f"s3://{SettingsManager.get_settings().AWS_S3_BUCKET_UPLOADS}/{SettingsManager.get_settings().ENV}/{workflow_run_id}/{uuid.uuid4()}" uri = f"s3://{settings.AWS_S3_BUCKET_UPLOADS}/{settings.ENV}/{workflow_run_id}/{uuid.uuid4()}"
await self._upload_file_to_s3(uri, file_path) await self._upload_file_to_s3(uri, file_path)
except Exception as e: except Exception as e:
LOG.error("DownloadToS3Block: Failed to upload file to S3", uri=uri, error=str(e)) LOG.error("DownloadToS3Block: Failed to upload file to S3", uri=uri, error=str(e))
@@ -1019,8 +1018,8 @@ class UploadToS3Block(Block):
@staticmethod @staticmethod
def _get_s3_uri(workflow_run_id: str, path: str) -> str: def _get_s3_uri(workflow_run_id: str, path: str) -> str:
s3_bucket = SettingsManager.get_settings().AWS_S3_BUCKET_UPLOADS s3_bucket = settings.AWS_S3_BUCKET_UPLOADS
s3_key = f"{SettingsManager.get_settings().ENV}/{workflow_run_id}/{uuid.uuid4()}_{Path(path).name}" s3_key = f"{settings.ENV}/{workflow_run_id}/{uuid.uuid4()}_{Path(path).name}"
return f"s3://{s3_bucket}/{s3_key}" return f"s3://{s3_bucket}/{s3_key}"
async def execute(self, workflow_run_id: str, **kwargs: dict) -> BlockResult: async def execute(self, workflow_run_id: str, **kwargs: dict) -> BlockResult:
@@ -1037,7 +1036,7 @@ class UploadToS3Block(Block):
) )
self.path = file_path_parameter_value self.path = file_path_parameter_value
# if the path is WORKFLOW_DOWNLOAD_DIRECTORY_PARAMETER_KEY, use the download directory for the workflow run # if the path is WORKFLOW_DOWNLOAD_DIRECTORY_PARAMETER_KEY, use the download directory for the workflow run
elif self.path == SettingsManager.get_settings().WORKFLOW_DOWNLOAD_DIRECTORY_PARAMETER_KEY: elif self.path == settings.WORKFLOW_DOWNLOAD_DIRECTORY_PARAMETER_KEY:
self.path = str(get_path_for_workflow_download_directory(workflow_run_id).absolute()) self.path = str(get_path_for_workflow_download_directory(workflow_run_id).absolute())
self.format_potential_template_parameters(workflow_run_context) self.format_potential_template_parameters(workflow_run_context)
@@ -1173,7 +1172,7 @@ class SendEmailBlock(Block):
else: else:
path = file_path_parameter_value path = file_path_parameter_value
if path == SettingsManager.get_settings().WORKFLOW_DOWNLOAD_DIRECTORY_PARAMETER_KEY: if path == settings.WORKFLOW_DOWNLOAD_DIRECTORY_PARAMETER_KEY:
# if the path is WORKFLOW_DOWNLOAD_DIRECTORY_PARAMETER_KEY, use download directory for the workflow run # if the path is WORKFLOW_DOWNLOAD_DIRECTORY_PARAMETER_KEY, use download directory for the workflow run
path = str(get_path_for_workflow_download_directory(workflow_run_id).absolute()) path = str(get_path_for_workflow_download_directory(workflow_run_id).absolute())
LOG.info( LOG.info(

View File

@@ -7,6 +7,7 @@ import httpx
import structlog import structlog
from skyvern import analytics from skyvern import analytics
from skyvern.config import settings
from skyvern.constants import GET_DOWNLOADED_FILES_TIMEOUT, SAVE_DOWNLOADED_FILES_TIMEOUT from skyvern.constants import GET_DOWNLOADED_FILES_TIMEOUT, SAVE_DOWNLOADED_FILES_TIMEOUT
from skyvern.exceptions import ( from skyvern.exceptions import (
FailedToSendWebhook, FailedToSendWebhook,
@@ -23,7 +24,6 @@ from skyvern.forge.sdk.core.skyvern_context import SkyvernContext
from skyvern.forge.sdk.db.enums import TaskType from skyvern.forge.sdk.db.enums import TaskType
from skyvern.forge.sdk.models import Organization, Step from skyvern.forge.sdk.models import Organization, Step
from skyvern.forge.sdk.schemas.tasks import ProxyLocation, Task from skyvern.forge.sdk.schemas.tasks import ProxyLocation, Task
from skyvern.forge.sdk.settings_manager import SettingsManager
from skyvern.forge.sdk.workflow.exceptions import ( from skyvern.forge.sdk.workflow.exceptions import (
ContextParameterSourceNotDefined, ContextParameterSourceNotDefined,
InvalidWaitBlockTime, InvalidWaitBlockTime,
@@ -1508,11 +1508,8 @@ class WorkflowService:
) )
elif block_yaml.block_type == BlockType.WAIT: elif block_yaml.block_type == BlockType.WAIT:
if ( if block_yaml.wait_sec <= 0 or block_yaml.wait_sec > settings.WORKFLOW_WAIT_BLOCK_MAX_SEC:
block_yaml.wait_sec <= 0 raise InvalidWaitBlockTime(settings.WORKFLOW_WAIT_BLOCK_MAX_SEC)
or block_yaml.wait_sec > SettingsManager.get_settings().WORKFLOW_WAIT_BLOCK_MAX_SEC
):
raise InvalidWaitBlockTime(SettingsManager.get_settings().WORKFLOW_WAIT_BLOCK_MAX_SEC)
return WaitBlock( return WaitBlock(
label=block_yaml.label, label=block_yaml.label,

View File

@@ -12,6 +12,7 @@ import structlog
from playwright.async_api import FileChooser, Frame, Locator, Page, TimeoutError from playwright.async_api import FileChooser, Frame, Locator, Page, TimeoutError
from pydantic import BaseModel from pydantic import BaseModel
from skyvern.config import settings
from skyvern.constants import REPO_ROOT_DIR, SKYVERN_ID_ATTR from skyvern.constants import REPO_ROOT_DIR, SKYVERN_ID_ATTR
from skyvern.exceptions import ( from skyvern.exceptions import (
EmptySelect, EmptySelect,
@@ -52,7 +53,6 @@ from skyvern.forge.sdk.db.enums import OrganizationAuthTokenType
from skyvern.forge.sdk.models import Step from skyvern.forge.sdk.models import Step
from skyvern.forge.sdk.schemas.tasks import Task from skyvern.forge.sdk.schemas.tasks import Task
from skyvern.forge.sdk.services.bitwarden import BitwardenConstants from skyvern.forge.sdk.services.bitwarden import BitwardenConstants
from skyvern.forge.sdk.settings_manager import SettingsManager
from skyvern.webeye.actions import actions from skyvern.webeye.actions import actions
from skyvern.webeye.actions.actions import ( from skyvern.webeye.actions.actions import (
Action, Action,
@@ -371,7 +371,7 @@ async def handle_click_action(
page, page,
action, action,
skyvern_element, skyvern_element,
timeout=SettingsManager.get_settings().BROWSER_ACTION_TIMEOUT_MS, timeout=settings.BROWSER_ACTION_TIMEOUT_MS,
) )
if results and task.workflow_run_id and download_dir: if results and task.workflow_run_id and download_dir:
@@ -400,8 +400,8 @@ async def handle_click_to_download_file_action(
locator = skyvern_element.locator locator = skyvern_element.locator
try: try:
await locator.click(timeout=SettingsManager.get_settings().BROWSER_ACTION_TIMEOUT_MS) await locator.click(timeout=settings.BROWSER_ACTION_TIMEOUT_MS)
await page.wait_for_load_state(timeout=SettingsManager.get_settings().BROWSER_LOADING_TIMEOUT_MS) await page.wait_for_load_state(timeout=settings.BROWSER_LOADING_TIMEOUT_MS)
except Exception as e: except Exception as e:
LOG.exception("ClickAction with download failed", action=action, exc_info=True) LOG.exception("ClickAction with download failed", action=action, exc_info=True)
return [ActionFailure(e, download_triggered=False)] return [ActionFailure(e, download_triggered=False)]
@@ -420,7 +420,7 @@ async def handle_input_text_action(
skyvern_element = await dom.get_skyvern_element_by_id(action.element_id) skyvern_element = await dom.get_skyvern_element_by_id(action.element_id)
skyvern_frame = await SkyvernFrame.create_instance(skyvern_element.get_frame()) skyvern_frame = await SkyvernFrame.create_instance(skyvern_element.get_frame())
incremental_scraped = IncrementalScrapePage(skyvern_frame=skyvern_frame) incremental_scraped = IncrementalScrapePage(skyvern_frame=skyvern_frame)
timeout = SettingsManager.get_settings().BROWSER_ACTION_TIMEOUT_MS timeout = settings.BROWSER_ACTION_TIMEOUT_MS
current_text = await get_input_value(skyvern_element.get_tag_name(), skyvern_element.get_locator()) current_text = await get_input_value(skyvern_element.get_tag_name(), skyvern_element.get_locator())
if current_text == action.text: if current_text == action.text:
@@ -656,7 +656,7 @@ async def handle_upload_file_action(
if file_path: if file_path:
await locator.set_input_files( await locator.set_input_files(
file_path, file_path,
timeout=SettingsManager.get_settings().BROWSER_ACTION_TIMEOUT_MS, timeout=settings.BROWSER_ACTION_TIMEOUT_MS,
) )
# Sleep for 10 seconds after uploading a file to let the page process it # Sleep for 10 seconds after uploading a file to let the page process it
@@ -675,7 +675,7 @@ async def handle_upload_file_action(
page, page,
action, action,
skyvern_element, skyvern_element,
timeout=SettingsManager.get_settings().BROWSER_ACTION_TIMEOUT_MS, timeout=settings.BROWSER_ACTION_TIMEOUT_MS,
) )
@@ -699,7 +699,7 @@ async def handle_download_file_action(
locator = skyvern_element.locator locator = skyvern_element.locator
await locator.click( await locator.click(
timeout=SettingsManager.get_settings().BROWSER_ACTION_TIMEOUT_MS, timeout=settings.BROWSER_ACTION_TIMEOUT_MS,
modifiers=["Alt"], modifiers=["Alt"],
) )
@@ -847,7 +847,7 @@ async def handle_select_option_action(
action=action, action=action,
) )
timeout = SettingsManager.get_settings().BROWSER_ACTION_TIMEOUT_MS timeout = settings.BROWSER_ACTION_TIMEOUT_MS
skyvern_frame = await SkyvernFrame.create_instance(skyvern_element.get_frame()) skyvern_frame = await SkyvernFrame.create_instance(skyvern_element.get_frame())
incremental_scraped = IncrementalScrapePage(skyvern_frame=skyvern_frame) incremental_scraped = IncrementalScrapePage(skyvern_frame=skyvern_frame)
is_open = False is_open = False
@@ -935,7 +935,7 @@ async def handle_select_option_action(
) )
try: try:
await incremental_scraped.start_listen_dom_increment() await incremental_scraped.start_listen_dom_increment()
timeout = SettingsManager.get_settings().BROWSER_ACTION_TIMEOUT_MS timeout = settings.BROWSER_ACTION_TIMEOUT_MS
await skyvern_element.scroll_into_view() await skyvern_element.scroll_into_view()
try: try:
@@ -999,9 +999,9 @@ async def handle_checkbox_action(
locator = skyvern_element.locator locator = skyvern_element.locator
if action.is_checked: if action.is_checked:
await locator.check(timeout=SettingsManager.get_settings().BROWSER_ACTION_TIMEOUT_MS) await locator.check(timeout=settings.BROWSER_ACTION_TIMEOUT_MS)
else: else:
await locator.uncheck(timeout=SettingsManager.get_settings().BROWSER_ACTION_TIMEOUT_MS) await locator.uncheck(timeout=settings.BROWSER_ACTION_TIMEOUT_MS)
# TODO (suchintan): Why does checking the label work, but not the actual input element? # TODO (suchintan): Why does checking the label work, but not the actual input element?
return [ActionSuccess()] return [ActionSuccess()]
@@ -1136,7 +1136,7 @@ async def chain_click(
page: Page, page: Page,
action: ClickAction | UploadFileAction, action: ClickAction | UploadFileAction,
skyvern_element: SkyvernElement, skyvern_element: SkyvernElement,
timeout: int = SettingsManager.get_settings().BROWSER_ACTION_TIMEOUT_MS, timeout: int = settings.BROWSER_ACTION_TIMEOUT_MS,
) -> List[ActionResult]: ) -> List[ActionResult]:
# Add a defensive page handler here in case a click action opens a file chooser. # Add a defensive page handler here in case a click action opens a file chooser.
# This automatically dismisses the dialog # This automatically dismisses the dialog
@@ -1374,9 +1374,7 @@ async def choose_auto_completion_dropdown(
if cnt == 0: if cnt == 0:
continue continue
element_handler = await locator.element_handle( element_handler = await locator.element_handle(timeout=settings.BROWSER_ACTION_TIMEOUT_MS)
timeout=SettingsManager.get_settings().BROWSER_ACTION_TIMEOUT_MS
)
if not element_handler: if not element_handler:
continue continue
@@ -1444,7 +1442,7 @@ async def choose_auto_completion_dropdown(
if await locator.count() == 0: if await locator.count() == 0:
raise MissingElement(element_id=element_id) raise MissingElement(element_id=element_id)
await locator.click(timeout=SettingsManager.get_settings().BROWSER_ACTION_TIMEOUT_MS) await locator.click(timeout=settings.BROWSER_ACTION_TIMEOUT_MS)
clear_input = False clear_input = False
return result return result
except Exception as e: except Exception as e:
@@ -1776,7 +1774,7 @@ async def select_from_dropdown(
select_history = [] if select_history is None else select_history select_history = [] if select_history is None else select_history
single_select_result = CustomSingleSelectResult(skyvern_frame=skyvern_frame) single_select_result = CustomSingleSelectResult(skyvern_frame=skyvern_frame)
timeout = SettingsManager.get_settings().BROWSER_ACTION_TIMEOUT_MS timeout = settings.BROWSER_ACTION_TIMEOUT_MS
if dropdown_menu_element is None: if dropdown_menu_element is None:
dropdown_menu_element = await locate_dropdown_menu( dropdown_menu_element = await locate_dropdown_menu(
@@ -1927,7 +1925,7 @@ async def select_from_dropdown_by_value(
step: Step, step: Step,
dropdown_menu_element: SkyvernElement | None = None, dropdown_menu_element: SkyvernElement | None = None,
) -> ActionResult: ) -> ActionResult:
timeout = SettingsManager.get_settings().BROWSER_ACTION_TIMEOUT_MS timeout = settings.BROWSER_ACTION_TIMEOUT_MS
await incremental_scraped.get_incremental_element_tree( await incremental_scraped.get_incremental_element_tree(
clean_and_remove_element_tree_factory(task=task, step=step, check_exist_funcs=[dom.check_id_in_dom]), clean_and_remove_element_tree_factory(task=task, step=step, check_exist_funcs=[dom.check_id_in_dom]),
) )
@@ -2061,9 +2059,7 @@ async def locate_dropdown_menu(
# sometimes taking screenshot might scroll away, need to scroll back after the screenshot # sometimes taking screenshot might scroll away, need to scroll back after the screenshot
x, y = await skyvern_frame.get_scroll_x_y() x, y = await skyvern_frame.get_scroll_x_y()
screenshot = await head_element.get_locator().screenshot( screenshot = await head_element.get_locator().screenshot(timeout=settings.BROWSER_SCREENSHOT_TIMEOUT_MS)
timeout=SettingsManager.get_settings().BROWSER_SCREENSHOT_TIMEOUT_MS
)
await skyvern_frame.scroll_to_x_y(x, y) await skyvern_frame.scroll_to_x_y(x, y)
# TODO: better to send untrimmed HTML without skyvern attributes in the future # TODO: better to send untrimmed HTML without skyvern attributes in the future
@@ -2139,7 +2135,7 @@ async def scroll_down_to_load_all_options(
step_id=step.step_id if step else "none", step_id=step.step_id if step else "none",
task_id=task.task_id if task else "none", task_id=task.task_id if task else "none",
) )
timeout = SettingsManager.get_settings().BROWSER_ACTION_TIMEOUT_MS timeout = settings.BROWSER_ACTION_TIMEOUT_MS
dropdown_menu_element_handle = await scrollable_element.get_locator().element_handle(timeout=timeout) dropdown_menu_element_handle = await scrollable_element.get_locator().element_handle(timeout=timeout)
if dropdown_menu_element_handle is None: if dropdown_menu_element_handle is None:
@@ -2153,12 +2149,10 @@ async def scroll_down_to_load_all_options(
scroll_pace = 0 scroll_pace = 0
previous_num = await incremental_scraped.get_incremental_elements_num() previous_num = await incremental_scraped.get_incremental_elements_num()
deadline = datetime.now(timezone.utc) + timedelta( deadline = datetime.now(timezone.utc) + timedelta(milliseconds=settings.OPTION_LOADING_TIMEOUT_MS)
milliseconds=SettingsManager.get_settings().OPTION_LOADING_TIMEOUT_MS
)
while datetime.now(timezone.utc) < deadline: while datetime.now(timezone.utc) < deadline:
# make sure we can scroll to the bottom # make sure we can scroll to the bottom
scroll_interval = SettingsManager.get_settings().BROWSER_HEIGHT * 5 scroll_interval = settings.BROWSER_HEIGHT * 5
if dropdown_menu_element_handle is None: if dropdown_menu_element_handle is None:
LOG.info("element handle is None, using mouse to scroll down", element_id=scrollable_element.get_id()) LOG.info("element handle is None, using mouse to scroll down", element_id=scrollable_element.get_id())
await page.mouse.wheel(0, scroll_interval) await page.mouse.wheel(0, scroll_interval)
@@ -2250,7 +2244,7 @@ async def normal_select(
try: try:
await locator.click( await locator.click(
timeout=SettingsManager.get_settings().BROWSER_ACTION_TIMEOUT_MS, timeout=settings.BROWSER_ACTION_TIMEOUT_MS,
) )
except Exception as e: except Exception as e:
LOG.info( LOG.info(
@@ -2267,7 +2261,7 @@ async def normal_select(
# click by value (if it matches) # click by value (if it matches)
await locator.select_option( await locator.select_option(
value=value, value=value,
timeout=SettingsManager.get_settings().BROWSER_ACTION_TIMEOUT_MS, timeout=settings.BROWSER_ACTION_TIMEOUT_MS,
) )
is_success = True is_success = True
action_result.append(ActionSuccess()) action_result.append(ActionSuccess())
@@ -2293,7 +2287,7 @@ async def normal_select(
# This means the supplied index was for the select element, not a reference to the css dict # This means the supplied index was for the select element, not a reference to the css dict
await locator.select_option( await locator.select_option(
index=index, index=index,
timeout=SettingsManager.get_settings().BROWSER_ACTION_TIMEOUT_MS, timeout=settings.BROWSER_ACTION_TIMEOUT_MS,
) )
is_success = True is_success = True
action_result.append(ActionSuccess()) action_result.append(ActionSuccess())
@@ -2308,7 +2302,7 @@ async def normal_select(
try: try:
await locator.click( await locator.click(
timeout=SettingsManager.get_settings().BROWSER_ACTION_TIMEOUT_MS, timeout=settings.BROWSER_ACTION_TIMEOUT_MS,
) )
except Exception as e: except Exception as e:
LOG.info( LOG.info(
@@ -2464,7 +2458,7 @@ async def poll_verification_code(
totp_verification_url: str | None = None, totp_verification_url: str | None = None,
totp_identifier: str | None = None, totp_identifier: str | None = None,
) -> str | None: ) -> str | None:
timeout = timedelta(minutes=SettingsManager.get_settings().VERIFICATION_CODE_POLLING_TIMEOUT_MINS) timeout = timedelta(minutes=settings.VERIFICATION_CODE_POLLING_TIMEOUT_MINS)
start_datetime = datetime.utcnow() start_datetime = datetime.utcnow()
timeout_datetime = start_datetime + timeout timeout_datetime = start_datetime + timeout
org_token = await app.DATABASE.get_valid_org_auth_token(organization_id, OrganizationAuthTokenType.api) org_token = await app.DATABASE.get_valid_org_auth_token(organization_id, OrganizationAuthTokenType.api)
@@ -2472,7 +2466,7 @@ async def poll_verification_code(
LOG.error("Failed to get organization token when trying to get verification code") LOG.error("Failed to get organization token when trying to get verification code")
return None return None
# wait for 40 seconds to let the verification code comes in before polling # wait for 40 seconds to let the verification code comes in before polling
await asyncio.sleep(SettingsManager.get_settings().VERIFICATION_CODE_INITIAL_WAIT_TIME_SECS) await asyncio.sleep(settings.VERIFICATION_CODE_INITIAL_WAIT_TIME_SECS)
while True: while True:
# check timeout # check timeout
if datetime.utcnow() > timeout_datetime: if datetime.utcnow() > timeout_datetime:

View File

@@ -4,7 +4,7 @@ from typing import Any
from pydantic import BaseModel from pydantic import BaseModel
from skyvern.forge.sdk.settings_manager import SettingsManager from skyvern.config import settings
from skyvern.webeye.actions.actions import Action, DecisiveAction, UserDefinedError from skyvern.webeye.actions.actions import Action, DecisiveAction, UserDefinedError
from skyvern.webeye.actions.responses import ActionResult from skyvern.webeye.actions.responses import ActionResult
from skyvern.webeye.scraper.scraper import ScrapedPage from skyvern.webeye.scraper.scraper import ScrapedPage
@@ -45,7 +45,7 @@ class DetailedAgentStepOutput(BaseModel):
exclude = ["scraped_page", "extract_action_prompt"] exclude = ["scraped_page", "extract_action_prompt"]
def __repr__(self) -> str: def __repr__(self) -> str:
if SettingsManager.get_settings().DEBUG_MODE: if settings.DEBUG_MODE:
return f"DetailedAgentStepOutput({self.model_dump()})" return f"DetailedAgentStepOutput({self.model_dump()})"
else: else:
return f"AgentStepOutput({self.to_agent_step_output().model_dump()})" return f"AgentStepOutput({self.to_agent_step_output().model_dump()})"

View File

@@ -26,7 +26,6 @@ from skyvern.exceptions import (
from skyvern.forge.sdk.api.files import make_temp_directory from skyvern.forge.sdk.api.files import make_temp_directory
from skyvern.forge.sdk.core.skyvern_context import current from skyvern.forge.sdk.core.skyvern_context import current
from skyvern.forge.sdk.schemas.tasks import ProxyLocation from skyvern.forge.sdk.schemas.tasks import ProxyLocation
from skyvern.forge.sdk.settings_manager import SettingsManager
from skyvern.webeye.utils.page import SkyvernFrame from skyvern.webeye.utils.page import SkyvernFrame
LOG = structlog.get_logger() LOG = structlog.get_logger()
@@ -143,12 +142,14 @@ class BrowserContextFactory:
@staticmethod @staticmethod
def build_browser_args() -> dict[str, Any]: def build_browser_args() -> dict[str, Any]:
video_dir = f"{SettingsManager.get_settings().VIDEO_PATH}/{datetime.utcnow().strftime('%Y-%m-%d')}" video_dir = f"{settings.VIDEO_PATH}/{datetime.utcnow().strftime('%Y-%m-%d')}"
har_dir = f"{SettingsManager.get_settings().HAR_PATH}/{datetime.utcnow().strftime('%Y-%m-%d')}/{BrowserContextFactory.get_subdir()}.har" har_dir = (
f"{settings.HAR_PATH}/{datetime.utcnow().strftime('%Y-%m-%d')}/{BrowserContextFactory.get_subdir()}.har"
)
return { return {
"user_data_dir": make_temp_directory(prefix="skyvern_browser_"), "user_data_dir": make_temp_directory(prefix="skyvern_browser_"),
"locale": SettingsManager.get_settings().BROWSER_LOCALE, "locale": settings.BROWSER_LOCALE,
"timezone_id": SettingsManager.get_settings().BROWSER_TIMEZONE, "timezone_id": settings.BROWSER_TIMEZONE,
"color_scheme": "no-preference", "color_scheme": "no-preference",
"args": [ "args": [
"--disable-blink-features=AutomationControlled", "--disable-blink-features=AutomationControlled",
@@ -191,7 +192,7 @@ class BrowserContextFactory:
async def create_browser_context( async def create_browser_context(
cls, playwright: Playwright, **kwargs: Any cls, playwright: Playwright, **kwargs: Any
) -> tuple[BrowserContext, BrowserArtifacts, BrowserCleanupFunc]: ) -> tuple[BrowserContext, BrowserArtifacts, BrowserCleanupFunc]:
browser_type = SettingsManager.get_settings().BROWSER_TYPE browser_type = settings.BROWSER_TYPE
browser_context: BrowserContext | None = None browser_context: BrowserContext | None = None
try: try:
creator = cls._creators.get(browser_type) creator = cls._creators.get(browser_type)

View File

@@ -9,10 +9,10 @@ import structlog
from playwright.async_api import Frame, Locator, Page from playwright.async_api import Frame, Locator, Page
from pydantic import BaseModel, PrivateAttr from pydantic import BaseModel, PrivateAttr
from skyvern.config import settings
from skyvern.constants import BUILDING_ELEMENT_TREE_TIMEOUT_MS, SKYVERN_DIR, SKYVERN_ID_ATTR from skyvern.constants import BUILDING_ELEMENT_TREE_TIMEOUT_MS, SKYVERN_DIR, SKYVERN_ID_ATTR
from skyvern.exceptions import FailedToTakeScreenshot, UnknownElementTreeFormat from skyvern.exceptions import FailedToTakeScreenshot, UnknownElementTreeFormat
from skyvern.forge.sdk.api.crypto import calculate_sha256 from skyvern.forge.sdk.api.crypto import calculate_sha256
from skyvern.forge.sdk.settings_manager import SettingsManager
from skyvern.webeye.browser_factory import BrowserState from skyvern.webeye.browser_factory import BrowserState
from skyvern.webeye.utils.page import SkyvernFrame from skyvern.webeye.utils.page import SkyvernFrame
@@ -301,10 +301,10 @@ async def scrape_website(
) )
except Exception as e: except Exception as e:
# NOTE: MAX_SCRAPING_RETRIES is set to 0 in both staging and production # NOTE: MAX_SCRAPING_RETRIES is set to 0 in both staging and production
if num_retry > SettingsManager.get_settings().MAX_SCRAPING_RETRIES: if num_retry > settings.MAX_SCRAPING_RETRIES:
LOG.error( LOG.error(
"Scraping failed after max retries, aborting.", "Scraping failed after max retries, aborting.",
max_retries=SettingsManager.get_settings().MAX_SCRAPING_RETRIES, max_retries=settings.MAX_SCRAPING_RETRIES,
url=url, url=url,
exc_info=True, exc_info=True,
) )

View File

@@ -9,6 +9,7 @@ from random import uniform
import structlog import structlog
from playwright.async_api import ElementHandle, Frame, FrameLocator, Locator, Page, TimeoutError from playwright.async_api import ElementHandle, Frame, FrameLocator, Locator, Page, TimeoutError
from skyvern.config import settings
from skyvern.constants import SKYVERN_ID_ATTR from skyvern.constants import SKYVERN_ID_ATTR
from skyvern.exceptions import ( from skyvern.exceptions import (
ElementIsNotLabel, ElementIsNotLabel,
@@ -21,7 +22,6 @@ from skyvern.exceptions import (
NoneFrameError, NoneFrameError,
SkyvernException, SkyvernException,
) )
from skyvern.forge.sdk.settings_manager import SettingsManager
from skyvern.webeye.scraper.scraper import IncrementalScrapePage, ScrapedPage, json_to_html, trim_element from skyvern.webeye.scraper.scraper import IncrementalScrapePage, ScrapedPage, json_to_html, trim_element
from skyvern.webeye.utils.page import SkyvernFrame from skyvern.webeye.utils.page import SkyvernFrame
@@ -313,9 +313,7 @@ class SkyvernElement:
def get_locator(self) -> Locator: def get_locator(self) -> Locator:
return self.locator return self.locator
async def get_element_handler( async def get_element_handler(self, timeout: float = settings.BROWSER_ACTION_TIMEOUT_MS) -> ElementHandle:
self, timeout: float = SettingsManager.get_settings().BROWSER_ACTION_TIMEOUT_MS
) -> ElementHandle:
handler = await self.locator.element_handle(timeout=timeout) handler = await self.locator.element_handle(timeout=timeout)
assert handler is not None assert handler is not None
return handler return handler
@@ -368,7 +366,7 @@ class SkyvernElement:
return None return None
async def find_label_for( async def find_label_for(
self, dom: DomUtil, timeout: float = SettingsManager.get_settings().BROWSER_ACTION_TIMEOUT_MS self, dom: DomUtil, timeout: float = settings.BROWSER_ACTION_TIMEOUT_MS
) -> SkyvernElement | None: ) -> SkyvernElement | None:
if self.get_tag_name() != "label": if self.get_tag_name() != "label":
return None return None
@@ -388,9 +386,7 @@ class SkyvernElement:
return await dom.get_skyvern_element_by_id(unique_id) return await dom.get_skyvern_element_by_id(unique_id)
async def find_bound_label_by_attr_id( async def find_bound_label_by_attr_id(self, timeout: float = settings.BROWSER_ACTION_TIMEOUT_MS) -> Locator | None:
self, timeout: float = SettingsManager.get_settings().BROWSER_ACTION_TIMEOUT_MS
) -> Locator | None:
if self.get_tag_name() == "label": if self.get_tag_name() == "label":
return None return None
@@ -406,7 +402,7 @@ class SkyvernElement:
return None return None
async def find_bound_label_by_direct_parent( async def find_bound_label_by_direct_parent(
self, timeout: float = SettingsManager.get_settings().BROWSER_ACTION_TIMEOUT_MS self, timeout: float = settings.BROWSER_ACTION_TIMEOUT_MS
) -> Locator | None: ) -> Locator | None:
if self.get_tag_name() == "label": if self.get_tag_name() == "label":
return None return None
@@ -490,7 +486,7 @@ class SkyvernElement:
self, self,
attr_name: str, attr_name: str,
dynamic: bool = False, dynamic: bool = False,
timeout: float = SettingsManager.get_settings().BROWSER_ACTION_TIMEOUT_MS, timeout: float = settings.BROWSER_ACTION_TIMEOUT_MS,
) -> typing.Any: ) -> typing.Any:
if not dynamic: if not dynamic:
attr = self.get_attributes().get(attr_name) attr = self.get_attributes().get(attr_name)
@@ -499,12 +495,10 @@ class SkyvernElement:
return await self.locator.get_attribute(attr_name, timeout=timeout) return await self.locator.get_attribute(attr_name, timeout=timeout)
async def focus(self, timeout: float = SettingsManager.get_settings().BROWSER_ACTION_TIMEOUT_MS) -> None: async def focus(self, timeout: float = settings.BROWSER_ACTION_TIMEOUT_MS) -> None:
await self.get_locator().focus(timeout=timeout) await self.get_locator().focus(timeout=timeout)
async def input_sequentially( async def input_sequentially(self, text: str, default_timeout: float = settings.BROWSER_ACTION_TIMEOUT_MS) -> None:
self, text: str, default_timeout: float = SettingsManager.get_settings().BROWSER_ACTION_TIMEOUT_MS
) -> None:
length = len(text) length = len(text)
if length > TEXT_PRESS_MAX_LENGTH: if length > TEXT_PRESS_MAX_LENGTH:
# if the text is longer than TEXT_PRESS_MAX_LENGTH characters, we will locator.fill in initial texts until the last TEXT_PRESS_MAX_LENGTH characters # if the text is longer than TEXT_PRESS_MAX_LENGTH characters, we will locator.fill in initial texts until the last TEXT_PRESS_MAX_LENGTH characters
@@ -514,22 +508,16 @@ class SkyvernElement:
await self.press_fill(text, timeout=default_timeout) await self.press_fill(text, timeout=default_timeout)
async def press_key( async def press_key(self, key: str, timeout: float = settings.BROWSER_ACTION_TIMEOUT_MS) -> None:
self, key: str, timeout: float = SettingsManager.get_settings().BROWSER_ACTION_TIMEOUT_MS
) -> None:
await self.get_locator().press(key=key, timeout=timeout) await self.get_locator().press(key=key, timeout=timeout)
async def press_fill( async def press_fill(self, text: str, timeout: float = settings.BROWSER_ACTION_TIMEOUT_MS) -> None:
self, text: str, timeout: float = SettingsManager.get_settings().BROWSER_ACTION_TIMEOUT_MS
) -> None:
await self.get_locator().press_sequentially(text, delay=TEXT_INPUT_DELAY, timeout=timeout) await self.get_locator().press_sequentially(text, delay=TEXT_INPUT_DELAY, timeout=timeout)
async def input_fill( async def input_fill(self, text: str, timeout: float = settings.BROWSER_ACTION_TIMEOUT_MS) -> None:
self, text: str, timeout: float = SettingsManager.get_settings().BROWSER_ACTION_TIMEOUT_MS
) -> None:
await self.get_locator().fill(text, timeout=timeout) await self.get_locator().fill(text, timeout=timeout)
async def input_clear(self, timeout: float = SettingsManager.get_settings().BROWSER_ACTION_TIMEOUT_MS) -> None: async def input_clear(self, timeout: float = settings.BROWSER_ACTION_TIMEOUT_MS) -> None:
await self.get_locator().clear(timeout=timeout) await self.get_locator().clear(timeout=timeout)
async def move_mouse_to_safe( async def move_mouse_to_safe(
@@ -537,7 +525,7 @@ class SkyvernElement:
page: Page, page: Page,
task_id: str | None = None, task_id: str | None = None,
step_id: str | None = None, step_id: str | None = None,
timeout: float = SettingsManager.get_settings().BROWSER_ACTION_TIMEOUT_MS, timeout: float = settings.BROWSER_ACTION_TIMEOUT_MS,
) -> tuple[float, float] | tuple[None, None]: ) -> tuple[float, float] | tuple[None, None]:
element_id = self.get_id() element_id = self.get_id()
try: try:
@@ -561,7 +549,7 @@ class SkyvernElement:
return None, None return None, None
async def move_mouse_to( async def move_mouse_to(
self, page: Page, timeout: float = SettingsManager.get_settings().BROWSER_ACTION_TIMEOUT_MS self, page: Page, timeout: float = settings.BROWSER_ACTION_TIMEOUT_MS
) -> tuple[float, float]: ) -> tuple[float, float]:
bounding_box = await self.get_locator().bounding_box(timeout=timeout) bounding_box = await self.get_locator().bounding_box(timeout=timeout)
if not bounding_box: if not bounding_box:
@@ -580,9 +568,7 @@ class SkyvernElement:
skyvern_frame = await SkyvernFrame.create_instance(self.get_frame()) skyvern_frame = await SkyvernFrame.create_instance(self.get_frame())
await skyvern_frame.click_element_in_javascript(await self.get_element_handler()) await skyvern_frame.click_element_in_javascript(await self.get_element_handler())
async def coordinate_click( async def coordinate_click(self, page: Page, timeout: float = settings.BROWSER_ACTION_TIMEOUT_MS) -> None:
self, page: Page, timeout: float = SettingsManager.get_settings().BROWSER_ACTION_TIMEOUT_MS
) -> None:
click_x, click_y = await self.move_mouse_to(page=page, timeout=timeout) click_x, click_y = await self.move_mouse_to(page=page, timeout=timeout)
await page.mouse.click(click_x, click_y) await page.mouse.click(click_x, click_y)
@@ -591,7 +577,7 @@ class SkyvernElement:
frame=self.get_frame(), expression="(element) => element.blur()", arg=await self.get_element_handler() frame=self.get_frame(), expression="(element) => element.blur()", arg=await self.get_element_handler()
) )
async def scroll_into_view(self, timeout: float = SettingsManager.get_settings().BROWSER_ACTION_TIMEOUT_MS) -> None: async def scroll_into_view(self, timeout: float = settings.BROWSER_ACTION_TIMEOUT_MS) -> None:
element_handler = await self.get_element_handler(timeout=timeout) element_handler = await self.get_element_handler(timeout=timeout)
try: try:
await element_handler.scroll_into_view_if_needed(timeout=timeout) await element_handler.scroll_into_view_if_needed(timeout=timeout)
@@ -608,7 +594,7 @@ class SkyvernElement:
self, self,
target_locator: Locator, target_locator: Locator,
mode: typing.Literal["inner", "outer"], mode: typing.Literal["inner", "outer"],
timeout: float = SettingsManager.get_settings().BROWSER_ACTION_TIMEOUT_MS, timeout: float = settings.BROWSER_ACTION_TIMEOUT_MS,
) -> float: ) -> float:
self_rect = await self.get_locator().bounding_box(timeout=timeout) self_rect = await self.get_locator().bounding_box(timeout=timeout)
if self_rect is None: if self_rect is None:

View File

@@ -8,9 +8,9 @@ import structlog
from playwright._impl._errors import TimeoutError from playwright._impl._errors import TimeoutError
from playwright.async_api import ElementHandle, Frame, Page from playwright.async_api import ElementHandle, Frame, Page
from skyvern.config import settings
from skyvern.constants import BUILDING_ELEMENT_TREE_TIMEOUT_MS, PAGE_CONTENT_TIMEOUT, SKYVERN_DIR from skyvern.constants import BUILDING_ELEMENT_TREE_TIMEOUT_MS, PAGE_CONTENT_TIMEOUT, SKYVERN_DIR
from skyvern.exceptions import FailedToTakeScreenshot from skyvern.exceptions import FailedToTakeScreenshot
from skyvern.forge.sdk.settings_manager import SettingsManager
LOG = structlog.get_logger() LOG = structlog.get_logger()
@@ -37,7 +37,7 @@ class SkyvernFrame:
frame: Page | Frame, frame: Page | Frame,
expression: str, expression: str,
arg: Any | None = None, arg: Any | None = None,
timeout_ms: float = SettingsManager.get_settings().BROWSER_ACTION_TIMEOUT_MS, timeout_ms: float = settings.BROWSER_ACTION_TIMEOUT_MS,
) -> Any: ) -> Any:
try: try:
async with asyncio.timeout(timeout_ms / 1000): async with asyncio.timeout(timeout_ms / 1000):
@@ -51,12 +51,12 @@ class SkyvernFrame:
page: Page, page: Page,
full_page: bool = False, full_page: bool = False,
file_path: str | None = None, file_path: str | None = None,
timeout: float = SettingsManager.get_settings().BROWSER_LOADING_TIMEOUT_MS, timeout: float = settings.BROWSER_LOADING_TIMEOUT_MS,
) -> bytes: ) -> bytes:
if page.is_closed(): if page.is_closed():
raise FailedToTakeScreenshot(error_message="Page is closed") raise FailedToTakeScreenshot(error_message="Page is closed")
try: try:
await page.wait_for_load_state(timeout=SettingsManager.get_settings().BROWSER_LOADING_TIMEOUT_MS) await page.wait_for_load_state(timeout=settings.BROWSER_LOADING_TIMEOUT_MS)
LOG.debug("Page is fully loaded, agent is about to take screenshots") LOG.debug("Page is fully loaded, agent is about to take screenshots")
start_time = time.time() start_time = time.time()
screenshot: bytes = bytes() screenshot: bytes = bytes()
@@ -92,7 +92,7 @@ class SkyvernFrame:
page: Page, page: Page,
url: str, url: str,
draw_boxes: bool = False, draw_boxes: bool = False,
max_number: int = SettingsManager.get_settings().MAX_NUM_SCREENSHOTS, max_number: int = settings.MAX_NUM_SCREENSHOTS,
) -> List[bytes]: ) -> List[bytes]:
skyvern_page = await SkyvernFrame.create_instance(frame=page) skyvern_page = await SkyvernFrame.create_instance(frame=page)
assert isinstance(skyvern_page.frame, Page) assert isinstance(skyvern_page.frame, Page)