From 41a986dd2ab4fcaa4ac159ba6887d5fb83b63314 Mon Sep 17 00:00:00 2001 From: Lucas Giordano Date: Tue, 25 Aug 2026 15:26:27 +0200 Subject: [PATCH 1/2] feat(session): retries on execute for both local and remote sessions The retry-on-transient-failure loop around execute() is common enough that callers (and prompt-taught helpers in downstream products) keep hand-rolling it. Add client-side call options next to raise_on_failure: session.execute(type="evaluate_js", code=code, retries=3) re-runs a failed action up to `retries` extra times, sleeping `retry_delay_ms` (default 2000) between attempts. The raise_on_failure contract applies to the last attempt; every local attempt is recorded in the trajectory; setup errors (no snapshot observed, missing tool) propagate immediately and are not retried. Purely client-side: nothing new crosses the wire, remote retries are additional API calls. Co-Authored-By: Claude Fable 5 --- docs/src/sdk-reference/misc/remotesession.mdx | 4 +- .../sdk-reference/remotesession/execute.mdx | 8 + .../src/notte_browser/session.py | 438 +++++++++++++++--- .../src/notte_sdk/endpoints/sessions.py | 219 +++++++-- tests/sdk/test_execute_retries.py | 38 ++ tests/test_execute_retries.py | 79 ++++ 6 files changed, 696 insertions(+), 90 deletions(-) create mode 100644 tests/sdk/test_execute_retries.py create mode 100644 tests/test_execute_retries.py diff --git a/docs/src/sdk-reference/misc/remotesession.mdx b/docs/src/sdk-reference/misc/remotesession.mdx index db4aa6ece..b53c107aa 100644 --- a/docs/src/sdk-reference/misc/remotesession.mdx +++ b/docs/src/sdk-reference/misc/remotesession.mdx @@ -55,7 +55,7 @@ Debug information for the session. ### execute ```python -execute(action: notte_core.actions.actions.BaseAction | None = None, raise_on_failure: bool | None = None, kwargs: typing.Any) -> +execute(action: notte_core.actions.actions.BaseAction | None = None, raise_on_failure: bool | None = None, retries: = 0, retry_delay_ms: = 2000, kwargs: typing.Any) -> ``` Executes an action on the current session page @@ -63,6 +63,8 @@ Executes an action on the current session page **Parameters:** - `raise_on_failure`: If true, will raise if we could not execute the action +- `retries`: Re-run a failed action up to this many extra times, sleeping +- `retry_delay_ms`: Milliseconds to sleep between attempts. - `**kwargs`: Action fields as keyword arguments. **Returns:** diff --git a/docs/src/sdk-reference/remotesession/execute.mdx b/docs/src/sdk-reference/remotesession/execute.mdx index c5b9341d6..4152712b2 100644 --- a/docs/src/sdk-reference/remotesession/execute.mdx +++ b/docs/src/sdk-reference/remotesession/execute.mdx @@ -76,6 +76,14 @@ This syntax also supports Xpath (e.g. `xpath=/html/body/div[3]/div/button[1]`) o If true, will raise if we could not execute the action + + Re-run a failed action up to this many extra times, sleeping `retry_delay_ms` between attempts. The `raise_on_failure` contract applies to the last attempt. + + + + Milliseconds to sleep between attempts. + + diff --git a/packages/notte-browser/src/notte_browser/session.py b/packages/notte-browser/src/notte_browser/session.py index 9407e18dd..1b9e8c426 100644 --- a/packages/notte-browser/src/notte_browser/session.py +++ b/packages/notte-browser/src/notte_browser/session.py @@ -570,118 +570,260 @@ async def _action_with_vault(self, action: BaseAction) -> BaseAction: return action @overload - async def aexecute(self, action: BaseAction, *, raise_on_failure: bool | None = None) -> ExecutionResult: ... + async def aexecute( + self, action: BaseAction, *, raise_on_failure: bool | None = None, retries: int = 0, retry_delay_ms: int = 2000 + ) -> ExecutionResult: ... @overload async def aexecute( - self, *, raise_on_failure: bool | None = None, **kwargs: Unpack[FormFillActionDict] + self, + *, + raise_on_failure: bool | None = None, + retries: int = 0, + retry_delay_ms: int = 2000, + **kwargs: Unpack[FormFillActionDict], ) -> ExecutionResult: ... @overload async def aexecute( - self, *, raise_on_failure: bool | None = None, **kwargs: Unpack[GotoActionDict] + self, + *, + raise_on_failure: bool | None = None, + retries: int = 0, + retry_delay_ms: int = 2000, + **kwargs: Unpack[GotoActionDict], ) -> ExecutionResult: ... @overload async def aexecute( - self, *, raise_on_failure: bool | None = None, **kwargs: Unpack[GotoNewTabActionDict] + self, + *, + raise_on_failure: bool | None = None, + retries: int = 0, + retry_delay_ms: int = 2000, + **kwargs: Unpack[GotoNewTabActionDict], ) -> ExecutionResult: ... @overload async def aexecute( - self, *, raise_on_failure: bool | None = None, **kwargs: Unpack[CloseTabActionDict] + self, + *, + raise_on_failure: bool | None = None, + retries: int = 0, + retry_delay_ms: int = 2000, + **kwargs: Unpack[CloseTabActionDict], ) -> ExecutionResult: ... @overload async def aexecute( - self, *, raise_on_failure: bool | None = None, **kwargs: Unpack[SwitchTabActionDict] + self, + *, + raise_on_failure: bool | None = None, + retries: int = 0, + retry_delay_ms: int = 2000, + **kwargs: Unpack[SwitchTabActionDict], ) -> ExecutionResult: ... @overload async def aexecute( - self, *, raise_on_failure: bool | None = None, **kwargs: Unpack[GoBackActionDict] + self, + *, + raise_on_failure: bool | None = None, + retries: int = 0, + retry_delay_ms: int = 2000, + **kwargs: Unpack[GoBackActionDict], ) -> ExecutionResult: ... @overload async def aexecute( - self, *, raise_on_failure: bool | None = None, **kwargs: Unpack[GoForwardActionDict] + self, + *, + raise_on_failure: bool | None = None, + retries: int = 0, + retry_delay_ms: int = 2000, + **kwargs: Unpack[GoForwardActionDict], ) -> ExecutionResult: ... @overload async def aexecute( - self, *, raise_on_failure: bool | None = None, **kwargs: Unpack[ReloadActionDict] + self, + *, + raise_on_failure: bool | None = None, + retries: int = 0, + retry_delay_ms: int = 2000, + **kwargs: Unpack[ReloadActionDict], ) -> ExecutionResult: ... @overload async def aexecute( - self, *, raise_on_failure: bool | None = None, **kwargs: Unpack[WaitActionDict] + self, + *, + raise_on_failure: bool | None = None, + retries: int = 0, + retry_delay_ms: int = 2000, + **kwargs: Unpack[WaitActionDict], ) -> ExecutionResult: ... @overload async def aexecute( - self, *, raise_on_failure: bool | None = None, **kwargs: Unpack[PressKeyActionDict] + self, + *, + raise_on_failure: bool | None = None, + retries: int = 0, + retry_delay_ms: int = 2000, + **kwargs: Unpack[PressKeyActionDict], ) -> ExecutionResult: ... @overload async def aexecute( - self, *, raise_on_failure: bool | None = None, **kwargs: Unpack[ScrollUpActionDict] + self, + *, + raise_on_failure: bool | None = None, + retries: int = 0, + retry_delay_ms: int = 2000, + **kwargs: Unpack[ScrollUpActionDict], ) -> ExecutionResult: ... @overload async def aexecute( - self, *, raise_on_failure: bool | None = None, **kwargs: Unpack[ScrollDownActionDict] + self, + *, + raise_on_failure: bool | None = None, + retries: int = 0, + retry_delay_ms: int = 2000, + **kwargs: Unpack[ScrollDownActionDict], ) -> ExecutionResult: ... @overload async def aexecute( - self, *, raise_on_failure: bool | None = None, **kwargs: Unpack[CaptchaSolveActionDict] + self, + *, + raise_on_failure: bool | None = None, + retries: int = 0, + retry_delay_ms: int = 2000, + **kwargs: Unpack[CaptchaSolveActionDict], ) -> ExecutionResult: ... @overload async def aexecute( - self, *, raise_on_failure: bool | None = None, **kwargs: Unpack[HelpActionDict] + self, + *, + raise_on_failure: bool | None = None, + retries: int = 0, + retry_delay_ms: int = 2000, + **kwargs: Unpack[HelpActionDict], ) -> ExecutionResult: ... @overload async def aexecute( - self, *, raise_on_failure: bool | None = None, **kwargs: Unpack[CompletionActionDict] + self, + *, + raise_on_failure: bool | None = None, + retries: int = 0, + retry_delay_ms: int = 2000, + **kwargs: Unpack[CompletionActionDict], ) -> ExecutionResult: ... @overload async def aexecute( - self, *, raise_on_failure: bool | None = None, **kwargs: Unpack[ScrapeActionDict] + self, + *, + raise_on_failure: bool | None = None, + retries: int = 0, + retry_delay_ms: int = 2000, + **kwargs: Unpack[ScrapeActionDict], ) -> ExecutionResult: ... @overload async def aexecute( - self, *, raise_on_failure: bool | None = None, **kwargs: Unpack[EmailReadActionDict] + self, + *, + raise_on_failure: bool | None = None, + retries: int = 0, + retry_delay_ms: int = 2000, + **kwargs: Unpack[EmailReadActionDict], ) -> ExecutionResult: ... @overload async def aexecute( - self, *, raise_on_failure: bool | None = None, **kwargs: Unpack[EmailVerificationReadActionDict] + self, + *, + raise_on_failure: bool | None = None, + retries: int = 0, + retry_delay_ms: int = 2000, + **kwargs: Unpack[EmailVerificationReadActionDict], ) -> ExecutionResult: ... @overload async def aexecute( - self, *, raise_on_failure: bool | None = None, **kwargs: Unpack[SmsReadActionDict] + self, + *, + raise_on_failure: bool | None = None, + retries: int = 0, + retry_delay_ms: int = 2000, + **kwargs: Unpack[SmsReadActionDict], ) -> ExecutionResult: ... @overload async def aexecute( - self, *, raise_on_failure: bool | None = None, **kwargs: Unpack[EvaluateJsActionDict] + self, + *, + raise_on_failure: bool | None = None, + retries: int = 0, + retry_delay_ms: int = 2000, + **kwargs: Unpack[EvaluateJsActionDict], ) -> ExecutionResult: ... @overload async def aexecute( - self, *, raise_on_failure: bool | None = None, **kwargs: Unpack[ClickActionDict] + self, + *, + raise_on_failure: bool | None = None, + retries: int = 0, + retry_delay_ms: int = 2000, + **kwargs: Unpack[ClickActionDict], ) -> ExecutionResult: ... @overload async def aexecute( - self, *, raise_on_failure: bool | None = None, **kwargs: Unpack[FillActionDict] + self, + *, + raise_on_failure: bool | None = None, + retries: int = 0, + retry_delay_ms: int = 2000, + **kwargs: Unpack[FillActionDict], ) -> ExecutionResult: ... @overload async def aexecute( - self, *, raise_on_failure: bool | None = None, **kwargs: Unpack[MultiFactorFillActionDict] + self, + *, + raise_on_failure: bool | None = None, + retries: int = 0, + retry_delay_ms: int = 2000, + **kwargs: Unpack[MultiFactorFillActionDict], ) -> ExecutionResult: ... @overload async def aexecute( - self, *, raise_on_failure: bool | None = None, **kwargs: Unpack[FallbackFillActionDict] + self, + *, + raise_on_failure: bool | None = None, + retries: int = 0, + retry_delay_ms: int = 2000, + **kwargs: Unpack[FallbackFillActionDict], ) -> ExecutionResult: ... @overload async def aexecute( - self, *, raise_on_failure: bool | None = None, **kwargs: Unpack[CheckActionDict] + self, + *, + raise_on_failure: bool | None = None, + retries: int = 0, + retry_delay_ms: int = 2000, + **kwargs: Unpack[CheckActionDict], ) -> ExecutionResult: ... @overload async def aexecute( - self, *, raise_on_failure: bool | None = None, **kwargs: Unpack[SelectDropdownOptionActionDict] + self, + *, + raise_on_failure: bool | None = None, + retries: int = 0, + retry_delay_ms: int = 2000, + **kwargs: Unpack[SelectDropdownOptionActionDict], ) -> ExecutionResult: ... @overload async def aexecute( - self, *, raise_on_failure: bool | None = None, **kwargs: Unpack[UploadFileActionDict] + self, + *, + raise_on_failure: bool | None = None, + retries: int = 0, + retry_delay_ms: int = 2000, + **kwargs: Unpack[UploadFileActionDict], ) -> ExecutionResult: ... @overload async def aexecute( - self, *, raise_on_failure: bool | None = None, **kwargs: Unpack[DownloadFileActionDict] + self, + *, + raise_on_failure: bool | None = None, + retries: int = 0, + retry_delay_ms: int = 2000, + **kwargs: Unpack[DownloadFileActionDict], ) -> ExecutionResult: ... @timeit("aexecute") @@ -691,14 +833,32 @@ async def aexecute( action: BaseAction | None = None, *, raise_on_failure: bool | None = None, + retries: int = 0, + retry_delay_ms: int = 2000, **kwargs: Any, ) -> ExecutionResult: """ Execute an action, either by passing a BaseAction as the first argument, or by passing action fields as kwargs. + + `retries` re-runs a failed action up to that many extra times, sleeping + `retry_delay_ms` between attempts. Every attempt is recorded in the + trajectory; the `raise_on_failure` contract applies to the last one. + Setup errors (e.g. no snapshot observed) propagate immediately. """ # Profile with action type attribute async with profiler.profile("aexecute", service_name="execution") as span: - result = await self._aexecute_impl(action, raise_on_failure=raise_on_failure, **kwargs) + _raise_on_failure = raise_on_failure if raise_on_failure is not None else self.default_raise_on_failure + result: ExecutionResult | None = None + for attempt in range(max(retries, 0) + 1): + final_attempt = attempt >= retries + result = await self._aexecute_impl( + action, raise_on_failure=_raise_on_failure if final_attempt else False, **kwargs + ) + if result.success: + break + if not final_attempt: + await asyncio.sleep(retry_delay_ms / 1000) + assert result is not None if span is not None and result.action is not None: span.set_attribute("action_type", result.action.type) return result @@ -926,110 +1086,260 @@ def execute_saved_actions(self, actions_file: str) -> None: logger.info("🎉 All actions executed successfully") @overload - def execute(self, action: BaseAction, *, raise_on_failure: bool | None = None) -> ExecutionResult: ... + def execute( + self, action: BaseAction, *, raise_on_failure: bool | None = None, retries: int = 0, retry_delay_ms: int = 2000 + ) -> ExecutionResult: ... @overload def execute( - self, *, raise_on_failure: bool | None = None, **kwargs: Unpack[FormFillActionDict] + self, + *, + raise_on_failure: bool | None = None, + retries: int = 0, + retry_delay_ms: int = 2000, + **kwargs: Unpack[FormFillActionDict], ) -> ExecutionResult: ... @overload - def execute(self, *, raise_on_failure: bool | None = None, **kwargs: Unpack[GotoActionDict]) -> ExecutionResult: ... + def execute( + self, + *, + raise_on_failure: bool | None = None, + retries: int = 0, + retry_delay_ms: int = 2000, + **kwargs: Unpack[GotoActionDict], + ) -> ExecutionResult: ... @overload def execute( - self, *, raise_on_failure: bool | None = None, **kwargs: Unpack[GotoNewTabActionDict] + self, + *, + raise_on_failure: bool | None = None, + retries: int = 0, + retry_delay_ms: int = 2000, + **kwargs: Unpack[GotoNewTabActionDict], ) -> ExecutionResult: ... @overload def execute( - self, *, raise_on_failure: bool | None = None, **kwargs: Unpack[CloseTabActionDict] + self, + *, + raise_on_failure: bool | None = None, + retries: int = 0, + retry_delay_ms: int = 2000, + **kwargs: Unpack[CloseTabActionDict], ) -> ExecutionResult: ... @overload def execute( - self, *, raise_on_failure: bool | None = None, **kwargs: Unpack[SwitchTabActionDict] + self, + *, + raise_on_failure: bool | None = None, + retries: int = 0, + retry_delay_ms: int = 2000, + **kwargs: Unpack[SwitchTabActionDict], ) -> ExecutionResult: ... @overload def execute( - self, *, raise_on_failure: bool | None = None, **kwargs: Unpack[GoBackActionDict] + self, + *, + raise_on_failure: bool | None = None, + retries: int = 0, + retry_delay_ms: int = 2000, + **kwargs: Unpack[GoBackActionDict], ) -> ExecutionResult: ... @overload def execute( - self, *, raise_on_failure: bool | None = None, **kwargs: Unpack[GoForwardActionDict] + self, + *, + raise_on_failure: bool | None = None, + retries: int = 0, + retry_delay_ms: int = 2000, + **kwargs: Unpack[GoForwardActionDict], ) -> ExecutionResult: ... @overload def execute( - self, *, raise_on_failure: bool | None = None, **kwargs: Unpack[ReloadActionDict] + self, + *, + raise_on_failure: bool | None = None, + retries: int = 0, + retry_delay_ms: int = 2000, + **kwargs: Unpack[ReloadActionDict], ) -> ExecutionResult: ... @overload - def execute(self, *, raise_on_failure: bool | None = None, **kwargs: Unpack[WaitActionDict]) -> ExecutionResult: ... + def execute( + self, + *, + raise_on_failure: bool | None = None, + retries: int = 0, + retry_delay_ms: int = 2000, + **kwargs: Unpack[WaitActionDict], + ) -> ExecutionResult: ... @overload def execute( - self, *, raise_on_failure: bool | None = None, **kwargs: Unpack[PressKeyActionDict] + self, + *, + raise_on_failure: bool | None = None, + retries: int = 0, + retry_delay_ms: int = 2000, + **kwargs: Unpack[PressKeyActionDict], ) -> ExecutionResult: ... @overload def execute( - self, *, raise_on_failure: bool | None = None, **kwargs: Unpack[ScrollUpActionDict] + self, + *, + raise_on_failure: bool | None = None, + retries: int = 0, + retry_delay_ms: int = 2000, + **kwargs: Unpack[ScrollUpActionDict], ) -> ExecutionResult: ... @overload def execute( - self, *, raise_on_failure: bool | None = None, **kwargs: Unpack[ScrollDownActionDict] + self, + *, + raise_on_failure: bool | None = None, + retries: int = 0, + retry_delay_ms: int = 2000, + **kwargs: Unpack[ScrollDownActionDict], ) -> ExecutionResult: ... @overload def execute( - self, *, raise_on_failure: bool | None = None, **kwargs: Unpack[CaptchaSolveActionDict] + self, + *, + raise_on_failure: bool | None = None, + retries: int = 0, + retry_delay_ms: int = 2000, + **kwargs: Unpack[CaptchaSolveActionDict], ) -> ExecutionResult: ... @overload - def execute(self, *, raise_on_failure: bool | None = None, **kwargs: Unpack[HelpActionDict]) -> ExecutionResult: ... + def execute( + self, + *, + raise_on_failure: bool | None = None, + retries: int = 0, + retry_delay_ms: int = 2000, + **kwargs: Unpack[HelpActionDict], + ) -> ExecutionResult: ... @overload def execute( - self, *, raise_on_failure: bool | None = None, **kwargs: Unpack[CompletionActionDict] + self, + *, + raise_on_failure: bool | None = None, + retries: int = 0, + retry_delay_ms: int = 2000, + **kwargs: Unpack[CompletionActionDict], ) -> ExecutionResult: ... @overload def execute( - self, *, raise_on_failure: bool | None = None, **kwargs: Unpack[ScrapeActionDict] + self, + *, + raise_on_failure: bool | None = None, + retries: int = 0, + retry_delay_ms: int = 2000, + **kwargs: Unpack[ScrapeActionDict], ) -> ExecutionResult: ... @overload def execute( - self, *, raise_on_failure: bool | None = None, **kwargs: Unpack[EmailReadActionDict] + self, + *, + raise_on_failure: bool | None = None, + retries: int = 0, + retry_delay_ms: int = 2000, + **kwargs: Unpack[EmailReadActionDict], ) -> ExecutionResult: ... @overload def execute( - self, *, raise_on_failure: bool | None = None, **kwargs: Unpack[EmailVerificationReadActionDict] + self, + *, + raise_on_failure: bool | None = None, + retries: int = 0, + retry_delay_ms: int = 2000, + **kwargs: Unpack[EmailVerificationReadActionDict], ) -> ExecutionResult: ... @overload def execute( - self, *, raise_on_failure: bool | None = None, **kwargs: Unpack[SmsReadActionDict] + self, + *, + raise_on_failure: bool | None = None, + retries: int = 0, + retry_delay_ms: int = 2000, + **kwargs: Unpack[SmsReadActionDict], ) -> ExecutionResult: ... @overload def execute( - self, *, raise_on_failure: bool | None = None, **kwargs: Unpack[EvaluateJsActionDict] + self, + *, + raise_on_failure: bool | None = None, + retries: int = 0, + retry_delay_ms: int = 2000, + **kwargs: Unpack[EvaluateJsActionDict], ) -> ExecutionResult: ... @overload def execute( - self, *, raise_on_failure: bool | None = None, **kwargs: Unpack[ClickActionDict] + self, + *, + raise_on_failure: bool | None = None, + retries: int = 0, + retry_delay_ms: int = 2000, + **kwargs: Unpack[ClickActionDict], ) -> ExecutionResult: ... @overload - def execute(self, *, raise_on_failure: bool | None = None, **kwargs: Unpack[FillActionDict]) -> ExecutionResult: ... + def execute( + self, + *, + raise_on_failure: bool | None = None, + retries: int = 0, + retry_delay_ms: int = 2000, + **kwargs: Unpack[FillActionDict], + ) -> ExecutionResult: ... @overload def execute( - self, *, raise_on_failure: bool | None = None, **kwargs: Unpack[MultiFactorFillActionDict] + self, + *, + raise_on_failure: bool | None = None, + retries: int = 0, + retry_delay_ms: int = 2000, + **kwargs: Unpack[MultiFactorFillActionDict], ) -> ExecutionResult: ... @overload def execute( - self, *, raise_on_failure: bool | None = None, **kwargs: Unpack[FallbackFillActionDict] + self, + *, + raise_on_failure: bool | None = None, + retries: int = 0, + retry_delay_ms: int = 2000, + **kwargs: Unpack[FallbackFillActionDict], ) -> ExecutionResult: ... @overload def execute( - self, *, raise_on_failure: bool | None = None, **kwargs: Unpack[CheckActionDict] + self, + *, + raise_on_failure: bool | None = None, + retries: int = 0, + retry_delay_ms: int = 2000, + **kwargs: Unpack[CheckActionDict], ) -> ExecutionResult: ... @overload def execute( - self, *, raise_on_failure: bool | None = None, **kwargs: Unpack[SelectDropdownOptionActionDict] + self, + *, + raise_on_failure: bool | None = None, + retries: int = 0, + retry_delay_ms: int = 2000, + **kwargs: Unpack[SelectDropdownOptionActionDict], ) -> ExecutionResult: ... @overload def execute( - self, *, raise_on_failure: bool | None = None, **kwargs: Unpack[UploadFileActionDict] + self, + *, + raise_on_failure: bool | None = None, + retries: int = 0, + retry_delay_ms: int = 2000, + **kwargs: Unpack[UploadFileActionDict], ) -> ExecutionResult: ... @overload def execute( - self, *, raise_on_failure: bool | None = None, **kwargs: Unpack[DownloadFileActionDict] + self, + *, + raise_on_failure: bool | None = None, + retries: int = 0, + retry_delay_ms: int = 2000, + **kwargs: Unpack[DownloadFileActionDict], ) -> ExecutionResult: ... def execute( @@ -1037,6 +1347,8 @@ def execute( action: BaseAction | None = None, *, raise_on_failure: bool | None = None, + retries: int = 0, + retry_delay_ms: int = 2000, **kwargs: Any, ) -> ExecutionResult: """ @@ -1044,7 +1356,13 @@ def execute( """ return asyncio.run( - self.aexecute(action=action, raise_on_failure=raise_on_failure, **kwargs) # pyright: ignore [reportArgumentType] + self.aexecute( + action=action, # pyright: ignore [reportArgumentType] + raise_on_failure=raise_on_failure, + retries=retries, + retry_delay_ms=retry_delay_ms, + **kwargs, + ) ) @overload diff --git a/packages/notte-sdk/src/notte_sdk/endpoints/sessions.py b/packages/notte-sdk/src/notte_sdk/endpoints/sessions.py index efe3c9536..28e8a0c84 100644 --- a/packages/notte-sdk/src/notte_sdk/endpoints/sessions.py +++ b/packages/notte-sdk/src/notte_sdk/endpoints/sessions.py @@ -1378,116 +1378,268 @@ def observe(self, **data: Unpack[ObserveRequestDict]) -> ObserveResponse | list[ @overload def execute( - self, *, raise_on_failure: bool | None = None, **kwargs: Unpack[FormFillActionDict] + self, + *, + raise_on_failure: bool | None = None, + retries: int = 0, + retry_delay_ms: int = 2000, + **kwargs: Unpack[FormFillActionDict], ) -> ExecutionResult: ... @overload - def execute(self, *, raise_on_failure: bool | None = None, **kwargs: Unpack[GotoActionDict]) -> ExecutionResult: ... + def execute( + self, + *, + raise_on_failure: bool | None = None, + retries: int = 0, + retry_delay_ms: int = 2000, + **kwargs: Unpack[GotoActionDict], + ) -> ExecutionResult: ... @overload def execute( - self, *, raise_on_failure: bool | None = None, **kwargs: Unpack[GotoNewTabActionDict] + self, + *, + raise_on_failure: bool | None = None, + retries: int = 0, + retry_delay_ms: int = 2000, + **kwargs: Unpack[GotoNewTabActionDict], ) -> ExecutionResult: ... @overload def execute( - self, *, raise_on_failure: bool | None = None, **kwargs: Unpack[CloseTabActionDict] + self, + *, + raise_on_failure: bool | None = None, + retries: int = 0, + retry_delay_ms: int = 2000, + **kwargs: Unpack[CloseTabActionDict], ) -> ExecutionResult: ... @overload def execute( - self, *, raise_on_failure: bool | None = None, **kwargs: Unpack[SwitchTabActionDict] + self, + *, + raise_on_failure: bool | None = None, + retries: int = 0, + retry_delay_ms: int = 2000, + **kwargs: Unpack[SwitchTabActionDict], ) -> ExecutionResult: ... @overload def execute( - self, *, raise_on_failure: bool | None = None, **kwargs: Unpack[GoBackActionDict] + self, + *, + raise_on_failure: bool | None = None, + retries: int = 0, + retry_delay_ms: int = 2000, + **kwargs: Unpack[GoBackActionDict], ) -> ExecutionResult: ... @overload def execute( - self, *, raise_on_failure: bool | None = None, **kwargs: Unpack[GoForwardActionDict] + self, + *, + raise_on_failure: bool | None = None, + retries: int = 0, + retry_delay_ms: int = 2000, + **kwargs: Unpack[GoForwardActionDict], ) -> ExecutionResult: ... @overload def execute( - self, *, raise_on_failure: bool | None = None, **kwargs: Unpack[ReloadActionDict] + self, + *, + raise_on_failure: bool | None = None, + retries: int = 0, + retry_delay_ms: int = 2000, + **kwargs: Unpack[ReloadActionDict], ) -> ExecutionResult: ... @overload - def execute(self, *, raise_on_failure: bool | None = None, **kwargs: Unpack[WaitActionDict]) -> ExecutionResult: ... + def execute( + self, + *, + raise_on_failure: bool | None = None, + retries: int = 0, + retry_delay_ms: int = 2000, + **kwargs: Unpack[WaitActionDict], + ) -> ExecutionResult: ... @overload def execute( - self, *, raise_on_failure: bool | None = None, **kwargs: Unpack[PressKeyActionDict] + self, + *, + raise_on_failure: bool | None = None, + retries: int = 0, + retry_delay_ms: int = 2000, + **kwargs: Unpack[PressKeyActionDict], ) -> ExecutionResult: ... @overload def execute( - self, *, raise_on_failure: bool | None = None, **kwargs: Unpack[ScrollUpActionDict] + self, + *, + raise_on_failure: bool | None = None, + retries: int = 0, + retry_delay_ms: int = 2000, + **kwargs: Unpack[ScrollUpActionDict], ) -> ExecutionResult: ... @overload def execute( - self, *, raise_on_failure: bool | None = None, **kwargs: Unpack[ScrollDownActionDict] + self, + *, + raise_on_failure: bool | None = None, + retries: int = 0, + retry_delay_ms: int = 2000, + **kwargs: Unpack[ScrollDownActionDict], ) -> ExecutionResult: ... @overload def execute( - self, *, raise_on_failure: bool | None = None, **kwargs: Unpack[CaptchaSolveActionDict] + self, + *, + raise_on_failure: bool | None = None, + retries: int = 0, + retry_delay_ms: int = 2000, + **kwargs: Unpack[CaptchaSolveActionDict], ) -> ExecutionResult: ... @overload - def execute(self, *, raise_on_failure: bool | None = None, **kwargs: Unpack[HelpActionDict]) -> ExecutionResult: ... + def execute( + self, + *, + raise_on_failure: bool | None = None, + retries: int = 0, + retry_delay_ms: int = 2000, + **kwargs: Unpack[HelpActionDict], + ) -> ExecutionResult: ... @overload def execute( - self, *, raise_on_failure: bool | None = None, **kwargs: Unpack[CompletionActionDict] + self, + *, + raise_on_failure: bool | None = None, + retries: int = 0, + retry_delay_ms: int = 2000, + **kwargs: Unpack[CompletionActionDict], ) -> ExecutionResult: ... @overload def execute( - self, *, raise_on_failure: bool | None = None, **kwargs: Unpack[ScrapeActionDict] + self, + *, + raise_on_failure: bool | None = None, + retries: int = 0, + retry_delay_ms: int = 2000, + **kwargs: Unpack[ScrapeActionDict], ) -> ExecutionResult: ... @overload def execute( - self, *, raise_on_failure: bool | None = None, **kwargs: Unpack[EmailReadActionDict] + self, + *, + raise_on_failure: bool | None = None, + retries: int = 0, + retry_delay_ms: int = 2000, + **kwargs: Unpack[EmailReadActionDict], ) -> ExecutionResult: ... @overload def execute( - self, *, raise_on_failure: bool | None = None, **kwargs: Unpack[EmailVerificationReadActionDict] + self, + *, + raise_on_failure: bool | None = None, + retries: int = 0, + retry_delay_ms: int = 2000, + **kwargs: Unpack[EmailVerificationReadActionDict], ) -> ExecutionResult: ... @overload def execute( - self, *, raise_on_failure: bool | None = None, **kwargs: Unpack[SmsReadActionDict] + self, + *, + raise_on_failure: bool | None = None, + retries: int = 0, + retry_delay_ms: int = 2000, + **kwargs: Unpack[SmsReadActionDict], ) -> ExecutionResult: ... @overload def execute( - self, *, raise_on_failure: bool | None = None, **kwargs: Unpack[EvaluateJsActionDict] + self, + *, + raise_on_failure: bool | None = None, + retries: int = 0, + retry_delay_ms: int = 2000, + **kwargs: Unpack[EvaluateJsActionDict], ) -> ExecutionResult: ... @overload def execute( - self, *, raise_on_failure: bool | None = None, **kwargs: Unpack[ClickActionDict] + self, + *, + raise_on_failure: bool | None = None, + retries: int = 0, + retry_delay_ms: int = 2000, + **kwargs: Unpack[ClickActionDict], ) -> ExecutionResult: ... @overload - def execute(self, *, raise_on_failure: bool | None = None, **kwargs: Unpack[FillActionDict]) -> ExecutionResult: ... + def execute( + self, + *, + raise_on_failure: bool | None = None, + retries: int = 0, + retry_delay_ms: int = 2000, + **kwargs: Unpack[FillActionDict], + ) -> ExecutionResult: ... @overload def execute( - self, *, raise_on_failure: bool | None = None, **kwargs: Unpack[MultiFactorFillActionDict] + self, + *, + raise_on_failure: bool | None = None, + retries: int = 0, + retry_delay_ms: int = 2000, + **kwargs: Unpack[MultiFactorFillActionDict], ) -> ExecutionResult: ... @overload def execute( - self, *, raise_on_failure: bool | None = None, **kwargs: Unpack[FallbackFillActionDict] + self, + *, + raise_on_failure: bool | None = None, + retries: int = 0, + retry_delay_ms: int = 2000, + **kwargs: Unpack[FallbackFillActionDict], ) -> ExecutionResult: ... @overload def execute( - self, *, raise_on_failure: bool | None = None, **kwargs: Unpack[CheckActionDict] + self, + *, + raise_on_failure: bool | None = None, + retries: int = 0, + retry_delay_ms: int = 2000, + **kwargs: Unpack[CheckActionDict], ) -> ExecutionResult: ... @overload def execute( - self, *, raise_on_failure: bool | None = None, **kwargs: Unpack[SelectDropdownOptionActionDict] + self, + *, + raise_on_failure: bool | None = None, + retries: int = 0, + retry_delay_ms: int = 2000, + **kwargs: Unpack[SelectDropdownOptionActionDict], ) -> ExecutionResult: ... @overload def execute( - self, *, raise_on_failure: bool | None = None, **kwargs: Unpack[UploadFileActionDict] + self, + *, + raise_on_failure: bool | None = None, + retries: int = 0, + retry_delay_ms: int = 2000, + **kwargs: Unpack[UploadFileActionDict], ) -> ExecutionResult: ... @overload def execute( - self, *, raise_on_failure: bool | None = None, **kwargs: Unpack[DownloadFileActionDict] + self, + *, + raise_on_failure: bool | None = None, + retries: int = 0, + retry_delay_ms: int = 2000, + **kwargs: Unpack[DownloadFileActionDict], ) -> ExecutionResult: ... @overload - def execute(self, action: BaseAction, *, raise_on_failure: bool | None = None) -> ExecutionResult: ... + def execute( + self, action: BaseAction, *, raise_on_failure: bool | None = None, retries: int = 0, retry_delay_ms: int = 2000 + ) -> ExecutionResult: ... def execute( self, action: BaseAction | None = None, *, raise_on_failure: bool | None = None, + retries: int = 0, + retry_delay_ms: int = 2000, **kwargs: Any, ) -> ExecutionResult: """ @@ -1554,6 +1706,10 @@ def execute( Args: raise_on_failure: If true, will raise if we could not execute the action + retries: Re-run a failed action up to this many extra times, sleeping + `retry_delay_ms` between attempts. The `raise_on_failure` contract + applies to the last attempt. + retry_delay_ms: Milliseconds to sleep between attempts. **kwargs: Action fields as keyword arguments. Returns: @@ -1578,6 +1734,11 @@ def execute( action_obj = ExecutionRequest.get_action(action=action, data=None) # pyright: ignore [reportUnreachable] result = self.client.page.execute(session_id=self.session_id, action=action_obj) + for _ in range(max(retries, 0)): + if result.success: + break + time.sleep(retry_delay_ms / 1000) + result = self.client.page.execute(session_id=self.session_id, action=action_obj) # raise exception if needed _raise_on_failure = raise_on_failure if raise_on_failure is not None else self.default_raise_on_failure # Gate on "did the action fail", not "did something throw", to mirror the local session. diff --git a/tests/sdk/test_execute_retries.py b/tests/sdk/test_execute_retries.py new file mode 100644 index 000000000..ab2b91828 --- /dev/null +++ b/tests/sdk/test_execute_retries.py @@ -0,0 +1,38 @@ +"""`retries=` on RemoteSession.execute: re-run server-reported failures client side.""" + +import pytest +from notte_core.errors.base import NotteBaseError + +from tests.sdk.test_execute_raise_on_failure import ACTION, over_the_wire, remote_session, server_side_result + + +def test_retries_rerun_until_the_server_reports_success() -> None: + failed = over_the_wire(server_side_result(exception=None)) + ok = failed.model_copy(update={"success": True, "exception": None, "exception_detail": None}) + session = remote_session(failed) + session.client.page.execute.side_effect = [failed, failed, ok] # pyright: ignore[reportAttributeAccessIssue] + + result = session.execute(ACTION, retries=2, retry_delay_ms=0) + + assert result.success is True + assert session.client.page.execute.call_count == 3 # pyright: ignore[reportAttributeAccessIssue] + + +def test_retries_exhausted_applies_the_raise_gate() -> None: + failed = over_the_wire(server_side_result(exception=None)) + session = remote_session(failed) + + with pytest.raises(NotteBaseError): + _ = session.execute(ACTION, retries=2, retry_delay_ms=0) + + assert session.client.page.execute.call_count == 3 # pyright: ignore[reportAttributeAccessIssue] + + +def test_retries_exhausted_returns_failed_result_when_not_raising() -> None: + failed = over_the_wire(server_side_result(exception=None)) + session = remote_session(failed, raise_on_failure=False) + + result = session.execute(ACTION, retries=1, retry_delay_ms=0) + + assert result.success is False + assert session.client.page.execute.call_count == 2 # pyright: ignore[reportAttributeAccessIssue] diff --git a/tests/test_execute_retries.py b/tests/test_execute_retries.py new file mode 100644 index 000000000..e2c079f73 --- /dev/null +++ b/tests/test_execute_retries.py @@ -0,0 +1,79 @@ +"""`retries=` on execute: re-run a failed action, apply the raise gate to the last attempt.""" + +from typing import Any + +import pytest +from notte_browser.session import NotteSession +from notte_core.errors.actions import ActionExecutionError + + +@pytest.mark.asyncio +async def test_retries_rerun_a_failed_action_until_it_succeeds() -> None: + calls = 0 + + async def flaky(*_args: Any, **_kwargs: Any) -> bool: + nonlocal calls + calls += 1 + return calls >= 3 + + async with NotteSession(headless=True) as session: + session.controller.execute = flaky # pyright: ignore[reportAttributeAccessIssue, reportMethodAssign] + + result = await session.aexecute(type="wait", time_ms=1, retries=2, retry_delay_ms=0) + + assert result.success is True + assert calls == 3 + + +@pytest.mark.asyncio +async def test_retries_exhausted_applies_the_raise_gate() -> None: + calls = 0 + + async def failing(*_args: Any, **_kwargs: Any) -> bool: + nonlocal calls + calls += 1 + return False + + async with NotteSession(headless=True) as session: + session.controller.execute = failing # pyright: ignore[reportAttributeAccessIssue, reportMethodAssign] + + with pytest.raises(ActionExecutionError): + _ = await session.aexecute(type="wait", time_ms=1, retries=2, retry_delay_ms=0) + + assert calls == 3 + + +@pytest.mark.asyncio +async def test_retries_exhausted_returns_failed_result_when_not_raising() -> None: + calls = 0 + + async def failing(*_args: Any, **_kwargs: Any) -> bool: + nonlocal calls + calls += 1 + return False + + async with NotteSession(headless=True) as session: + session.controller.execute = failing # pyright: ignore[reportAttributeAccessIssue, reportMethodAssign] + + result = await session.aexecute(type="wait", time_ms=1, retries=1, retry_delay_ms=0, raise_on_failure=False) + + assert result.success is False + assert calls == 2 + + +@pytest.mark.asyncio +async def test_no_retries_is_a_single_attempt() -> None: + calls = 0 + + async def failing(*_args: Any, **_kwargs: Any) -> bool: + nonlocal calls + calls += 1 + return False + + async with NotteSession(headless=True) as session: + session.controller.execute = failing # pyright: ignore[reportAttributeAccessIssue, reportMethodAssign] + + result = await session.aexecute(type="wait", time_ms=1, raise_on_failure=False) + + assert result.success is False + assert calls == 1 From 09660a65d375ae8c795eadeee5763327cef35bc2 Mon Sep 17 00:00:00 2001 From: Lucas Giordano Date: Tue, 25 Aug 2026 15:29:39 +0200 Subject: [PATCH 2/2] refactor(session): share the execute retry defaults as named constants EXECUTE_RETRY_DEFAULT and EXECUTE_RETRY_DELAY_MS live in notte_core.common.config, the common root of both packages, so the local and remote sessions cannot drift and callers can import the values instead of hardcoding them. Co-Authored-By: Claude Fable 5 --- .../src/notte_browser/session.py | 257 ++++++++++-------- .../src/notte_core/common/config.py | 5 + .../src/notte_sdk/endpoints/sessions.py | 125 +++++---- 3 files changed, 208 insertions(+), 179 deletions(-) diff --git a/packages/notte-browser/src/notte_browser/session.py b/packages/notte-browser/src/notte_browser/session.py index 1b9e8c426..fe0000707 100644 --- a/packages/notte-browser/src/notte_browser/session.py +++ b/packages/notte-browser/src/notte_browser/session.py @@ -58,7 +58,16 @@ ) from notte_core.browser.observation import ExecutionResult, Observation, Screenshot, TimedSpan from notte_core.browser.snapshot import BrowserSnapshot -from notte_core.common.config import BrowserBackend, CookieDict, PerceptionType, RaiseCondition, ScreenshotType, config +from notte_core.common.config import ( + EXECUTE_RETRY_DEFAULT, + EXECUTE_RETRY_DELAY_MS, + BrowserBackend, + CookieDict, + PerceptionType, + RaiseCondition, + ScreenshotType, + config, +) from notte_core.common.logging import logger, timeit from notte_core.common.resource import AsyncResource, SyncResource from notte_core.common.telemetry import track_usage @@ -571,15 +580,20 @@ async def _action_with_vault(self, action: BaseAction) -> BaseAction: @overload async def aexecute( - self, action: BaseAction, *, raise_on_failure: bool | None = None, retries: int = 0, retry_delay_ms: int = 2000 + self, + action: BaseAction, + *, + raise_on_failure: bool | None = None, + retries: int = EXECUTE_RETRY_DEFAULT, + retry_delay_ms: int = EXECUTE_RETRY_DELAY_MS, ) -> ExecutionResult: ... @overload async def aexecute( self, *, raise_on_failure: bool | None = None, - retries: int = 0, - retry_delay_ms: int = 2000, + retries: int = EXECUTE_RETRY_DEFAULT, + retry_delay_ms: int = EXECUTE_RETRY_DELAY_MS, **kwargs: Unpack[FormFillActionDict], ) -> ExecutionResult: ... @overload @@ -587,8 +601,8 @@ async def aexecute( self, *, raise_on_failure: bool | None = None, - retries: int = 0, - retry_delay_ms: int = 2000, + retries: int = EXECUTE_RETRY_DEFAULT, + retry_delay_ms: int = EXECUTE_RETRY_DELAY_MS, **kwargs: Unpack[GotoActionDict], ) -> ExecutionResult: ... @overload @@ -596,8 +610,8 @@ async def aexecute( self, *, raise_on_failure: bool | None = None, - retries: int = 0, - retry_delay_ms: int = 2000, + retries: int = EXECUTE_RETRY_DEFAULT, + retry_delay_ms: int = EXECUTE_RETRY_DELAY_MS, **kwargs: Unpack[GotoNewTabActionDict], ) -> ExecutionResult: ... @overload @@ -605,8 +619,8 @@ async def aexecute( self, *, raise_on_failure: bool | None = None, - retries: int = 0, - retry_delay_ms: int = 2000, + retries: int = EXECUTE_RETRY_DEFAULT, + retry_delay_ms: int = EXECUTE_RETRY_DELAY_MS, **kwargs: Unpack[CloseTabActionDict], ) -> ExecutionResult: ... @overload @@ -614,8 +628,8 @@ async def aexecute( self, *, raise_on_failure: bool | None = None, - retries: int = 0, - retry_delay_ms: int = 2000, + retries: int = EXECUTE_RETRY_DEFAULT, + retry_delay_ms: int = EXECUTE_RETRY_DELAY_MS, **kwargs: Unpack[SwitchTabActionDict], ) -> ExecutionResult: ... @overload @@ -623,8 +637,8 @@ async def aexecute( self, *, raise_on_failure: bool | None = None, - retries: int = 0, - retry_delay_ms: int = 2000, + retries: int = EXECUTE_RETRY_DEFAULT, + retry_delay_ms: int = EXECUTE_RETRY_DELAY_MS, **kwargs: Unpack[GoBackActionDict], ) -> ExecutionResult: ... @overload @@ -632,8 +646,8 @@ async def aexecute( self, *, raise_on_failure: bool | None = None, - retries: int = 0, - retry_delay_ms: int = 2000, + retries: int = EXECUTE_RETRY_DEFAULT, + retry_delay_ms: int = EXECUTE_RETRY_DELAY_MS, **kwargs: Unpack[GoForwardActionDict], ) -> ExecutionResult: ... @overload @@ -641,8 +655,8 @@ async def aexecute( self, *, raise_on_failure: bool | None = None, - retries: int = 0, - retry_delay_ms: int = 2000, + retries: int = EXECUTE_RETRY_DEFAULT, + retry_delay_ms: int = EXECUTE_RETRY_DELAY_MS, **kwargs: Unpack[ReloadActionDict], ) -> ExecutionResult: ... @overload @@ -650,8 +664,8 @@ async def aexecute( self, *, raise_on_failure: bool | None = None, - retries: int = 0, - retry_delay_ms: int = 2000, + retries: int = EXECUTE_RETRY_DEFAULT, + retry_delay_ms: int = EXECUTE_RETRY_DELAY_MS, **kwargs: Unpack[WaitActionDict], ) -> ExecutionResult: ... @overload @@ -659,8 +673,8 @@ async def aexecute( self, *, raise_on_failure: bool | None = None, - retries: int = 0, - retry_delay_ms: int = 2000, + retries: int = EXECUTE_RETRY_DEFAULT, + retry_delay_ms: int = EXECUTE_RETRY_DELAY_MS, **kwargs: Unpack[PressKeyActionDict], ) -> ExecutionResult: ... @overload @@ -668,8 +682,8 @@ async def aexecute( self, *, raise_on_failure: bool | None = None, - retries: int = 0, - retry_delay_ms: int = 2000, + retries: int = EXECUTE_RETRY_DEFAULT, + retry_delay_ms: int = EXECUTE_RETRY_DELAY_MS, **kwargs: Unpack[ScrollUpActionDict], ) -> ExecutionResult: ... @overload @@ -677,8 +691,8 @@ async def aexecute( self, *, raise_on_failure: bool | None = None, - retries: int = 0, - retry_delay_ms: int = 2000, + retries: int = EXECUTE_RETRY_DEFAULT, + retry_delay_ms: int = EXECUTE_RETRY_DELAY_MS, **kwargs: Unpack[ScrollDownActionDict], ) -> ExecutionResult: ... @overload @@ -686,8 +700,8 @@ async def aexecute( self, *, raise_on_failure: bool | None = None, - retries: int = 0, - retry_delay_ms: int = 2000, + retries: int = EXECUTE_RETRY_DEFAULT, + retry_delay_ms: int = EXECUTE_RETRY_DELAY_MS, **kwargs: Unpack[CaptchaSolveActionDict], ) -> ExecutionResult: ... @overload @@ -695,8 +709,8 @@ async def aexecute( self, *, raise_on_failure: bool | None = None, - retries: int = 0, - retry_delay_ms: int = 2000, + retries: int = EXECUTE_RETRY_DEFAULT, + retry_delay_ms: int = EXECUTE_RETRY_DELAY_MS, **kwargs: Unpack[HelpActionDict], ) -> ExecutionResult: ... @overload @@ -704,8 +718,8 @@ async def aexecute( self, *, raise_on_failure: bool | None = None, - retries: int = 0, - retry_delay_ms: int = 2000, + retries: int = EXECUTE_RETRY_DEFAULT, + retry_delay_ms: int = EXECUTE_RETRY_DELAY_MS, **kwargs: Unpack[CompletionActionDict], ) -> ExecutionResult: ... @overload @@ -713,8 +727,8 @@ async def aexecute( self, *, raise_on_failure: bool | None = None, - retries: int = 0, - retry_delay_ms: int = 2000, + retries: int = EXECUTE_RETRY_DEFAULT, + retry_delay_ms: int = EXECUTE_RETRY_DELAY_MS, **kwargs: Unpack[ScrapeActionDict], ) -> ExecutionResult: ... @overload @@ -722,8 +736,8 @@ async def aexecute( self, *, raise_on_failure: bool | None = None, - retries: int = 0, - retry_delay_ms: int = 2000, + retries: int = EXECUTE_RETRY_DEFAULT, + retry_delay_ms: int = EXECUTE_RETRY_DELAY_MS, **kwargs: Unpack[EmailReadActionDict], ) -> ExecutionResult: ... @overload @@ -731,8 +745,8 @@ async def aexecute( self, *, raise_on_failure: bool | None = None, - retries: int = 0, - retry_delay_ms: int = 2000, + retries: int = EXECUTE_RETRY_DEFAULT, + retry_delay_ms: int = EXECUTE_RETRY_DELAY_MS, **kwargs: Unpack[EmailVerificationReadActionDict], ) -> ExecutionResult: ... @overload @@ -740,8 +754,8 @@ async def aexecute( self, *, raise_on_failure: bool | None = None, - retries: int = 0, - retry_delay_ms: int = 2000, + retries: int = EXECUTE_RETRY_DEFAULT, + retry_delay_ms: int = EXECUTE_RETRY_DELAY_MS, **kwargs: Unpack[SmsReadActionDict], ) -> ExecutionResult: ... @overload @@ -749,8 +763,8 @@ async def aexecute( self, *, raise_on_failure: bool | None = None, - retries: int = 0, - retry_delay_ms: int = 2000, + retries: int = EXECUTE_RETRY_DEFAULT, + retry_delay_ms: int = EXECUTE_RETRY_DELAY_MS, **kwargs: Unpack[EvaluateJsActionDict], ) -> ExecutionResult: ... @overload @@ -758,8 +772,8 @@ async def aexecute( self, *, raise_on_failure: bool | None = None, - retries: int = 0, - retry_delay_ms: int = 2000, + retries: int = EXECUTE_RETRY_DEFAULT, + retry_delay_ms: int = EXECUTE_RETRY_DELAY_MS, **kwargs: Unpack[ClickActionDict], ) -> ExecutionResult: ... @overload @@ -767,8 +781,8 @@ async def aexecute( self, *, raise_on_failure: bool | None = None, - retries: int = 0, - retry_delay_ms: int = 2000, + retries: int = EXECUTE_RETRY_DEFAULT, + retry_delay_ms: int = EXECUTE_RETRY_DELAY_MS, **kwargs: Unpack[FillActionDict], ) -> ExecutionResult: ... @overload @@ -776,8 +790,8 @@ async def aexecute( self, *, raise_on_failure: bool | None = None, - retries: int = 0, - retry_delay_ms: int = 2000, + retries: int = EXECUTE_RETRY_DEFAULT, + retry_delay_ms: int = EXECUTE_RETRY_DELAY_MS, **kwargs: Unpack[MultiFactorFillActionDict], ) -> ExecutionResult: ... @overload @@ -785,8 +799,8 @@ async def aexecute( self, *, raise_on_failure: bool | None = None, - retries: int = 0, - retry_delay_ms: int = 2000, + retries: int = EXECUTE_RETRY_DEFAULT, + retry_delay_ms: int = EXECUTE_RETRY_DELAY_MS, **kwargs: Unpack[FallbackFillActionDict], ) -> ExecutionResult: ... @overload @@ -794,8 +808,8 @@ async def aexecute( self, *, raise_on_failure: bool | None = None, - retries: int = 0, - retry_delay_ms: int = 2000, + retries: int = EXECUTE_RETRY_DEFAULT, + retry_delay_ms: int = EXECUTE_RETRY_DELAY_MS, **kwargs: Unpack[CheckActionDict], ) -> ExecutionResult: ... @overload @@ -803,8 +817,8 @@ async def aexecute( self, *, raise_on_failure: bool | None = None, - retries: int = 0, - retry_delay_ms: int = 2000, + retries: int = EXECUTE_RETRY_DEFAULT, + retry_delay_ms: int = EXECUTE_RETRY_DELAY_MS, **kwargs: Unpack[SelectDropdownOptionActionDict], ) -> ExecutionResult: ... @overload @@ -812,8 +826,8 @@ async def aexecute( self, *, raise_on_failure: bool | None = None, - retries: int = 0, - retry_delay_ms: int = 2000, + retries: int = EXECUTE_RETRY_DEFAULT, + retry_delay_ms: int = EXECUTE_RETRY_DELAY_MS, **kwargs: Unpack[UploadFileActionDict], ) -> ExecutionResult: ... @overload @@ -821,8 +835,8 @@ async def aexecute( self, *, raise_on_failure: bool | None = None, - retries: int = 0, - retry_delay_ms: int = 2000, + retries: int = EXECUTE_RETRY_DEFAULT, + retry_delay_ms: int = EXECUTE_RETRY_DELAY_MS, **kwargs: Unpack[DownloadFileActionDict], ) -> ExecutionResult: ... @@ -833,8 +847,8 @@ async def aexecute( action: BaseAction | None = None, *, raise_on_failure: bool | None = None, - retries: int = 0, - retry_delay_ms: int = 2000, + retries: int = EXECUTE_RETRY_DEFAULT, + retry_delay_ms: int = EXECUTE_RETRY_DELAY_MS, **kwargs: Any, ) -> ExecutionResult: """ @@ -1087,15 +1101,20 @@ def execute_saved_actions(self, actions_file: str) -> None: @overload def execute( - self, action: BaseAction, *, raise_on_failure: bool | None = None, retries: int = 0, retry_delay_ms: int = 2000 + self, + action: BaseAction, + *, + raise_on_failure: bool | None = None, + retries: int = EXECUTE_RETRY_DEFAULT, + retry_delay_ms: int = EXECUTE_RETRY_DELAY_MS, ) -> ExecutionResult: ... @overload def execute( self, *, raise_on_failure: bool | None = None, - retries: int = 0, - retry_delay_ms: int = 2000, + retries: int = EXECUTE_RETRY_DEFAULT, + retry_delay_ms: int = EXECUTE_RETRY_DELAY_MS, **kwargs: Unpack[FormFillActionDict], ) -> ExecutionResult: ... @overload @@ -1103,8 +1122,8 @@ def execute( self, *, raise_on_failure: bool | None = None, - retries: int = 0, - retry_delay_ms: int = 2000, + retries: int = EXECUTE_RETRY_DEFAULT, + retry_delay_ms: int = EXECUTE_RETRY_DELAY_MS, **kwargs: Unpack[GotoActionDict], ) -> ExecutionResult: ... @overload @@ -1112,8 +1131,8 @@ def execute( self, *, raise_on_failure: bool | None = None, - retries: int = 0, - retry_delay_ms: int = 2000, + retries: int = EXECUTE_RETRY_DEFAULT, + retry_delay_ms: int = EXECUTE_RETRY_DELAY_MS, **kwargs: Unpack[GotoNewTabActionDict], ) -> ExecutionResult: ... @overload @@ -1121,8 +1140,8 @@ def execute( self, *, raise_on_failure: bool | None = None, - retries: int = 0, - retry_delay_ms: int = 2000, + retries: int = EXECUTE_RETRY_DEFAULT, + retry_delay_ms: int = EXECUTE_RETRY_DELAY_MS, **kwargs: Unpack[CloseTabActionDict], ) -> ExecutionResult: ... @overload @@ -1130,8 +1149,8 @@ def execute( self, *, raise_on_failure: bool | None = None, - retries: int = 0, - retry_delay_ms: int = 2000, + retries: int = EXECUTE_RETRY_DEFAULT, + retry_delay_ms: int = EXECUTE_RETRY_DELAY_MS, **kwargs: Unpack[SwitchTabActionDict], ) -> ExecutionResult: ... @overload @@ -1139,8 +1158,8 @@ def execute( self, *, raise_on_failure: bool | None = None, - retries: int = 0, - retry_delay_ms: int = 2000, + retries: int = EXECUTE_RETRY_DEFAULT, + retry_delay_ms: int = EXECUTE_RETRY_DELAY_MS, **kwargs: Unpack[GoBackActionDict], ) -> ExecutionResult: ... @overload @@ -1148,8 +1167,8 @@ def execute( self, *, raise_on_failure: bool | None = None, - retries: int = 0, - retry_delay_ms: int = 2000, + retries: int = EXECUTE_RETRY_DEFAULT, + retry_delay_ms: int = EXECUTE_RETRY_DELAY_MS, **kwargs: Unpack[GoForwardActionDict], ) -> ExecutionResult: ... @overload @@ -1157,8 +1176,8 @@ def execute( self, *, raise_on_failure: bool | None = None, - retries: int = 0, - retry_delay_ms: int = 2000, + retries: int = EXECUTE_RETRY_DEFAULT, + retry_delay_ms: int = EXECUTE_RETRY_DELAY_MS, **kwargs: Unpack[ReloadActionDict], ) -> ExecutionResult: ... @overload @@ -1166,8 +1185,8 @@ def execute( self, *, raise_on_failure: bool | None = None, - retries: int = 0, - retry_delay_ms: int = 2000, + retries: int = EXECUTE_RETRY_DEFAULT, + retry_delay_ms: int = EXECUTE_RETRY_DELAY_MS, **kwargs: Unpack[WaitActionDict], ) -> ExecutionResult: ... @overload @@ -1175,8 +1194,8 @@ def execute( self, *, raise_on_failure: bool | None = None, - retries: int = 0, - retry_delay_ms: int = 2000, + retries: int = EXECUTE_RETRY_DEFAULT, + retry_delay_ms: int = EXECUTE_RETRY_DELAY_MS, **kwargs: Unpack[PressKeyActionDict], ) -> ExecutionResult: ... @overload @@ -1184,8 +1203,8 @@ def execute( self, *, raise_on_failure: bool | None = None, - retries: int = 0, - retry_delay_ms: int = 2000, + retries: int = EXECUTE_RETRY_DEFAULT, + retry_delay_ms: int = EXECUTE_RETRY_DELAY_MS, **kwargs: Unpack[ScrollUpActionDict], ) -> ExecutionResult: ... @overload @@ -1193,8 +1212,8 @@ def execute( self, *, raise_on_failure: bool | None = None, - retries: int = 0, - retry_delay_ms: int = 2000, + retries: int = EXECUTE_RETRY_DEFAULT, + retry_delay_ms: int = EXECUTE_RETRY_DELAY_MS, **kwargs: Unpack[ScrollDownActionDict], ) -> ExecutionResult: ... @overload @@ -1202,8 +1221,8 @@ def execute( self, *, raise_on_failure: bool | None = None, - retries: int = 0, - retry_delay_ms: int = 2000, + retries: int = EXECUTE_RETRY_DEFAULT, + retry_delay_ms: int = EXECUTE_RETRY_DELAY_MS, **kwargs: Unpack[CaptchaSolveActionDict], ) -> ExecutionResult: ... @overload @@ -1211,8 +1230,8 @@ def execute( self, *, raise_on_failure: bool | None = None, - retries: int = 0, - retry_delay_ms: int = 2000, + retries: int = EXECUTE_RETRY_DEFAULT, + retry_delay_ms: int = EXECUTE_RETRY_DELAY_MS, **kwargs: Unpack[HelpActionDict], ) -> ExecutionResult: ... @overload @@ -1220,8 +1239,8 @@ def execute( self, *, raise_on_failure: bool | None = None, - retries: int = 0, - retry_delay_ms: int = 2000, + retries: int = EXECUTE_RETRY_DEFAULT, + retry_delay_ms: int = EXECUTE_RETRY_DELAY_MS, **kwargs: Unpack[CompletionActionDict], ) -> ExecutionResult: ... @overload @@ -1229,8 +1248,8 @@ def execute( self, *, raise_on_failure: bool | None = None, - retries: int = 0, - retry_delay_ms: int = 2000, + retries: int = EXECUTE_RETRY_DEFAULT, + retry_delay_ms: int = EXECUTE_RETRY_DELAY_MS, **kwargs: Unpack[ScrapeActionDict], ) -> ExecutionResult: ... @overload @@ -1238,8 +1257,8 @@ def execute( self, *, raise_on_failure: bool | None = None, - retries: int = 0, - retry_delay_ms: int = 2000, + retries: int = EXECUTE_RETRY_DEFAULT, + retry_delay_ms: int = EXECUTE_RETRY_DELAY_MS, **kwargs: Unpack[EmailReadActionDict], ) -> ExecutionResult: ... @overload @@ -1247,8 +1266,8 @@ def execute( self, *, raise_on_failure: bool | None = None, - retries: int = 0, - retry_delay_ms: int = 2000, + retries: int = EXECUTE_RETRY_DEFAULT, + retry_delay_ms: int = EXECUTE_RETRY_DELAY_MS, **kwargs: Unpack[EmailVerificationReadActionDict], ) -> ExecutionResult: ... @overload @@ -1256,8 +1275,8 @@ def execute( self, *, raise_on_failure: bool | None = None, - retries: int = 0, - retry_delay_ms: int = 2000, + retries: int = EXECUTE_RETRY_DEFAULT, + retry_delay_ms: int = EXECUTE_RETRY_DELAY_MS, **kwargs: Unpack[SmsReadActionDict], ) -> ExecutionResult: ... @overload @@ -1265,8 +1284,8 @@ def execute( self, *, raise_on_failure: bool | None = None, - retries: int = 0, - retry_delay_ms: int = 2000, + retries: int = EXECUTE_RETRY_DEFAULT, + retry_delay_ms: int = EXECUTE_RETRY_DELAY_MS, **kwargs: Unpack[EvaluateJsActionDict], ) -> ExecutionResult: ... @overload @@ -1274,8 +1293,8 @@ def execute( self, *, raise_on_failure: bool | None = None, - retries: int = 0, - retry_delay_ms: int = 2000, + retries: int = EXECUTE_RETRY_DEFAULT, + retry_delay_ms: int = EXECUTE_RETRY_DELAY_MS, **kwargs: Unpack[ClickActionDict], ) -> ExecutionResult: ... @overload @@ -1283,8 +1302,8 @@ def execute( self, *, raise_on_failure: bool | None = None, - retries: int = 0, - retry_delay_ms: int = 2000, + retries: int = EXECUTE_RETRY_DEFAULT, + retry_delay_ms: int = EXECUTE_RETRY_DELAY_MS, **kwargs: Unpack[FillActionDict], ) -> ExecutionResult: ... @overload @@ -1292,8 +1311,8 @@ def execute( self, *, raise_on_failure: bool | None = None, - retries: int = 0, - retry_delay_ms: int = 2000, + retries: int = EXECUTE_RETRY_DEFAULT, + retry_delay_ms: int = EXECUTE_RETRY_DELAY_MS, **kwargs: Unpack[MultiFactorFillActionDict], ) -> ExecutionResult: ... @overload @@ -1301,8 +1320,8 @@ def execute( self, *, raise_on_failure: bool | None = None, - retries: int = 0, - retry_delay_ms: int = 2000, + retries: int = EXECUTE_RETRY_DEFAULT, + retry_delay_ms: int = EXECUTE_RETRY_DELAY_MS, **kwargs: Unpack[FallbackFillActionDict], ) -> ExecutionResult: ... @overload @@ -1310,8 +1329,8 @@ def execute( self, *, raise_on_failure: bool | None = None, - retries: int = 0, - retry_delay_ms: int = 2000, + retries: int = EXECUTE_RETRY_DEFAULT, + retry_delay_ms: int = EXECUTE_RETRY_DELAY_MS, **kwargs: Unpack[CheckActionDict], ) -> ExecutionResult: ... @overload @@ -1319,8 +1338,8 @@ def execute( self, *, raise_on_failure: bool | None = None, - retries: int = 0, - retry_delay_ms: int = 2000, + retries: int = EXECUTE_RETRY_DEFAULT, + retry_delay_ms: int = EXECUTE_RETRY_DELAY_MS, **kwargs: Unpack[SelectDropdownOptionActionDict], ) -> ExecutionResult: ... @overload @@ -1328,8 +1347,8 @@ def execute( self, *, raise_on_failure: bool | None = None, - retries: int = 0, - retry_delay_ms: int = 2000, + retries: int = EXECUTE_RETRY_DEFAULT, + retry_delay_ms: int = EXECUTE_RETRY_DELAY_MS, **kwargs: Unpack[UploadFileActionDict], ) -> ExecutionResult: ... @overload @@ -1337,8 +1356,8 @@ def execute( self, *, raise_on_failure: bool | None = None, - retries: int = 0, - retry_delay_ms: int = 2000, + retries: int = EXECUTE_RETRY_DEFAULT, + retry_delay_ms: int = EXECUTE_RETRY_DELAY_MS, **kwargs: Unpack[DownloadFileActionDict], ) -> ExecutionResult: ... @@ -1347,8 +1366,8 @@ def execute( action: BaseAction | None = None, *, raise_on_failure: bool | None = None, - retries: int = 0, - retry_delay_ms: int = 2000, + retries: int = EXECUTE_RETRY_DEFAULT, + retry_delay_ms: int = EXECUTE_RETRY_DELAY_MS, **kwargs: Any, ) -> ExecutionResult: """ diff --git a/packages/notte-core/src/notte_core/common/config.py b/packages/notte-core/src/notte_core/common/config.py index a5f08c749..ee0eb4e50 100644 --- a/packages/notte-core/src/notte_core/common/config.py +++ b/packages/notte-core/src/notte_core/common/config.py @@ -12,6 +12,11 @@ DEFAULT_CONFIG_PATH = Path(__file__).parent.parent / "config.toml" +# Defaults for the `retries` / `retry_delay_ms` call options on `session.execute`, +# shared by the local session (notte-browser) and the remote one (notte-sdk). +EXECUTE_RETRY_DEFAULT = 0 +EXECUTE_RETRY_DELAY_MS = 2000 + if not DEFAULT_CONFIG_PATH.exists(): raise FileNotFoundError(f"Config file not found: {DEFAULT_CONFIG_PATH}") diff --git a/packages/notte-sdk/src/notte_sdk/endpoints/sessions.py b/packages/notte-sdk/src/notte_sdk/endpoints/sessions.py index 28e8a0c84..e7207775b 100644 --- a/packages/notte-sdk/src/notte_sdk/endpoints/sessions.py +++ b/packages/notte-sdk/src/notte_sdk/endpoints/sessions.py @@ -39,7 +39,7 @@ action_dict_to_base_action, ) from notte_core.browser.observation import ExecutionResult -from notte_core.common.config import CookieDict, PerceptionType, config +from notte_core.common.config import EXECUTE_RETRY_DEFAULT, EXECUTE_RETRY_DELAY_MS, CookieDict, PerceptionType, config from notte_core.common.logging import logger from notte_core.common.resource import SyncResource from notte_core.common.telemetry import track_usage @@ -1381,8 +1381,8 @@ def execute( self, *, raise_on_failure: bool | None = None, - retries: int = 0, - retry_delay_ms: int = 2000, + retries: int = EXECUTE_RETRY_DEFAULT, + retry_delay_ms: int = EXECUTE_RETRY_DELAY_MS, **kwargs: Unpack[FormFillActionDict], ) -> ExecutionResult: ... @overload @@ -1390,8 +1390,8 @@ def execute( self, *, raise_on_failure: bool | None = None, - retries: int = 0, - retry_delay_ms: int = 2000, + retries: int = EXECUTE_RETRY_DEFAULT, + retry_delay_ms: int = EXECUTE_RETRY_DELAY_MS, **kwargs: Unpack[GotoActionDict], ) -> ExecutionResult: ... @overload @@ -1399,8 +1399,8 @@ def execute( self, *, raise_on_failure: bool | None = None, - retries: int = 0, - retry_delay_ms: int = 2000, + retries: int = EXECUTE_RETRY_DEFAULT, + retry_delay_ms: int = EXECUTE_RETRY_DELAY_MS, **kwargs: Unpack[GotoNewTabActionDict], ) -> ExecutionResult: ... @overload @@ -1408,8 +1408,8 @@ def execute( self, *, raise_on_failure: bool | None = None, - retries: int = 0, - retry_delay_ms: int = 2000, + retries: int = EXECUTE_RETRY_DEFAULT, + retry_delay_ms: int = EXECUTE_RETRY_DELAY_MS, **kwargs: Unpack[CloseTabActionDict], ) -> ExecutionResult: ... @overload @@ -1417,8 +1417,8 @@ def execute( self, *, raise_on_failure: bool | None = None, - retries: int = 0, - retry_delay_ms: int = 2000, + retries: int = EXECUTE_RETRY_DEFAULT, + retry_delay_ms: int = EXECUTE_RETRY_DELAY_MS, **kwargs: Unpack[SwitchTabActionDict], ) -> ExecutionResult: ... @overload @@ -1426,8 +1426,8 @@ def execute( self, *, raise_on_failure: bool | None = None, - retries: int = 0, - retry_delay_ms: int = 2000, + retries: int = EXECUTE_RETRY_DEFAULT, + retry_delay_ms: int = EXECUTE_RETRY_DELAY_MS, **kwargs: Unpack[GoBackActionDict], ) -> ExecutionResult: ... @overload @@ -1435,8 +1435,8 @@ def execute( self, *, raise_on_failure: bool | None = None, - retries: int = 0, - retry_delay_ms: int = 2000, + retries: int = EXECUTE_RETRY_DEFAULT, + retry_delay_ms: int = EXECUTE_RETRY_DELAY_MS, **kwargs: Unpack[GoForwardActionDict], ) -> ExecutionResult: ... @overload @@ -1444,8 +1444,8 @@ def execute( self, *, raise_on_failure: bool | None = None, - retries: int = 0, - retry_delay_ms: int = 2000, + retries: int = EXECUTE_RETRY_DEFAULT, + retry_delay_ms: int = EXECUTE_RETRY_DELAY_MS, **kwargs: Unpack[ReloadActionDict], ) -> ExecutionResult: ... @overload @@ -1453,8 +1453,8 @@ def execute( self, *, raise_on_failure: bool | None = None, - retries: int = 0, - retry_delay_ms: int = 2000, + retries: int = EXECUTE_RETRY_DEFAULT, + retry_delay_ms: int = EXECUTE_RETRY_DELAY_MS, **kwargs: Unpack[WaitActionDict], ) -> ExecutionResult: ... @overload @@ -1462,8 +1462,8 @@ def execute( self, *, raise_on_failure: bool | None = None, - retries: int = 0, - retry_delay_ms: int = 2000, + retries: int = EXECUTE_RETRY_DEFAULT, + retry_delay_ms: int = EXECUTE_RETRY_DELAY_MS, **kwargs: Unpack[PressKeyActionDict], ) -> ExecutionResult: ... @overload @@ -1471,8 +1471,8 @@ def execute( self, *, raise_on_failure: bool | None = None, - retries: int = 0, - retry_delay_ms: int = 2000, + retries: int = EXECUTE_RETRY_DEFAULT, + retry_delay_ms: int = EXECUTE_RETRY_DELAY_MS, **kwargs: Unpack[ScrollUpActionDict], ) -> ExecutionResult: ... @overload @@ -1480,8 +1480,8 @@ def execute( self, *, raise_on_failure: bool | None = None, - retries: int = 0, - retry_delay_ms: int = 2000, + retries: int = EXECUTE_RETRY_DEFAULT, + retry_delay_ms: int = EXECUTE_RETRY_DELAY_MS, **kwargs: Unpack[ScrollDownActionDict], ) -> ExecutionResult: ... @overload @@ -1489,8 +1489,8 @@ def execute( self, *, raise_on_failure: bool | None = None, - retries: int = 0, - retry_delay_ms: int = 2000, + retries: int = EXECUTE_RETRY_DEFAULT, + retry_delay_ms: int = EXECUTE_RETRY_DELAY_MS, **kwargs: Unpack[CaptchaSolveActionDict], ) -> ExecutionResult: ... @overload @@ -1498,8 +1498,8 @@ def execute( self, *, raise_on_failure: bool | None = None, - retries: int = 0, - retry_delay_ms: int = 2000, + retries: int = EXECUTE_RETRY_DEFAULT, + retry_delay_ms: int = EXECUTE_RETRY_DELAY_MS, **kwargs: Unpack[HelpActionDict], ) -> ExecutionResult: ... @overload @@ -1507,8 +1507,8 @@ def execute( self, *, raise_on_failure: bool | None = None, - retries: int = 0, - retry_delay_ms: int = 2000, + retries: int = EXECUTE_RETRY_DEFAULT, + retry_delay_ms: int = EXECUTE_RETRY_DELAY_MS, **kwargs: Unpack[CompletionActionDict], ) -> ExecutionResult: ... @overload @@ -1516,8 +1516,8 @@ def execute( self, *, raise_on_failure: bool | None = None, - retries: int = 0, - retry_delay_ms: int = 2000, + retries: int = EXECUTE_RETRY_DEFAULT, + retry_delay_ms: int = EXECUTE_RETRY_DELAY_MS, **kwargs: Unpack[ScrapeActionDict], ) -> ExecutionResult: ... @overload @@ -1525,8 +1525,8 @@ def execute( self, *, raise_on_failure: bool | None = None, - retries: int = 0, - retry_delay_ms: int = 2000, + retries: int = EXECUTE_RETRY_DEFAULT, + retry_delay_ms: int = EXECUTE_RETRY_DELAY_MS, **kwargs: Unpack[EmailReadActionDict], ) -> ExecutionResult: ... @overload @@ -1534,8 +1534,8 @@ def execute( self, *, raise_on_failure: bool | None = None, - retries: int = 0, - retry_delay_ms: int = 2000, + retries: int = EXECUTE_RETRY_DEFAULT, + retry_delay_ms: int = EXECUTE_RETRY_DELAY_MS, **kwargs: Unpack[EmailVerificationReadActionDict], ) -> ExecutionResult: ... @overload @@ -1543,8 +1543,8 @@ def execute( self, *, raise_on_failure: bool | None = None, - retries: int = 0, - retry_delay_ms: int = 2000, + retries: int = EXECUTE_RETRY_DEFAULT, + retry_delay_ms: int = EXECUTE_RETRY_DELAY_MS, **kwargs: Unpack[SmsReadActionDict], ) -> ExecutionResult: ... @overload @@ -1552,8 +1552,8 @@ def execute( self, *, raise_on_failure: bool | None = None, - retries: int = 0, - retry_delay_ms: int = 2000, + retries: int = EXECUTE_RETRY_DEFAULT, + retry_delay_ms: int = EXECUTE_RETRY_DELAY_MS, **kwargs: Unpack[EvaluateJsActionDict], ) -> ExecutionResult: ... @overload @@ -1561,8 +1561,8 @@ def execute( self, *, raise_on_failure: bool | None = None, - retries: int = 0, - retry_delay_ms: int = 2000, + retries: int = EXECUTE_RETRY_DEFAULT, + retry_delay_ms: int = EXECUTE_RETRY_DELAY_MS, **kwargs: Unpack[ClickActionDict], ) -> ExecutionResult: ... @overload @@ -1570,8 +1570,8 @@ def execute( self, *, raise_on_failure: bool | None = None, - retries: int = 0, - retry_delay_ms: int = 2000, + retries: int = EXECUTE_RETRY_DEFAULT, + retry_delay_ms: int = EXECUTE_RETRY_DELAY_MS, **kwargs: Unpack[FillActionDict], ) -> ExecutionResult: ... @overload @@ -1579,8 +1579,8 @@ def execute( self, *, raise_on_failure: bool | None = None, - retries: int = 0, - retry_delay_ms: int = 2000, + retries: int = EXECUTE_RETRY_DEFAULT, + retry_delay_ms: int = EXECUTE_RETRY_DELAY_MS, **kwargs: Unpack[MultiFactorFillActionDict], ) -> ExecutionResult: ... @overload @@ -1588,8 +1588,8 @@ def execute( self, *, raise_on_failure: bool | None = None, - retries: int = 0, - retry_delay_ms: int = 2000, + retries: int = EXECUTE_RETRY_DEFAULT, + retry_delay_ms: int = EXECUTE_RETRY_DELAY_MS, **kwargs: Unpack[FallbackFillActionDict], ) -> ExecutionResult: ... @overload @@ -1597,8 +1597,8 @@ def execute( self, *, raise_on_failure: bool | None = None, - retries: int = 0, - retry_delay_ms: int = 2000, + retries: int = EXECUTE_RETRY_DEFAULT, + retry_delay_ms: int = EXECUTE_RETRY_DELAY_MS, **kwargs: Unpack[CheckActionDict], ) -> ExecutionResult: ... @overload @@ -1606,8 +1606,8 @@ def execute( self, *, raise_on_failure: bool | None = None, - retries: int = 0, - retry_delay_ms: int = 2000, + retries: int = EXECUTE_RETRY_DEFAULT, + retry_delay_ms: int = EXECUTE_RETRY_DELAY_MS, **kwargs: Unpack[SelectDropdownOptionActionDict], ) -> ExecutionResult: ... @overload @@ -1615,8 +1615,8 @@ def execute( self, *, raise_on_failure: bool | None = None, - retries: int = 0, - retry_delay_ms: int = 2000, + retries: int = EXECUTE_RETRY_DEFAULT, + retry_delay_ms: int = EXECUTE_RETRY_DELAY_MS, **kwargs: Unpack[UploadFileActionDict], ) -> ExecutionResult: ... @overload @@ -1624,13 +1624,18 @@ def execute( self, *, raise_on_failure: bool | None = None, - retries: int = 0, - retry_delay_ms: int = 2000, + retries: int = EXECUTE_RETRY_DEFAULT, + retry_delay_ms: int = EXECUTE_RETRY_DELAY_MS, **kwargs: Unpack[DownloadFileActionDict], ) -> ExecutionResult: ... @overload def execute( - self, action: BaseAction, *, raise_on_failure: bool | None = None, retries: int = 0, retry_delay_ms: int = 2000 + self, + action: BaseAction, + *, + raise_on_failure: bool | None = None, + retries: int = EXECUTE_RETRY_DEFAULT, + retry_delay_ms: int = EXECUTE_RETRY_DELAY_MS, ) -> ExecutionResult: ... def execute( @@ -1638,8 +1643,8 @@ def execute( action: BaseAction | None = None, *, raise_on_failure: bool | None = None, - retries: int = 0, - retry_delay_ms: int = 2000, + retries: int = EXECUTE_RETRY_DEFAULT, + retry_delay_ms: int = EXECUTE_RETRY_DELAY_MS, **kwargs: Any, ) -> ExecutionResult: """