optimize svg cache and add retry (#752)

This commit is contained in:
LawyZheng
2024-08-29 11:11:32 +08:00
committed by GitHub
parent 3dfef04b1e
commit f1152022a0
2 changed files with 34 additions and 17 deletions

View File

@@ -293,6 +293,11 @@ class StepUnableToExecuteError(SkyvernException):
super().__init__(f"Step {step_id} cannot be executed and task execution is stopped. Reason: {reason}") super().__init__(f"Step {step_id} cannot be executed and task execution is stopped. Reason: {reason}")
class SVGConversionFailed(SkyvernException):
def __init__(self, svg_html: str) -> None:
super().__init__(f"Failed to convert SVG after max retries. svg_html={svg_html}")
class UnsupportedActionType(SkyvernException): class UnsupportedActionType(SkyvernException):
def __init__(self, action_type: str): def __init__(self, action_type: str):
super().__init__(f"Unsupport action type: {action_type}") super().__init__(f"Unsupport action type: {action_type}")

View File

@@ -1,3 +1,4 @@
import asyncio
import copy import copy
import hashlib import hashlib
from typing import Awaitable, Callable, Dict, List from typing import Awaitable, Callable, Dict, List
@@ -6,7 +7,7 @@ import structlog
from playwright.async_api import Page from playwright.async_api import Page
from skyvern.constants import SKYVERN_ID_ATTR from skyvern.constants import SKYVERN_ID_ATTR
from skyvern.exceptions import StepUnableToExecuteError from skyvern.exceptions import StepUnableToExecuteError, SVGConversionFailed
from skyvern.forge import app from skyvern.forge import app
from skyvern.forge.async_operations import AsyncOperation from skyvern.forge.async_operations import AsyncOperation
from skyvern.forge.prompts import prompt_engine from skyvern.forge.prompts import prompt_engine
@@ -19,6 +20,9 @@ CleanupElementTreeFunc = Callable[[str, list[dict]], Awaitable[list[dict]]]
LOG = structlog.get_logger() LOG = structlog.get_logger()
USELESS_SVG_ATTRIBUTE = [SKYVERN_ID_ATTR, "id", "aria-describedby"]
SVG_RETRY_ATTEMPT = 3
def _remove_rect(element: dict) -> None: def _remove_rect(element: dict) -> None:
if "rect" in element: if "rect" in element:
@@ -38,8 +42,11 @@ def _remove_skyvern_attributes(element: Dict) -> Dict:
if element_copied.get(attr): if element_copied.get(attr):
del element_copied[attr] del element_copied[attr]
if element_copied.get("attributes") and SKYVERN_ID_ATTR in element_copied.get("attributes", {}): if "attributes" in element_copied:
del element_copied["attributes"][SKYVERN_ID_ATTR] attributes: dict = copy.deepcopy(element_copied.get("attributes", {}))
for key in attributes.keys():
if key in USELESS_SVG_ATTRIBUTE:
del element_copied["attributes"][key]
children: List[Dict] | None = element_copied.get("children", None) children: List[Dict] | None = element_copied.get("children", None)
if children is None: if children is None:
@@ -80,6 +87,8 @@ async def _convert_svg_to_string(task: Task, step: Step, organization: Organizat
else: else:
LOG.debug("call LLM to convert SVG to string shape", element_id=element_id) LOG.debug("call LLM to convert SVG to string shape", element_id=element_id)
svg_convert_prompt = prompt_engine.load_prompt("svg-convert", svg_element=svg_html) svg_convert_prompt = prompt_engine.load_prompt("svg-convert", svg_element=svg_html)
for retry in range(SVG_RETRY_ATTEMPT):
try: try:
json_response = await app.SECONDARY_LLM_API_HANDLER(prompt=svg_convert_prompt, step=step) json_response = await app.SECONDARY_LLM_API_HANDLER(prompt=svg_convert_prompt, step=step)
svg_shape = json_response.get("shape", "") svg_shape = json_response.get("shape", "")
@@ -87,13 +96,16 @@ async def _convert_svg_to_string(task: Task, step: Step, organization: Organizat
raise Exception("Empty SVG shape replied by secondary llm") raise Exception("Empty SVG shape replied by secondary llm")
LOG.info("SVG converted by LLM", element_id=element_id, shape=svg_shape) LOG.info("SVG converted by LLM", element_id=element_id, shape=svg_shape)
await app.CACHE.set(svg_key, svg_shape) await app.CACHE.set(svg_key, svg_shape)
break
except Exception: except Exception:
LOG.exception( LOG.exception(
"Failed to convert SVG to string shape by secondary llm", "Failed to convert SVG to string shape by secondary llm. Will retry if haven't met the max try attempt after 3s.",
element=element, element_id=element_id,
svg_html=svg_html, retry=retry,
) )
return await asyncio.sleep(3)
else:
raise SVGConversionFailed(svg_html=svg_html)
element["attributes"] = dict() element["attributes"] = dict()
element["attributes"]["alt"] = svg_shape element["attributes"]["alt"] = svg_shape