SDK: use Skyvern.local() factory method (#4206)

This commit is contained in:
Stanislav Novosad
2025-12-05 13:21:40 -07:00
committed by GitHub
parent 32fb2315f0
commit e1693b2fef
7 changed files with 89 additions and 94 deletions

View File

@@ -50,7 +50,7 @@ You can also run browser tasks locally with Python code, with a little bit of se
from skyvern import Skyvern from skyvern import Skyvern
import asyncio 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")) asyncio.run(skyvern.run_task(prompt="Find the top post on hackernews today"))
``` ```

View File

@@ -37,7 +37,7 @@ Laminar.initialize(disabled_instruments=set([Instruments.OPENAI]))
# Configure LiteLLM to trace all LLM calls made by Skyvern # Configure LiteLLM to trace all LLM calls made by Skyvern
litellm.callbacks = [LaminarLiteLLMCallback()] litellm.callbacks = [LaminarLiteLLMCallback()]
skyvern = Skyvern() skyvern = Skyvern(api_key="YOUR_API_KEY")
async def main(): async def main():
task = await skyvern.run_task( task = await skyvern.run_task(

View File

@@ -131,7 +131,7 @@ For example, if you want to get the title, URL, and points of the top post on Ha
```python ```python
from skyvern import Skyvern from skyvern import Skyvern
skyvern = Skyvern() skyvern = Skyvern(api_key="YOUR_API_KEY")
task = await skyvern.run_task( task = await skyvern.run_task(
prompt="Find the top post on hackernews today", prompt="Find the top post on hackernews today",
data_extraction_schema={ data_extraction_schema={
@@ -159,7 +159,7 @@ When you are sending a run task request the Skyvern service, you can set the `wa
```python ```python
from skyvern import Skyvern from skyvern import Skyvern
skyvern = Skyvern() skyvern = Skyvern(api_key="YOUR_API_KEY")
task = await skyvern.run_task( task = await skyvern.run_task(
prompt="Find the top post on hackernews today", prompt="Find the top post on hackernews today",
# the request will be hanging until the task is done # 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 ```python
from skyvern import Skyvern from skyvern import Skyvern
skyvern = Skyvern() skyvern = Skyvern(api_key="YOUR_API_KEY")
task = await skyvern.run_task( task = await skyvern.run_task(
prompt="Find the top post on hackernews today", prompt="Find the top post on hackernews today",
webhook_url="https://your-webhook-url.com", webhook_url="https://your-webhook-url.com",

View File

@@ -15,7 +15,7 @@ from skyvern.schemas.runs import RunEngine
class SkyvernTaskBaseTool(BaseTool): class SkyvernTaskBaseTool(BaseTool):
engine: RunEngine = Field(default=settings.engine) engine: RunEngine = Field(default=settings.engine)
run_task_timeout_seconds: int = Field(default=settings.run_task_timeout_seconds) 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: def _run(self, *args: Any, **kwargs: Any) -> None:
raise NotImplementedError("skyvern task tool does not support sync") raise NotImplementedError("skyvern task tool does not support sync")

View File

@@ -13,7 +13,7 @@ from skyvern.schemas.runs import RunEngine
class SkyvernTool: class SkyvernTool:
def __init__(self, agent: Skyvern | None = None): def __init__(self, agent: Skyvern | None = None):
if agent is None: if agent is None:
agent = Skyvern() agent = Skyvern.local()
self.agent = agent self.agent = agent
def run_task(self) -> FunctionTool: def run_task(self) -> FunctionTool:
@@ -44,7 +44,7 @@ class SkyvernTaskToolSpec(BaseToolSpec):
run_task_timeout_seconds: int = settings.run_task_timeout_seconds, run_task_timeout_seconds: int = settings.run_task_timeout_seconds,
) -> None: ) -> None:
if agent is None: if agent is None:
agent = Skyvern() agent = Skyvern.local()
self.agent = agent self.agent = agent
self.engine = engine self.engine = engine
self.run_task_timeout_seconds = run_task_timeout_seconds self.run_task_timeout_seconds = run_task_timeout_seconds

View File

@@ -2,7 +2,7 @@ import asyncio
import os import os
import pathlib import pathlib
import tempfile import tempfile
from typing import Any, overload from typing import Any
import httpx import httpx
import structlog 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.client.types.workflow_run_response import WorkflowRunResponse
from skyvern.forge.sdk.api.llm.models import LLMConfig, LLMRouterConfig 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.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.library.skyvern_browser import SkyvernBrowser
from skyvern.schemas.run_blocks import CredentialType from skyvern.schemas.run_blocks import CredentialType
from skyvern.schemas.runs import ProxyLocation, RunEngine, RunStatus from skyvern.schemas.runs import ProxyLocation, RunEngine, RunStatus
@@ -33,13 +32,13 @@ class Skyvern(AsyncSkyvern):
Example: Example:
```python ```python
# Initialize with remote environment and API key # Remote mode: Connect to Skyvern Cloud (API key required)
skyvern = Skyvern(environment=SkyvernEnvironment.CLOUD, api_key="your-api-key") skyvern = Skyvern(api_key="your-api-key")
# Or in embedded mode (run `skyvern quickstart` first): # Local/embedded mode (run `skyvern quickstart` first):
skyvern = Skyvern() skyvern = Skyvern.local()
# Launch a local browser # Launch a local browser (works only in local environment)
browser = await skyvern.launch_local_browser(headless=False) browser = await skyvern.launch_local_browser(headless=False)
page = await browser.get_working_page() page = await browser.get_working_page()
@@ -81,12 +80,11 @@ class Skyvern(AsyncSkyvern):
``` ```
""" """
@overload
def __init__( def __init__(
self, self,
*, *,
environment: SkyvernEnvironment,
api_key: str, api_key: str,
environment: SkyvernEnvironment = SkyvernEnvironment.CLOUD,
base_url: str | None = None, base_url: str | None = None,
timeout: float | None = None, timeout: float | None = None,
follow_redirects: bool | None = True, follow_redirects: bool | None = True,
@@ -95,11 +93,11 @@ class Skyvern(AsyncSkyvern):
"""Remote mode: Connect to Skyvern Cloud or self-hosted instance. """Remote mode: Connect to Skyvern Cloud or self-hosted instance.
Args: 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. api_key: API key for authenticating with Skyvern.
Can be found on the settings page: https://app.skyvern.com/settings 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 base_url: Override the base URL for the Skyvern API. If not provided, uses the default URL for
the specified environment. the specified environment.
timeout: Timeout in seconds for API requests. If not provided, uses the default timeout. timeout: Timeout in seconds for API requests. If not provided, uses the default timeout.
@@ -107,102 +105,6 @@ class Skyvern(AsyncSkyvern):
httpx_client: Custom httpx AsyncClient for making API requests. httpx_client: Custom httpx AsyncClient for making API requests.
If not provided, a default client will be created. If not provided, a default client will be created.
""" """
...
@overload
def __init__(
self,
*,
llm_config: LLMRouterConfig | LLMConfig | None = None,
settings: dict[str, Any] | None = None,
) -> None:
"""Embedded mode: Run Skyvern locally in-process.
Prerequisites:
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).
If provided, this will be registered as "CUSTOM_LLM" and used as the primary LLM,
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):
```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(llm_config=llm_config)
```
Example 2 - 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(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"}
"""
...
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")
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.")
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}")
super().__init__( super().__init__(
base_url=base_url, base_url=base_url,
environment=environment, environment=environment,
@@ -213,10 +115,104 @@ class Skyvern(AsyncSkyvern):
) )
self._environment = environment self._environment = environment
self._api_key = api_key self._api_key: str | None = api_key
self._playwright: Playwright | None = None self._playwright: Playwright | None = None
@classmethod
def local(
cls,
*,
llm_config: LLMRouterConfig | LLMConfig | None = None,
settings: dict[str, Any] | None = None,
) -> "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
Args:
llm_config: Optional custom LLM configuration (LLMConfig or LLMRouterConfig).
If provided, this will be registered as "CUSTOM_LLM" and used as the primary LLM,
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 .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
skyvern = Skyvern.local(
llm_config=LLMConfig(
model_name="gpt-4o",
required_env_vars=["OPENAI_API_KEY"],
supports_vision=True,
add_assistant_prefix=False,
)
)
```
Example 3 - Explicitly providing credentials:
```python
from skyvern import Skyvern
from skyvern.forge.sdk.api.llm.models import LLMConfig, LiteLLMParams
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
),
)
)
```
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
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.")
obj = cls.__new__(cls)
AsyncSkyvern.__init__(
obj,
environment=SkyvernEnvironment.LOCAL,
httpx_client=create_embedded_server(
llm_config=llm_config,
settings_overrides=settings,
),
)
obj._environment = SkyvernEnvironment.LOCAL
obj._api_key = None
obj._playwright = None
return obj
async def run_task( async def run_task(
self, self,
prompt: str, prompt: str,

View File

@@ -18,7 +18,7 @@ class SkyvernBrowser(BrowserContext):
Example: Example:
```python ```python
skyvern = Skyvern() skyvern = Skyvern.local()
browser = await skyvern.launch_local_browser() browser = await skyvern.launch_local_browser()
# Get or create the working page # Get or create the working page
@@ -32,7 +32,6 @@ class SkyvernBrowser(BrowserContext):
_browser_context: The underlying Playwright BrowserContext. _browser_context: The underlying Playwright BrowserContext.
_browser_session_id: Optional session ID for persistent browser sessions. _browser_session_id: Optional session ID for persistent browser sessions.
_browser_address: Optional address for remote browser connections. _browser_address: Optional address for remote browser connections.
_client: The AsyncSkyvern client for API communication.
""" """
def __init__( def __init__(