add timezone support (#1389)

This commit is contained in:
LawyZheng
2024-12-16 11:22:51 +08:00
committed by GitHub
parent bc57dc105b
commit 9b1aeff79e
13 changed files with 86 additions and 28 deletions

View File

@@ -1310,6 +1310,7 @@ class ForgeAgent:
if not template:
raise UnsupportedTaskType(task_type=task_type)
context = skyvern_context.ensure_context()
return prompt_engine.load_prompt(
template=template,
navigation_goal=navigation_goal,
@@ -1320,7 +1321,7 @@ class ForgeAgent:
data_extraction_goal=task.data_extraction_goal,
action_history=actions_and_results_str,
error_code_mapping_str=(json.dumps(task.error_code_mapping) if task.error_code_mapping else None),
utc_datetime=datetime.utcnow().strftime("%Y-%m-%d %H:%M"),
local_datetime=datetime.now(context.tz_info).isoformat(),
verification_code_check=verification_code_check,
complete_criterion=task.complete_criterion,
terminate_criterion=task.terminate_criterion,

View File

@@ -51,7 +51,7 @@ Select History:
{{ select_history }}
```
{% endif %}
Current datetime in UTC, YYYY-MM-DD HH:MM format:
Current datetime, ISO format:
```
{{ utc_datetime }}
{{ local_datetime }}
```

View File

@@ -49,7 +49,7 @@ User details:
{{ navigation_payload_str }}
```
Current datetime in UTC, YYYY-MM-DD HH:MM format:
Current datetime, ISO format:
```
{{ utc_datetime }}
{{ local_datetime }}
```

View File

@@ -73,7 +73,7 @@ User details:
Action results from previous steps: (note: even if the action history suggests goal is achieved, check the screenshot and the DOM elements to make sure the goal is achieved)
{{ action_history }}
{% endif %}
Current datetime in UTC, YYYY-MM-DD HH:MM format:
Current datetime, ISO format:
```
{{ utc_datetime }}
{{ local_datetime }}
```

View File

@@ -26,7 +26,7 @@ Text extracted from the webpage: {{ extracted_text }}
User Navigation Payload: {{ navigation_payload }}
Current datetime in UTC, YYYY-MM-DD HH:MM format:
Current datetime, ISO format:
```
{{ utc_datetime }}
{{ local_datetime }}
```

View File

@@ -35,7 +35,7 @@ User details:
{{ navigation_payload_str }}
```
Current datetime in UTC, YYYY-MM-DD HH:MM format:
Current datetime, ISO format:
```
{{ utc_datetime }}
{{ local_datetime }}
```

View File

@@ -37,7 +37,7 @@ User details:
{{ navigation_payload_str }}
```
Current datetime in UTC, YYYY-MM-DD HH:MM format:
Current datetime, ISO format:
```
{{ utc_datetime }}
{{ local_datetime }}
```

View File

@@ -39,7 +39,7 @@ User details:
{{ navigation_payload_str }}
```
Current datetime in UTC, YYYY-MM-DD HH:MM format:
Current datetime, ISO format:
```
{{ utc_datetime }}
{{ local_datetime }}
```

View File

@@ -35,7 +35,7 @@ User details:
{{ navigation_payload_str }}
```
Current datetime in UTC, YYYY-MM-DD HH:MM format:
Current datetime, ISO format:
```
{{ utc_datetime }}
{{ local_datetime }}
```

View File

@@ -1,5 +1,6 @@
from contextvars import ContextVar
from dataclasses import dataclass, field
from zoneinfo import ZoneInfo
@dataclass
@@ -10,6 +11,7 @@ class SkyvernContext:
workflow_id: str | None = None
workflow_run_id: str | None = None
max_steps_override: int | None = None
tz_info: ZoneInfo | None = None
totp_codes: dict[str, str | None] = field(default_factory=dict)
def __repr__(self) -> str:

View File

@@ -3,6 +3,7 @@ from __future__ import annotations
from datetime import datetime
from enum import StrEnum
from typing import Any
from zoneinfo import ZoneInfo
from pydantic import BaseModel, Field, field_validator
@@ -26,6 +27,46 @@ class ProxyLocation(StrEnum):
NONE = "NONE"
def get_tzinfo_from_proxy(proxy_location: ProxyLocation) -> ZoneInfo | None:
if proxy_location == ProxyLocation.NONE:
return None
if proxy_location == ProxyLocation.US_CA:
return ZoneInfo("America/Los_Angeles")
if proxy_location == ProxyLocation.US_NY:
return ZoneInfo("America/New_York")
if proxy_location == ProxyLocation.US_TX:
return ZoneInfo("America/Chicago")
if proxy_location == ProxyLocation.US_FL:
return ZoneInfo("America/New_York")
if proxy_location == ProxyLocation.US_WA:
return ZoneInfo("America/New_York")
if proxy_location == ProxyLocation.RESIDENTIAL:
return ZoneInfo("America/New_York")
if proxy_location == ProxyLocation.RESIDENTIAL_ES:
return ZoneInfo("Europe/Madrid")
if proxy_location == ProxyLocation.RESIDENTIAL_IE:
return ZoneInfo("Europe/Dublin")
if proxy_location == ProxyLocation.RESIDENTIAL_GB:
return ZoneInfo("Europe/London")
if proxy_location == ProxyLocation.RESIDENTIAL_IN:
return ZoneInfo("Asia/Kolkata")
if proxy_location == ProxyLocation.RESIDENTIAL_JP:
return ZoneInfo("Asia/Kolkata")
return None
class TaskBase(BaseModel):
title: str | None = Field(
default=None,

View File

@@ -52,6 +52,7 @@ from skyvern.forge.prompts import prompt_engine
from skyvern.forge.sdk.api.files import download_file, get_download_dir, list_files_in_directory
from skyvern.forge.sdk.core.aiohttp_helper import aiohttp_post
from skyvern.forge.sdk.core.security import generate_skyvern_signature
from skyvern.forge.sdk.core.skyvern_context import ensure_context
from skyvern.forge.sdk.db.enums import OrganizationAuthTokenType
from skyvern.forge.sdk.models import Step
from skyvern.forge.sdk.schemas.tasks import Task
@@ -1877,6 +1878,7 @@ async def select_from_dropdown(
html = incremental_scraped.build_html_tree(element_tree=trimmed_element_tree)
skyvern_context = ensure_context()
prompt = prompt_engine.load_prompt(
"custom-select",
field_information=context.field,
@@ -1886,7 +1888,7 @@ async def select_from_dropdown(
navigation_payload_str=json.dumps(task.navigation_payload),
elements=html,
select_history=json.dumps(build_sequential_select_history(select_history)) if select_history else "",
utc_datetime=datetime.utcnow().strftime("%Y-%m-%d %H:%M"),
local_datetime=datetime.now(skyvern_context.tz_info).isoformat(),
)
LOG.info(
@@ -2449,6 +2451,8 @@ async def extract_information_for_navigation_goal(
element_tree_in_prompt: str = scraped_page.build_element_tree(element_tree_format)
scraped_page_refreshed = await scraped_page.refresh()
context = ensure_context()
extract_information_prompt = prompt_engine.load_prompt(
prompt_template,
navigation_goal=task.navigation_goal,
@@ -2459,7 +2463,7 @@ async def extract_information_for_navigation_goal(
current_url=scraped_page_refreshed.url,
extracted_text=scraped_page_refreshed.extracted_text,
error_code_mapping_str=(json.dumps(task.error_code_mapping) if task.error_code_mapping else None),
utc_datetime=datetime.utcnow().strftime("%Y-%m-%d %H:%M"),
local_datetime=datetime.now(context.tz_info).isoformat(),
)
json_response = await app.LLM_API_HANDLER(

View File

@@ -25,7 +25,7 @@ from skyvern.exceptions import (
)
from skyvern.forge.sdk.api.files import get_download_dir, make_temp_directory
from skyvern.forge.sdk.core.skyvern_context import current, ensure_context
from skyvern.forge.sdk.schemas.tasks import ProxyLocation
from skyvern.forge.sdk.schemas.tasks import ProxyLocation, get_tzinfo_from_proxy
from skyvern.webeye.utils.page import SkyvernFrame
LOG = structlog.get_logger()
@@ -128,7 +128,7 @@ def initialize_download_dir() -> str:
class BrowserContextCreator(Protocol):
def __call__(
self, playwright: Playwright, **kwargs: dict[str, Any]
self, playwright: Playwright, proxy_location: ProxyLocation | None = None, **kwargs: dict[str, Any]
) -> Awaitable[tuple[BrowserContext, BrowserArtifacts, BrowserCleanupFunc]]: ...
@@ -162,14 +162,13 @@ class BrowserContextFactory:
f.write(preference_file_content)
@staticmethod
def build_browser_args() -> dict[str, Any]:
def build_browser_args(proxy_location: ProxyLocation | None = None) -> dict[str, Any]:
video_dir = f"{settings.VIDEO_PATH}/{datetime.utcnow().strftime('%Y-%m-%d')}"
har_dir = (
f"{settings.HAR_PATH}/{datetime.utcnow().strftime('%Y-%m-%d')}/{BrowserContextFactory.get_subdir()}.har"
)
return {
args = {
"locale": settings.BROWSER_LOCALE,
"timezone_id": settings.BROWSER_TIMEZONE,
"color_scheme": "no-preference",
"args": [
"--disable-blink-features=AutomationControlled",
@@ -188,6 +187,11 @@ class BrowserContextFactory:
},
}
if proxy_location:
if tz_info := get_tzinfo_from_proxy(proxy_location=proxy_location):
args["timezone_id"] = tz_info.key
return args
@staticmethod
def build_browser_artifacts(
video_artifacts: list[VideoArtifact] | None = None,
@@ -221,6 +225,12 @@ class BrowserContextFactory:
browser_context, browser_artifacts, cleanup_func = await creator(playwright, **kwargs)
set_browser_console_log(browser_context=browser_context, browser_artifacts=browser_artifacts)
set_download_file_listener(browser_context=browser_context, **kwargs)
proxy_location: ProxyLocation | None = kwargs.get("proxy_location")
if proxy_location is not None:
context = ensure_context()
context.tz_info = get_tzinfo_from_proxy(proxy_location)
return browser_context, browser_artifacts, cleanup_func
except Exception as e:
if browser_context is not None:
@@ -279,7 +289,7 @@ class BrowserArtifacts(BaseModel):
async def _create_headless_chromium(
playwright: Playwright, **kwargs: dict
playwright: Playwright, proxy_location: ProxyLocation | None = None, **kwargs: dict
) -> tuple[BrowserContext, BrowserArtifacts, BrowserCleanupFunc]:
user_data_dir = make_temp_directory(prefix="skyvern_browser_")
download_dir = initialize_download_dir()
@@ -287,7 +297,7 @@ async def _create_headless_chromium(
user_data_dir=user_data_dir,
download_dir=download_dir,
)
browser_args = BrowserContextFactory.build_browser_args()
browser_args = BrowserContextFactory.build_browser_args(proxy_location=proxy_location)
browser_args.update(
{
"user_data_dir": user_data_dir,
@@ -301,7 +311,7 @@ async def _create_headless_chromium(
async def _create_headful_chromium(
playwright: Playwright, **kwargs: dict
playwright: Playwright, proxy_location: ProxyLocation | None = None, **kwargs: dict
) -> tuple[BrowserContext, BrowserArtifacts, BrowserCleanupFunc]:
user_data_dir = make_temp_directory(prefix="skyvern_browser_")
download_dir = initialize_download_dir()
@@ -309,7 +319,7 @@ async def _create_headful_chromium(
user_data_dir=user_data_dir,
download_dir=download_dir,
)
browser_args = BrowserContextFactory.build_browser_args()
browser_args = BrowserContextFactory.build_browser_args(proxy_location=proxy_location)
browser_args.update(
{
"user_data_dir": user_data_dir,