Flatten timeline so forloops and taskv2 blocks play nice (#3946)

This commit is contained in:
pedrohsdb
2025-11-07 17:32:10 -08:00
committed by GitHub
parent d1d0c9414b
commit ea7361c9f2
3 changed files with 161 additions and 36 deletions

View File

@@ -62,7 +62,9 @@ from skyvern.webeye.utils.page import SkyvernFrame
LOG = structlog.get_logger()
DEFAULT_WORKFLOW_TITLE = "New Workflow"
RANDOM_STRING_POOL = string.ascii_letters + string.digits
DEFAULT_MAX_ITERATIONS = 13
# Maximum number of planning iterations for TaskV2
# This limits how many times the LLM can plan and execute actions
DEFAULT_MAX_ITERATIONS = 50
MINI_GOAL_TEMPLATE = """Achieve the following mini goal and once it's achieved, complete:
```{mini_goal}```
@@ -574,6 +576,10 @@ async def run_task_v2_helper(
url = str(task_v2.url)
max_steps = int_max_steps_override or settings.MAX_STEPS_PER_TASK_V2
# When TaskV2 is inside a loop, each loop iteration should get fresh attempts
# This is managed at the ForLoop level by calling run_task_v2 for each iteration
# The DEFAULT_MAX_ITERATIONS limit applies to this single TaskV2 execution
for i in range(DEFAULT_MAX_ITERATIONS):
# validate the task execution
await app.AGENT_FUNCTION.validate_task_execution(
@@ -986,17 +992,16 @@ async def run_task_v2_helper(
)
return workflow, workflow_run, task_v2
else:
# Loop completed without early exit - task exceeded max iterations
LOG.info(
"Task v2 failed - run out of iterations",
"Task v2 failed - exceeded maximum iterations",
max_iterations=DEFAULT_MAX_ITERATIONS,
max_steps=max_steps,
workflow_run_id=workflow_run_id,
)
failure_reason = await _summarize_max_steps_failure_reason(task_v2, organization_id, browser_state)
task_v2 = await mark_task_v2_as_failed(
task_v2_id=task_v2_id,
workflow_run_id=workflow_run_id,
failure_reason=f"Max iterations reached. Possible failure reasons: {failure_reason}",
failure_reason=f"Task exceeded maximum of {DEFAULT_MAX_ITERATIONS} planning iterations. Consider simplifying the task or breaking it into smaller steps.",
organization_id=organization_id,
)
@@ -1821,19 +1826,22 @@ async def send_task_v2_webhook(task_v2: TaskV2) -> None:
payload_dict = json.loads(payload_json)
payload_dict.update(json.loads(task_run_response_json))
signed_data = generate_skyvern_webhook_signature(payload=payload_dict, api_key=api_key.token)
payload = signed_data.signed_payload
headers = signed_data.headers
LOG.info(
"Sending task v2 response to webhook callback url",
task_v2_id=task_v2.observer_cruise_id,
webhook_callback_url=task_v2.webhook_callback_url,
payload=signed_data.signed_payload,
headers=signed_data.headers,
)
resp = await httpx.AsyncClient().post(
task_v2.webhook_callback_url,
data=signed_data.signed_payload,
headers=signed_data.headers,
timeout=httpx.Timeout(30.0),
payload_length=len(payload),
header_keys=sorted(headers.keys()),
)
timeout = httpx.Timeout(30.0)
async with httpx.AsyncClient(timeout=timeout) as client:
resp = await client.post(
task_v2.webhook_callback_url,
data=payload,
headers=headers,
)
if resp.status_code >= 200 and resp.status_code < 300:
LOG.info(
"Task v2 webhook sent successfully",