diff --git a/fern/getting-started/quickstart.mdx b/fern/getting-started/quickstart.mdx index 7f8ae09b..7cd15824 100644 --- a/fern/getting-started/quickstart.mdx +++ b/fern/getting-started/quickstart.mdx @@ -50,7 +50,7 @@ You can also run browser tasks locally with Python code, with a little bit of se from skyvern import Skyvern import asyncio - skyvern = Skyvern() + skyvern = Skyvern(api_key="YOUR_LOCAL_API_KEY") asyncio.run(skyvern.run_task(prompt="Find the top post on hackernews today")) ``` diff --git a/fern/observability/overview.mdx b/fern/observability/overview.mdx index 4401f78f..ebb3dc7e 100644 --- a/fern/observability/overview.mdx +++ b/fern/observability/overview.mdx @@ -37,7 +37,7 @@ Laminar.initialize(disabled_instruments=set([Instruments.OPENAI])) # Configure LiteLLM to trace all LLM calls made by Skyvern litellm.callbacks = [LaminarLiteLLMCallback()] -skyvern = Skyvern() +skyvern = Skyvern(api_key="YOUR_API_KEY") async def main(): task = await skyvern.run_task( diff --git a/fern/running-tasks/run-tasks.mdx b/fern/running-tasks/run-tasks.mdx index 3598ac35..17815b2b 100644 --- a/fern/running-tasks/run-tasks.mdx +++ b/fern/running-tasks/run-tasks.mdx @@ -131,7 +131,7 @@ For example, if you want to get the title, URL, and points of the top post on Ha ```python from skyvern import Skyvern -skyvern = Skyvern() +skyvern = Skyvern(api_key="YOUR_API_KEY") task = await skyvern.run_task( prompt="Find the top post on hackernews today", data_extraction_schema={ @@ -159,7 +159,7 @@ When you are sending a run task request the Skyvern service, you can set the `wa ```python from skyvern import Skyvern -skyvern = Skyvern() +skyvern = Skyvern(api_key="YOUR_API_KEY") task = await skyvern.run_task( prompt="Find the top post on hackernews today", # the request will be hanging until the task is done @@ -173,7 +173,7 @@ Instead of waiting, you can also set the `webhook_url` in the run task request a ```python from skyvern import Skyvern -skyvern = Skyvern() +skyvern = Skyvern(api_key="YOUR_API_KEY") task = await skyvern.run_task( prompt="Find the top post on hackernews today", webhook_url="https://your-webhook-url.com", diff --git a/integrations/langchain/skyvern_langchain/agent.py b/integrations/langchain/skyvern_langchain/agent.py index 8cf0e698..b5cbdd6b 100644 --- a/integrations/langchain/skyvern_langchain/agent.py +++ b/integrations/langchain/skyvern_langchain/agent.py @@ -15,7 +15,7 @@ from skyvern.schemas.runs import RunEngine class SkyvernTaskBaseTool(BaseTool): engine: RunEngine = Field(default=settings.engine) run_task_timeout_seconds: int = Field(default=settings.run_task_timeout_seconds) - agent: Skyvern = Skyvern() + agent: Skyvern = Skyvern.local() def _run(self, *args: Any, **kwargs: Any) -> None: raise NotImplementedError("skyvern task tool does not support sync") diff --git a/integrations/llama_index/skyvern_llamaindex/agent.py b/integrations/llama_index/skyvern_llamaindex/agent.py index 9c3aa63c..85968ec1 100644 --- a/integrations/llama_index/skyvern_llamaindex/agent.py +++ b/integrations/llama_index/skyvern_llamaindex/agent.py @@ -13,7 +13,7 @@ from skyvern.schemas.runs import RunEngine class SkyvernTool: def __init__(self, agent: Skyvern | None = None): if agent is None: - agent = Skyvern() + agent = Skyvern.local() self.agent = agent def run_task(self) -> FunctionTool: @@ -44,7 +44,7 @@ class SkyvernTaskToolSpec(BaseToolSpec): run_task_timeout_seconds: int = settings.run_task_timeout_seconds, ) -> None: if agent is None: - agent = Skyvern() + agent = Skyvern.local() self.agent = agent self.engine = engine self.run_task_timeout_seconds = run_task_timeout_seconds diff --git a/skyvern/library/skyvern.py b/skyvern/library/skyvern.py index da86fbcf..bf7d10ba 100644 --- a/skyvern/library/skyvern.py +++ b/skyvern/library/skyvern.py @@ -2,7 +2,7 @@ import asyncio import os import pathlib import tempfile -from typing import Any, overload +from typing import Any import httpx import structlog @@ -15,7 +15,6 @@ from skyvern.client.types.task_run_response import TaskRunResponse from skyvern.client.types.workflow_run_response import WorkflowRunResponse from skyvern.forge.sdk.api.llm.models import LLMConfig, LLMRouterConfig from skyvern.library.constants import DEFAULT_AGENT_HEARTBEAT_INTERVAL, DEFAULT_AGENT_TIMEOUT, DEFAULT_CDP_PORT -from skyvern.library.embedded_server_factory import create_embedded_server from skyvern.library.skyvern_browser import SkyvernBrowser from skyvern.schemas.run_blocks import CredentialType from skyvern.schemas.runs import ProxyLocation, RunEngine, RunStatus @@ -33,13 +32,13 @@ class Skyvern(AsyncSkyvern): Example: ```python - # Initialize with remote environment and API key - skyvern = Skyvern(environment=SkyvernEnvironment.CLOUD, api_key="your-api-key") + # Remote mode: Connect to Skyvern Cloud (API key required) + skyvern = Skyvern(api_key="your-api-key") - # Or in embedded mode (run `skyvern quickstart` first): - skyvern = Skyvern() + # Local/embedded mode (run `skyvern quickstart` first): + skyvern = Skyvern.local() - # Launch a local browser + # Launch a local browser (works only in local environment) browser = await skyvern.launch_local_browser(headless=False) page = await browser.get_working_page() @@ -81,12 +80,11 @@ class Skyvern(AsyncSkyvern): ``` """ - @overload def __init__( self, *, - environment: SkyvernEnvironment, api_key: str, + environment: SkyvernEnvironment = SkyvernEnvironment.CLOUD, base_url: str | None = None, timeout: float | None = None, follow_redirects: bool | None = True, @@ -95,11 +93,11 @@ class Skyvern(AsyncSkyvern): """Remote mode: Connect to Skyvern Cloud or self-hosted instance. Args: - environment: The Skyvern environment to connect to. Use SkyvernEnvironment.CLOUD - for Skyvern Cloud or SkyvernEnvironment.PRODUCTION/STAGING for self-hosted - instances. api_key: API key for authenticating with Skyvern. Can be found on the settings page: https://app.skyvern.com/settings + environment: The Skyvern environment to connect to. Use SkyvernEnvironment.CLOUD + for Skyvern Cloud or SkyvernEnvironment.PRODUCTION/STAGING for self-hosted + instances. Defaults to SkyvernEnvironment.CLOUD. base_url: Override the base URL for the Skyvern API. If not provided, uses the default URL for the specified environment. timeout: Timeout in seconds for API requests. If not provided, uses the default timeout. @@ -107,19 +105,30 @@ class Skyvern(AsyncSkyvern): httpx_client: Custom httpx AsyncClient for making API requests. If not provided, a default client will be created. """ - ... + super().__init__( + base_url=base_url, + environment=environment, + api_key=api_key, + timeout=timeout, + follow_redirects=follow_redirects, + httpx_client=httpx_client, + ) - @overload - def __init__( - self, + self._environment = environment + self._api_key: str | None = api_key + self._playwright: Playwright | None = None + + @classmethod + def local( + cls, *, llm_config: LLMRouterConfig | LLMConfig | None = None, settings: dict[str, Any] | None = None, - ) -> None: - """Embedded mode: Run Skyvern locally in-process. + ) -> "Skyvern": + """Local/embedded mode: Run Skyvern locally in-process. Prerequisites: - Run `skyvern quickstart` first to set up your local environment and create a .env file. + Run `skyvern quickstart` first to set up your local environment and create a .env file Args: llm_config: Optional custom LLM configuration (LLMConfig or LLMRouterConfig). @@ -127,95 +136,82 @@ class Skyvern(AsyncSkyvern): overriding the LLM_KEY setting from your .env file. If not provided, uses the LLM configured via LLM_KEY in your .env file. - Example 1 - Using environment variables (recommended): + Example 1 - Using .env configuration (simplest, recommended): + ```python + from skyvern import Skyvern + + # Uses LLM_KEY and other settings from your .env file + # Created by running `skyvern quickstart` + skyvern = Skyvern.local() + ``` + + Example 2 - Custom LLM with environment variables: ```python from skyvern import Skyvern from skyvern.forge.sdk.api.llm.models import LLMConfig # Assumes OPENAI_API_KEY is set in your environment - llm_config = LLMConfig( - model_name="gpt-4o", - required_env_vars=["OPENAI_API_KEY"], - supports_vision=True, - add_assistant_prefix=False, + skyvern = Skyvern.local( + llm_config=LLMConfig( + model_name="gpt-4o", + required_env_vars=["OPENAI_API_KEY"], + supports_vision=True, + add_assistant_prefix=False, + ) ) - skyvern = Skyvern(llm_config=llm_config) ``` - Example 2 - Explicitly providing credentials: + Example 3 - Explicitly providing credentials: ```python from skyvern import Skyvern from skyvern.forge.sdk.api.llm.models import LLMConfig, LiteLLMParams - llm_config = LLMConfig( - model_name="gpt-4o", - required_env_vars=[], # No env vars required - supports_vision=True, - add_assistant_prefix=False, - litellm_params=LiteLLMParams( - api_base="https://api.openai.com/v1", - api_key="sk-...", # Your API key - ), + skyvern = Skyvern.local( + llm_config=LLMConfig( + model_name="gpt-4o", + required_env_vars=[], # No env vars required + supports_vision=True, + add_assistant_prefix=False, + litellm_params=LiteLLMParams( + api_base="https://api.openai.com/v1", + api_key="sk-...", # Your API key + ), + ) ) - skyvern = Skyvern(llm_config=llm_config) ``` settings: Optional dictionary of Skyvern settings to override. These override the corresponding settings from your .env file. Example: {"MAX_STEPS_PER_RUN": 100, "BROWSER_TYPE": "chromium-headful"} + + Returns: + Skyvern: A Skyvern instance running in local/embedded mode. """ - ... + from skyvern.library.embedded_server_factory import create_embedded_server # noqa: PLC0415 - def __init__( - self, - *, - environment: SkyvernEnvironment | None = None, - base_url: str | None = None, - api_key: str | None = None, - timeout: float | None = None, - follow_redirects: bool | None = True, - httpx_client: httpx.AsyncClient | None = None, - llm_config: LLMRouterConfig | LLMConfig | None = None, - settings: dict[str, Any] | None = None, - ): - if environment is None: - if httpx_client is not None: - raise ValueError("httpx_client is not supported in embedded mode") + if not os.path.exists(".env"): + raise ValueError("Please run `skyvern quickstart` to set up your local Skyvern environment") - if not os.path.exists(".env"): - raise ValueError("Please run `skyvern quickstart` to set up your local Skyvern environment") + load_dotenv(".env") + api_key = os.getenv("SKYVERN_API_KEY") + if not api_key: + raise ValueError("SKYVERN_API_KEY is not set. Provide api_key or set SKYVERN_API_KEY in .env file.") - load_dotenv(".env") - api_key = os.getenv("SKYVERN_API_KEY") - if not api_key: - raise ValueError("SKYVERN_API_KEY is not set. Provide api_key or set SKYVERN_API_KEY in .env file.") + obj = cls.__new__(cls) - super().__init__( - environment=SkyvernEnvironment.LOCAL, - api_key=api_key, - timeout=timeout, - follow_redirects=follow_redirects, - httpx_client=create_embedded_server( - llm_config=llm_config, - settings_overrides=settings, - ), - ) - else: - if not api_key: - raise ValueError(f"Missing api_key for {environment.name}") + AsyncSkyvern.__init__( + obj, + environment=SkyvernEnvironment.LOCAL, + httpx_client=create_embedded_server( + llm_config=llm_config, + settings_overrides=settings, + ), + ) - super().__init__( - base_url=base_url, - environment=environment, - api_key=api_key, - timeout=timeout, - follow_redirects=follow_redirects, - httpx_client=httpx_client, - ) + obj._environment = SkyvernEnvironment.LOCAL + obj._api_key = None + obj._playwright = None - self._environment = environment - self._api_key = api_key - - self._playwright: Playwright | None = None + return obj async def run_task( self, diff --git a/skyvern/library/skyvern_browser.py b/skyvern/library/skyvern_browser.py index 4eb0dd0f..a15c4d21 100644 --- a/skyvern/library/skyvern_browser.py +++ b/skyvern/library/skyvern_browser.py @@ -18,7 +18,7 @@ class SkyvernBrowser(BrowserContext): Example: ```python - skyvern = Skyvern() + skyvern = Skyvern.local() browser = await skyvern.launch_local_browser() # Get or create the working page @@ -32,7 +32,6 @@ class SkyvernBrowser(BrowserContext): _browser_context: The underlying Playwright BrowserContext. _browser_session_id: Optional session ID for persistent browser sessions. _browser_address: Optional address for remote browser connections. - _client: The AsyncSkyvern client for API communication. """ def __init__(