do not parse the dropped element twice (#2801)

This commit is contained in:
Shuchang Zheng
2025-06-27 08:46:10 +09:00
committed by GitHub
parent 3c72684c59
commit 9dd6207286
2 changed files with 32 additions and 8 deletions

View File

@@ -106,7 +106,19 @@ def _remove_skyvern_attributes(element: Dict) -> Dict:
return element_copied return element_copied
def _mark_element_as_dropped(element: dict) -> None: def _add_to_dropped_css_svg_element_map(hashed_key: str | None) -> None:
context = skyvern_context.ensure_context()
if hashed_key:
context.dropped_css_svg_element_map[hashed_key] = True
def _is_element_already_dropped(hashed_key: str) -> bool:
context = skyvern_context.ensure_context()
return hashed_key in context.dropped_css_svg_element_map
def _mark_element_as_dropped(element: dict, *, hashed_key: str | None) -> None:
_add_to_dropped_css_svg_element_map(hashed_key)
if "children" in element: if "children" in element:
del element["children"] del element["children"]
element["isDropped"] = True element["isDropped"] = True
@@ -127,7 +139,7 @@ async def _check_svg_eligibility(
return False return False
if always_drop: if always_drop:
_mark_element_as_dropped(element) _mark_element_as_dropped(element, hashed_key=None)
return False return False
task_id = task.task_id if task else None task_id = task.task_id if task else None
@@ -137,11 +149,11 @@ async def _check_svg_eligibility(
try: try:
locater = skyvern_frame.get_frame().locator(f'[{SKYVERN_ID_ATTR}="{element_id}"]') locater = skyvern_frame.get_frame().locator(f'[{SKYVERN_ID_ATTR}="{element_id}"]')
if await locater.count() == 0: if await locater.count() == 0:
_mark_element_as_dropped(element) _mark_element_as_dropped(element, hashed_key=None)
return False return False
if not await locater.is_visible(timeout=settings.BROWSER_ACTION_TIMEOUT_MS): if not await locater.is_visible(timeout=settings.BROWSER_ACTION_TIMEOUT_MS):
_mark_element_as_dropped(element) _mark_element_as_dropped(element, hashed_key=None)
return False return False
skyvern_element = SkyvernElement(locator=locater, frame=skyvern_frame.get_frame(), static_element=element) skyvern_element = SkyvernElement(locator=locater, frame=skyvern_frame.get_frame(), static_element=element)
@@ -150,7 +162,7 @@ async def _check_svg_eligibility(
await skyvern_element.get_element_handler(timeout=1000) await skyvern_element.get_element_handler(timeout=1000)
) )
if not skyvern_element.is_interactable() and blocked: if not skyvern_element.is_interactable() and blocked:
_mark_element_as_dropped(element) _mark_element_as_dropped(element, hashed_key=None)
return False return False
except Exception: except Exception:
LOG.warning( LOG.warning(
@@ -195,6 +207,11 @@ async def _convert_svg_to_string(
if svg_shape: if svg_shape:
LOG.debug("SVG loaded from cache", element_id=element_id, key=svg_key, shape=svg_shape) LOG.debug("SVG loaded from cache", element_id=element_id, key=svg_key, shape=svg_shape)
else: else:
if _is_element_already_dropped(svg_key):
LOG.debug("SVG is already dropped, going to abort conversion", element_id=element_id, key=svg_key)
_mark_element_as_dropped(element, hashed_key=svg_key)
return
if len(svg_html) > settings.SVG_MAX_LENGTH: if len(svg_html) > settings.SVG_MAX_LENGTH:
# TODO: implement a fallback solution for "too large" case, maybe convert by screenshot # TODO: implement a fallback solution for "too large" case, maybe convert by screenshot
LOG.warning( LOG.warning(
@@ -205,7 +222,7 @@ async def _convert_svg_to_string(
length=len(svg_html), length=len(svg_html),
key=svg_key, key=svg_key,
) )
_mark_element_as_dropped(element) _mark_element_as_dropped(element, hashed_key=svg_key)
return return
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)
@@ -244,7 +261,7 @@ async def _convert_svg_to_string(
element_id=element_id, element_id=element_id,
key=svg_key, key=svg_key,
) )
_mark_element_as_dropped(element) _mark_element_as_dropped(element, hashed_key=svg_key)
return return
except Exception: except Exception:
LOG.info( LOG.info(
@@ -268,7 +285,7 @@ async def _convert_svg_to_string(
key=svg_key, key=svg_key,
length=len(svg_html), length=len(svg_html),
) )
_mark_element_as_dropped(element) _mark_element_as_dropped(element, hashed_key=svg_key)
return return
element["attributes"] = dict() element["attributes"] = dict()
@@ -313,6 +330,9 @@ async def _convert_css_shape_to_string(
if css_shape: if css_shape:
LOG.debug("CSS shape loaded from cache", element_id=element_id, key=shape_key, shape=css_shape) LOG.debug("CSS shape loaded from cache", element_id=element_id, key=shape_key, shape=css_shape)
else: else:
if _is_element_already_dropped(shape_key):
LOG.debug("CSS shape is already dropped, going to abort conversion", element_id=element_id, key=shape_key)
return None
try: try:
locater = skyvern_frame.get_frame().locator(f'[{SKYVERN_ID_ATTR}="{element_id}"]') locater = skyvern_frame.get_frame().locator(f'[{SKYVERN_ID_ATTR}="{element_id}"]')
if await locater.count() == 0: if await locater.count() == 0:
@@ -399,6 +419,7 @@ async def _convert_css_shape_to_string(
element_id=element_id, element_id=element_id,
key=shape_key, key=shape_key,
) )
_add_to_dropped_css_svg_element_map(shape_key)
return None return None
except Exception: except Exception:
LOG.info( LOG.info(
@@ -422,6 +443,7 @@ async def _convert_css_shape_to_string(
element_id=element_id, element_id=element_id,
key=shape_key, key=shape_key,
) )
_add_to_dropped_css_svg_element_map(shape_key)
return None return None
except Exception: except Exception:
LOG.warning( LOG.warning(
@@ -432,6 +454,7 @@ async def _convert_css_shape_to_string(
element_id=element_id, element_id=element_id,
exc_info=True, exc_info=True,
) )
_add_to_dropped_css_svg_element_map(shape_key)
return None return None
if "attributes" not in element: if "attributes" not in element:

View File

@@ -25,6 +25,7 @@ class SkyvernContext:
refresh_working_page: bool = False refresh_working_page: bool = False
frame_index_map: dict[Frame, int] = field(default_factory=dict) frame_index_map: dict[Frame, int] = field(default_factory=dict)
max_screenshot_scrolling_times: int | None = None max_screenshot_scrolling_times: int | None = None
dropped_css_svg_element_map: dict[str, bool] = field(default_factory=dict)
def __repr__(self) -> str: def __repr__(self) -> str:
return f"SkyvernContext(request_id={self.request_id}, organization_id={self.organization_id}, task_id={self.task_id}, workflow_id={self.workflow_id}, workflow_run_id={self.workflow_run_id}, task_v2_id={self.task_v2_id}, max_steps_override={self.max_steps_override}, run_id={self.run_id})" return f"SkyvernContext(request_id={self.request_id}, organization_id={self.organization_id}, task_id={self.task_id}, workflow_id={self.workflow_id}, workflow_run_id={self.workflow_run_id}, task_v2_id={self.task_v2_id}, max_steps_override={self.max_steps_override}, run_id={self.run_id})"