Skip to content

Commit af25aee

Browse files
authored
use skyvern temp to save tempfiles (#1262)
1 parent 6b417d0 commit af25aee

File tree

7 files changed

+50
-17
lines changed

7 files changed

+50
-17
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,7 @@ traces/
166166
har/
167167
postgres-data
168168
files/
169+
temp/
169170

170171
# Streamlit ignores
171172
**/secrets*.toml

skyvern/config.py

+1-3
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ class Settings(BaseSettings):
1414
VIDEO_PATH: str | None = None
1515
HAR_PATH: str | None = "./har"
1616
LOG_PATH: str = "./log"
17+
TEMP_PATH: str = "./temp"
1718
BROWSER_ACTION_TIMEOUT_MS: int = 5000
1819
BROWSER_SCREENSHOT_TIMEOUT_MS: int = 20000
1920
BROWSER_LOADING_TIMEOUT_MS: int = 120000
@@ -74,9 +75,6 @@ class Settings(BaseSettings):
7475
WORKFLOW_DOWNLOAD_DIRECTORY_PARAMETER_KEY: str = "SKYVERN_DOWNLOAD_DIRECTORY"
7576
WORKFLOW_WAIT_BLOCK_MAX_SEC: int = 30 * 60
7677

77-
# streaming settings
78-
STREAMING_FILE_BASE_PATH: str = "/tmp"
79-
8078
# Saved browser session settings
8179
BROWSER_SESSION_BASE_PATH: str = f"{constants.REPO_ROOT_DIR}/browser_sessions"
8280

skyvern/forge/sdk/api/files.py

+31-2
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,14 @@
1414
from skyvern.constants import REPO_ROOT_DIR
1515
from skyvern.exceptions import DownloadFileMaxSizeExceeded
1616
from skyvern.forge.sdk.api.aws import AsyncAWSClient
17+
from skyvern.forge.sdk.settings_manager import SettingsManager
1718

1819
LOG = structlog.get_logger()
1920

2021

2122
async def download_from_s3(client: AsyncAWSClient, s3_uri: str) -> str:
2223
downloaded_bytes = await client.download_file(uri=s3_uri)
23-
file_path = tempfile.NamedTemporaryFile(delete=False)
24+
file_path = create_named_temporary_file(delete=False)
2425
file_path.write(downloaded_bytes)
2526
return file_path.name
2627

@@ -56,7 +57,7 @@ async def download_file(url: str, max_size_mb: int | None = None) -> str:
5657
a = urlparse(url)
5758

5859
# Get the file name
59-
temp_dir = tempfile.mkdtemp(prefix="skyvern_downloads_")
60+
temp_dir = make_temp_directory(prefix="skyvern_downloads_")
6061

6162
file_name = os.path.basename(a.path)
6263
# if no suffix in the URL, we need to parse it from HTTP headers
@@ -151,3 +152,31 @@ def calculate_sha256_for_file(file_path: str) -> str:
151152
for byte_block in iter(lambda: f.read(4096), b""):
152153
sha256_hash.update(byte_block)
153154
return sha256_hash.hexdigest()
155+
156+
157+
def create_folder_if_not_exist(dir: str) -> None:
158+
path = Path(dir)
159+
if path.exists():
160+
return
161+
path.mkdir(parents=True)
162+
163+
164+
def get_skyvern_temp_dir() -> str:
165+
temp_dir = SettingsManager.get_settings().TEMP_PATH
166+
create_folder_if_not_exist(temp_dir)
167+
return temp_dir
168+
169+
170+
def make_temp_directory(
171+
suffix: str | None = None,
172+
prefix: str | None = None,
173+
) -> str:
174+
temp_dir = SettingsManager.get_settings().TEMP_PATH
175+
create_folder_if_not_exist(temp_dir)
176+
return tempfile.mkdtemp(suffix=suffix, prefix=prefix, dir=temp_dir)
177+
178+
179+
def create_named_temporary_file(delete: bool = True) -> tempfile._TemporaryFileWrapper:
180+
temp_dir = SettingsManager.get_settings().TEMP_PATH
181+
create_folder_if_not_exist(temp_dir)
182+
return tempfile.NamedTemporaryFile(dir=temp_dir, delete=delete)

skyvern/forge/sdk/artifact/storage/local.py

+3-2
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
import structlog
88

9+
from skyvern.forge.sdk.api.files import get_skyvern_temp_dir
910
from skyvern.forge.sdk.artifact.models import Artifact, ArtifactType
1011
from skyvern.forge.sdk.artifact.storage.base import FILE_EXTENTSION_MAP, BaseStorage
1112
from skyvern.forge.sdk.models import Step
@@ -73,9 +74,9 @@ async def save_streaming_file(self, organization_id: str, file_name: str) -> Non
7374
return
7475

7576
async def get_streaming_file(self, organization_id: str, file_name: str, use_default: bool = True) -> bytes | None:
76-
file_path = Path(f"{SettingsManager.get_settings().STREAMING_FILE_BASE_PATH}/skyvern_screenshot.png")
77+
file_path = Path(f"{get_skyvern_temp_dir()}/skyvern_screenshot.png")
7778
if not use_default:
78-
file_path = Path(f"{SettingsManager.get_settings().STREAMING_FILE_BASE_PATH}/{organization_id}/{file_name}")
79+
file_path = Path(f"{get_skyvern_temp_dir()}/{organization_id}/{file_name}")
7980
try:
8081
with open(file_path, "rb") as f:
8182
return f.read()

skyvern/forge/sdk/artifact/storage/s3.py

+10-6
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
11
import shutil
2-
import tempfile
32
from datetime import datetime
43

54
from skyvern.config import settings
65
from skyvern.forge.sdk.api.aws import AsyncAWSClient
7-
from skyvern.forge.sdk.api.files import unzip_files
6+
from skyvern.forge.sdk.api.files import (
7+
create_named_temporary_file,
8+
get_skyvern_temp_dir,
9+
make_temp_directory,
10+
unzip_files,
11+
)
812
from skyvern.forge.sdk.artifact.models import Artifact, ArtifactType
913
from skyvern.forge.sdk.artifact.storage.base import FILE_EXTENTSION_MAP, BaseStorage
1014
from skyvern.forge.sdk.models import Step
@@ -36,7 +40,7 @@ async def store_artifact_from_path(self, artifact: Artifact, path: str) -> None:
3640
await self.async_client.upload_file_from_path(artifact.uri, path)
3741

3842
async def save_streaming_file(self, organization_id: str, file_name: str) -> None:
39-
from_path = f"{settings.STREAMING_FILE_BASE_PATH}/{organization_id}/{file_name}"
43+
from_path = f"{get_skyvern_temp_dir()}/{organization_id}/{file_name}"
4044
to_path = f"s3://{settings.AWS_S3_BUCKET_SCREENSHOTS}/{settings.ENV}/{organization_id}/{file_name}"
4145
await self.async_client.upload_file_from_path(to_path, from_path)
4246

@@ -46,7 +50,7 @@ async def get_streaming_file(self, organization_id: str, file_name: str, use_def
4650

4751
async def store_browser_session(self, organization_id: str, workflow_permanent_id: str, directory: str) -> None:
4852
# Zip the directory to a temp file
49-
temp_zip_file = tempfile.NamedTemporaryFile()
53+
temp_zip_file = create_named_temporary_file()
5054
zip_file_path = shutil.make_archive(temp_zip_file.name, "zip", directory)
5155
browser_session_uri = f"s3://{settings.AWS_S3_BUCKET_BROWSER_SESSIONS}/{settings.ENV}/{organization_id}/{workflow_permanent_id}.zip"
5256
await self.async_client.upload_file_from_path(browser_session_uri, zip_file_path)
@@ -56,11 +60,11 @@ async def retrieve_browser_session(self, organization_id: str, workflow_permanen
5660
downloaded_zip_bytes = await self.async_client.download_file(browser_session_uri, log_exception=True)
5761
if not downloaded_zip_bytes:
5862
return None
59-
temp_zip_file = tempfile.NamedTemporaryFile(delete=False)
63+
temp_zip_file = create_named_temporary_file(delete=False)
6064
temp_zip_file.write(downloaded_zip_bytes)
6165
temp_zip_file_path = temp_zip_file.name
6266

63-
temp_dir = tempfile.mkdtemp(prefix="skyvern_browser_session_")
67+
temp_dir = make_temp_directory(prefix="skyvern_browser_session_")
6468
unzip_files(temp_zip_file_path, temp_dir)
6569
temp_zip_file.close()
6670
return temp_dir

skyvern/forge/sdk/workflow/models/block.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
from email.message import EmailMessage
1212
from enum import StrEnum
1313
from pathlib import Path
14-
from tempfile import NamedTemporaryFile
1514
from typing import Annotated, Any, Literal, Union
1615

1716
import filetype
@@ -36,6 +35,7 @@
3635
from skyvern.forge.sdk.api.aws import AsyncAWSClient
3736
from skyvern.forge.sdk.api.files import (
3837
calculate_sha256_for_file,
38+
create_named_temporary_file,
3939
download_file,
4040
download_from_s3,
4141
get_path_for_workflow_download_directory,
@@ -1056,7 +1056,7 @@ def _get_file_paths(self, workflow_run_context: WorkflowRunContext, workflow_run
10561056
async def _download_from_s3(self, s3_uri: str) -> str:
10571057
client = self.get_async_aws_client()
10581058
downloaded_bytes = await client.download_file(uri=s3_uri)
1059-
file_path = NamedTemporaryFile(delete=False)
1059+
file_path = create_named_temporary_file(delete=False)
10601060
file_path.write(downloaded_bytes)
10611061
return file_path.name
10621062

skyvern/webeye/browser_factory.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
import asyncio
44
import os
5-
import tempfile
65
import time
76
import uuid
87
from datetime import datetime
@@ -24,6 +23,7 @@
2423
UnknownBrowserType,
2524
UnknownErrorWhileCreatingBrowserContext,
2625
)
26+
from skyvern.forge.sdk.api.files import make_temp_directory
2727
from skyvern.forge.sdk.core.skyvern_context import current
2828
from skyvern.forge.sdk.schemas.tasks import ProxyLocation
2929
from skyvern.forge.sdk.settings_manager import SettingsManager
@@ -153,7 +153,7 @@ def build_browser_args() -> dict[str, Any]:
153153
video_dir = f"{SettingsManager.get_settings().VIDEO_PATH}/{datetime.utcnow().strftime('%Y-%m-%d')}"
154154
har_dir = f"{SettingsManager.get_settings().HAR_PATH}/{datetime.utcnow().strftime('%Y-%m-%d')}/{BrowserContextFactory.get_subdir()}.har"
155155
return {
156-
"user_data_dir": tempfile.mkdtemp(prefix="skyvern_browser_"),
156+
"user_data_dir": make_temp_directory(prefix="skyvern_browser_"),
157157
"locale": SettingsManager.get_settings().BROWSER_LOCALE,
158158
"timezone_id": SettingsManager.get_settings().BROWSER_TIMEZONE,
159159
"color_scheme": "no-preference",

0 commit comments

Comments
 (0)