@@ -581,6 +581,56 @@ def _complete_trace(self) -> None:
581581 if self .logger :
582582 self .logger .warning (f"Error reporting trace completion: { e } " )
583583
584+ def _normalize_screenshot_data (
585+ self , screenshot_raw : str , default_format : str = "jpeg"
586+ ) -> tuple [str , str ]:
587+ """
588+ Normalize screenshot data by extracting base64 from data URL if needed.
589+
590+ Handles both formats:
591+ - Data URL: "data:image/jpeg;base64,/9j/4AAQ..."
592+ - Pure base64: "/9j/4AAQ..."
593+
594+ Args:
595+ screenshot_raw: Raw screenshot data (data URL or base64)
596+ default_format: Default format if not detected from data URL
597+
598+ Returns:
599+ Tuple of (base64_string, format_string)
600+ """
601+ if not screenshot_raw :
602+ return "" , default_format
603+
604+ # Check if it's a data URL
605+ if screenshot_raw .startswith ("data:image" ):
606+ # Extract format from "data:image/jpeg;base64,..." or "data:image/png;base64,..."
607+ try :
608+ # Split on comma to get the base64 part
609+ if "," in screenshot_raw :
610+ header , base64_data = screenshot_raw .split ("," , 1 )
611+ # Extract format from header: "data:image/jpeg;base64"
612+ if "/" in header and ";" in header :
613+ format_part = header .split ("/" )[1 ].split (";" )[0 ]
614+ if format_part in ("jpeg" , "jpg" ):
615+ return base64_data , "jpeg"
616+ elif format_part == "png" :
617+ return base64_data , "png"
618+ return base64_data , default_format
619+ else :
620+ # Malformed data URL - return as-is with warning
621+ if self .logger :
622+ self .logger .warning (
623+ "Malformed data URL in screenshot_base64 (missing comma)"
624+ )
625+ return screenshot_raw , default_format
626+ except Exception as e :
627+ if self .logger :
628+ self .logger .warning (f"Error parsing screenshot data URL: { e } " )
629+ return screenshot_raw , default_format
630+
631+ # Already pure base64
632+ return screenshot_raw , default_format
633+
584634 def _extract_screenshots_from_trace (self ) -> dict [int , dict [str , Any ]]:
585635 """
586636 Extract screenshots from trace events.
@@ -604,15 +654,22 @@ def _extract_screenshots_from_trace(self) -> dict[int, dict[str, Any]]:
604654 # Check if this is a snapshot event with screenshot
605655 if event .get ("type" ) == "snapshot" :
606656 data = event .get ("data" , {})
607- screenshot_base64 = data .get ("screenshot_base64" )
608-
609- if screenshot_base64 :
610- sequence += 1
611- screenshots [sequence ] = {
612- "base64" : screenshot_base64 ,
613- "format" : data .get ("screenshot_format" , "jpeg" ),
614- "step_id" : event .get ("step_id" ),
615- }
657+ screenshot_raw = data .get ("screenshot_base64" )
658+
659+ if screenshot_raw :
660+ # Normalize: extract base64 from data URL if needed
661+ # Handles both "data:image/jpeg;base64,..." and pure base64
662+ screenshot_base64 , screenshot_format = self ._normalize_screenshot_data (
663+ screenshot_raw ,
664+ data .get ("screenshot_format" , "jpeg" ),
665+ )
666+ if screenshot_base64 :
667+ sequence += 1
668+ screenshots [sequence ] = {
669+ "base64" : screenshot_base64 ,
670+ "format" : screenshot_format ,
671+ "step_id" : event .get ("step_id" ),
672+ }
616673 except Exception as e :
617674 if self .logger :
618675 self .logger .error (f"Error extracting screenshots: { e } " )
0 commit comments