better totp failure reason and error code (#3472)

This commit is contained in:
LawyZheng
2025-09-19 10:08:25 +08:00
committed by GitHub
parent a39ff77bd9
commit 82b2187583
5 changed files with 89 additions and 4 deletions

View File

@@ -5,7 +5,7 @@ from datetime import datetime, timedelta
import structlog import structlog
from skyvern.config import settings from skyvern.config import settings
from skyvern.exceptions import NoTOTPVerificationCodeFound from skyvern.exceptions import FailedToGetTOTPVerificationCode, NoTOTPVerificationCodeFound
from skyvern.forge import app from skyvern.forge import app
from skyvern.forge.sdk.core.aiohttp_helper import aiohttp_post from skyvern.forge.sdk.core.aiohttp_helper import aiohttp_post
from skyvern.forge.sdk.core.security import generate_skyvern_signature from skyvern.forge.sdk.core.security import generate_skyvern_signature
@@ -96,7 +96,17 @@ async def _get_verification_code_from_url(
"x-skyvern-signature": signature, "x-skyvern-signature": signature,
"Content-Type": "application/json", "Content-Type": "application/json",
} }
json_resp = await aiohttp_post(url=url, data=request_data, headers=headers, raise_exception=False) try:
json_resp = await aiohttp_post(url=url, data=request_data, headers=headers, raise_exception=False)
except Exception as e:
LOG.error("Failed to get verification code from url", exc_info=True)
raise FailedToGetTOTPVerificationCode(
task_id=task_id,
workflow_run_id=workflow_run_id,
workflow_id=workflow_permanent_id,
totp_verification_url=url,
reason=str(e),
)
return json_resp.get("verification_code", None) return json_resp.get("verification_code", None)

View File

@@ -17,6 +17,9 @@ class SkyvernDefinedError(BaseModel):
def __repr__(self) -> str: def __repr__(self) -> str:
return f"{self.reasoning}(error_code={self.error_code})" return f"{self.reasoning}(error_code={self.error_code})"
def to_user_defined_error(self) -> UserDefinedError:
return UserDefinedError(error_code=self.error_code, reasoning=self.reasoning, confidence_float=1.0)
class ReachMaxStepsError(SkyvernDefinedError): class ReachMaxStepsError(SkyvernDefinedError):
error_code: str = "REACH_MAX_STEPS" error_code: str = "REACH_MAX_STEPS"
@@ -26,3 +29,19 @@ class ReachMaxStepsError(SkyvernDefinedError):
class ReachMaxRetriesError(SkyvernDefinedError): class ReachMaxRetriesError(SkyvernDefinedError):
error_code: str = "REACH_MAX_RETRIES" error_code: str = "REACH_MAX_RETRIES"
reasoning: str = "The agent has reached the maximum number of retries. It might be an issue with the agent. Please reach out to the Skyvern team for support." reasoning: str = "The agent has reached the maximum number of retries. It might be an issue with the agent. Please reach out to the Skyvern team for support."
class GetTOTPVerificationCodeError(SkyvernDefinedError):
error_code: str = "OTP_ERROR"
reasoning: str = (
"Failed to get TOTP verification code. Please confirm the TOTP functionality is working correctly on your side."
)
def __init__(self, *, reason: str | None = None) -> None:
reasoning = f"Failed to get TOTP verification code. Reason: {reason}" if reason else self.reasoning
super().__init__(reasoning=reasoning)
class TimeoutGetTOTPVerificationCodeError(SkyvernDefinedError):
error_code: str = "OTP_TIMEOUT"
reasoning: str = "Timeout getting TOTP verification code."

View File

@@ -695,6 +695,33 @@ class NoTOTPVerificationCodeFound(SkyvernHTTPException):
super().__init__(msg) super().__init__(msg)
class FailedToGetTOTPVerificationCode(SkyvernException):
reason: str | None = None
def __init__(
self,
task_id: str | None = None,
workflow_run_id: str | None = None,
workflow_id: str | None = None,
totp_verification_url: str | None = None,
totp_identifier: str | None = None,
reason: str | None = None,
) -> None:
self.reason = reason
msg = "Failed to get TOTP verification code."
if task_id:
msg += f" task_id={task_id}"
if workflow_run_id:
msg += f" workflow_run_id={workflow_run_id}"
if workflow_id:
msg += f" workflow_id={workflow_id}"
if totp_verification_url:
msg += f" totp_verification_url={totp_verification_url}"
if totp_identifier:
msg += f" totp_identifier={totp_identifier}"
super().__init__(f"Failed to get TOTP verification code. reason: {reason}")
class SkyvernContextWindowExceededError(SkyvernException): class SkyvernContextWindowExceededError(SkyvernException):
def __init__(self) -> None: def __init__(self) -> None:
message = "Context window exceeded. Please contact support@skyvern.com for help." message = "Context window exceeded. Please contact support@skyvern.com for help."

View File

@@ -27,12 +27,19 @@ from skyvern.constants import (
ScrapeType, ScrapeType,
) )
from skyvern.core.totp import poll_verification_code from skyvern.core.totp import poll_verification_code
from skyvern.errors.errors import ReachMaxRetriesError, ReachMaxStepsError, UserDefinedError from skyvern.errors.errors import (
GetTOTPVerificationCodeError,
ReachMaxRetriesError,
ReachMaxStepsError,
TimeoutGetTOTPVerificationCodeError,
UserDefinedError,
)
from skyvern.exceptions import ( from skyvern.exceptions import (
BrowserSessionNotFound, BrowserSessionNotFound,
BrowserStateMissingPage, BrowserStateMissingPage,
DownloadFileMaxWaitingTime, DownloadFileMaxWaitingTime,
EmptyScrapePage, EmptyScrapePage,
FailedToGetTOTPVerificationCode,
FailedToNavigateToUrl, FailedToNavigateToUrl,
FailedToParseActionInstruction, FailedToParseActionInstruction,
FailedToSendWebhook, FailedToSendWebhook,
@@ -1012,6 +1019,21 @@ class ForgeAgent:
action_order=0, action_order=0,
reasoning="No TOTP verification code found. Going to terminate.", reasoning="No TOTP verification code found. Going to terminate.",
intention="No TOTP verification code found. Going to terminate.", intention="No TOTP verification code found. Going to terminate.",
errors=[TimeoutGetTOTPVerificationCodeError().to_user_defined_error()],
)
]
except FailedToGetTOTPVerificationCode as e:
actions = [
TerminateAction(
reasoning=f"Failed to get TOTP verification code. Going to terminate. Reason: {e.reason}",
intention=f"Failed to get TOTP verification code. Going to terminate. Reason: {e.reason}",
organization_id=task.organization_id,
workflow_run_id=task.workflow_run_id,
task_id=task.task_id,
step_id=step.step_id,
step_order=step.order,
action_order=0,
errors=[GetTOTPVerificationCodeError(reason=e.reason).to_user_defined_error()],
) )
] ]

View File

@@ -8,7 +8,7 @@ from pydantic import ValidationError
from skyvern.constants import SCROLL_AMOUNT_MULTIPLIER from skyvern.constants import SCROLL_AMOUNT_MULTIPLIER
from skyvern.core.totp import poll_verification_code from skyvern.core.totp import poll_verification_code
from skyvern.exceptions import NoTOTPVerificationCodeFound, UnsupportedActionType from skyvern.exceptions import FailedToGetTOTPVerificationCode, NoTOTPVerificationCodeFound, UnsupportedActionType
from skyvern.forge import app from skyvern.forge import app
from skyvern.forge.prompts import prompt_engine from skyvern.forge.prompts import prompt_engine
from skyvern.forge.sdk.core import skyvern_context from skyvern.forge.sdk.core import skyvern_context
@@ -829,6 +829,13 @@ async def generate_cua_fallback_actions(
reasoning=reasoning, reasoning=reasoning,
intention=reasoning, intention=reasoning,
) )
except FailedToGetTOTPVerificationCode as e:
reasoning_suffix = f"Failed to get verification code. Reason: {e.reason}"
reasoning = f"{reasoning}. {reasoning_suffix}" if reasoning else reasoning_suffix
action = TerminateAction(
reasoning=reasoning,
intention=reasoning,
)
else: else:
action = TerminateAction( action = TerminateAction(
reasoning=reasoning, reasoning=reasoning,