Skip to content

Commit 4fe26fd

Browse files
committed
docs: document wait until and interrupt timeouts
1 parent f03c481 commit 4fe26fd

16 files changed

Lines changed: 391 additions & 0 deletions

File tree

docs/human_in_the_loop.md

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -468,6 +468,57 @@ For a practical implementation, refer to the [email-triage-agent sample](https:/
468468

469469
---
470470

471+
## Time triggers
472+
473+
### WaitUntil
474+
475+
Suspends the agent until an Orchestrator timer fires. The SDK stores the resume timestamp as part of the resume condition, and Orchestrator resumes the suspended job when that time is reached.
476+
477+
| Attribute | Type | Description |
478+
| --- | --- | --- |
479+
| `resume_time` | `datetime` | Absolute time when Orchestrator should resume the suspended job. Use a timezone-aware `datetime`; it is normalized to UTC before the trigger is created. |
480+
481+
```python
482+
from langgraph.types import interrupt
483+
from uipath.platform.common import WaitUntil
484+
from datetime import datetime, timedelta, timezone
485+
486+
resume_payload = interrupt(
487+
WaitUntil(
488+
resume_time=datetime.now(timezone.utc) + timedelta(minutes=5),
489+
)
490+
)
491+
```
492+
493+
This is intended for deployed jobs where Orchestrator owns the scheduler. In a local run there is no scheduler to fire the time trigger, so resume the interrupt explicitly when testing locally.
494+
495+
---
496+
497+
## Interrupt timeouts
498+
499+
Typed interrupt models can also be given a timeout. The SDK creates both the requested resume trigger and a timer resume trigger; whichever fires first resumes the agent. Timeout resume values use reserved UiPath metadata so they cannot collide with user payload fields.
500+
501+
```python
502+
from langgraph.types import interrupt
503+
from uipath.platform.common import InvokeProcess
504+
from uipath.platform.resume_triggers import assert_no_timeout, is_timeout
505+
506+
result = interrupt(
507+
InvokeProcess(
508+
name="long-running-agent",
509+
input_arguments={"message": "start"},
510+
timeout=10,
511+
)
512+
)
513+
514+
if is_timeout(result):
515+
result = retry_or_fallback()
516+
517+
result = assert_no_timeout(result)
518+
```
519+
520+
---
521+
471522
## Resuming with a plain value (API trigger)
472523

473524
All the models above are typed interrupts that tie the agent's wait state to a specific UiPath operation. When you call `interrupt(...)` with a value that is **not** one of those models, most commonly a plain string but any JSON-serializable value works, the SDK creates an **API resume trigger**. The agent suspends and waits to be resumed by an explicit external API call rather than by polling a UiPath operation.

samples/README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,12 @@ This sample shows how to automate Outlook inbox organization with AI-powered rul
1515
## [HITL inbox server](hitl-inbox-server)
1616
This sample demonstrates a FastAPI server for managing human-in-the-loop workflows with job submissions and inbox message approvals.
1717

18+
## [Wait until agent](wait-until-agent)
19+
This sample demonstrates a LangGraph agent that suspends with `WaitUntil` and resumes when Orchestrator fires a timer resume trigger.
20+
21+
## [Invoke process timeout agent](invoke-process-timeout-agent)
22+
This sample demonstrates `InvokeProcess(..., timeout=...)` and handling timeout resume values with the timeout helpers.
23+
1824
## [Multi agent supervisor, researcher, coder](multi-agent-supervisor-researcher-coder)
1925
This sample showcases a multi-agent system, involving a supervisor, a researcher, and a coder working in coordination to tackle complex tasks.
2026

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# Invoke Process Timeout Agent
2+
3+
This sample demonstrates timeout handling for typed interrupts.
4+
5+
The parent graph invokes the child graph with `InvokeProcess(..., timeout=10)`. The child graph sleeps longer than the timeout, so Orchestrator resumes the parent through the timer trigger first. The parent uses `assert_no_timeout`, which raises `UiPathTimeoutError` on timeout.
6+
7+
Publish both graphs from this project before running the parent graph.
8+
9+
The child process is declared in `bindings.json`. The sample code passes `process_folder_path="Shared"` when invoking the child process so the process lookup is explicit and can be changed with the binding.
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
{
2+
"version": "2.0",
3+
"resources": [
4+
{
5+
"resource": "process",
6+
"key": "timeout-child-agent.Shared",
7+
"value": {
8+
"name": {
9+
"defaultValue": "timeout-child-agent",
10+
"isExpression": false,
11+
"displayName": "Process Name"
12+
},
13+
"folderPath": {
14+
"defaultValue": "Shared",
15+
"isExpression": false,
16+
"displayName": "Process Folder Path"
17+
}
18+
},
19+
"metadata": {
20+
"ActivityName": "InvokeProcess",
21+
"BindingsVersion": "2.2",
22+
"DisplayLabel": "timeout-child-agent"
23+
}
24+
}
25+
]
26+
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import asyncio
2+
from typing import TypedDict
3+
4+
from langgraph.graph import END, START, StateGraph
5+
from langgraph.types import interrupt
6+
from uipath.platform.common import InvokeProcess
7+
from uipath.platform.resume_triggers import assert_no_timeout
8+
9+
CHILD_PROCESS_NAME = "timeout-child-agent"
10+
PROCESS_FOLDER_PATH = "Shared"
11+
12+
13+
class ParentState(TypedDict, total=False):
14+
message: str
15+
status: str
16+
child_result: str
17+
timeout: dict[str, object]
18+
19+
20+
class ChildState(TypedDict, total=False):
21+
message: str
22+
result: str
23+
24+
25+
def parent_node(state: ParentState) -> ParentState:
26+
child_result = interrupt(
27+
InvokeProcess(
28+
name=CHILD_PROCESS_NAME,
29+
process_folder_path=PROCESS_FOLDER_PATH,
30+
input_arguments={"message": state.get("message", "start child work")},
31+
timeout=10,
32+
)
33+
)
34+
35+
# Raises UiPathTimeoutError on timeout.
36+
assert_no_timeout(child_result)
37+
38+
return {
39+
"status": "completed",
40+
"child_result": str(child_result),
41+
}
42+
43+
44+
async def child_node(state: ChildState) -> ChildState:
45+
await asyncio.sleep(300)
46+
return {
47+
"result": f"child completed: {state.get('message', '')}",
48+
}
49+
50+
51+
parent_builder = StateGraph(ParentState)
52+
parent_builder.add_node("parent", parent_node)
53+
parent_builder.add_edge(START, "parent")
54+
parent_builder.add_edge("parent", END)
55+
parent_graph = parent_builder.compile()
56+
57+
child_builder = StateGraph(ChildState)
58+
child_builder.add_node("child", child_node)
59+
child_builder.add_edge(START, "child")
60+
child_builder.add_edge("child", END)
61+
child_graph = child_builder.compile()
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"dependencies": ["."],
3+
"graphs": {
4+
"timeout-parent-agent": "./graph.py:parent_graph",
5+
"timeout-child-agent": "./graph.py:child_graph"
6+
},
7+
"env": ".env"
8+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
[project]
2+
name = "invoke-process-timeout-agent"
3+
version = "0.0.1"
4+
description = "LangGraph sample showing InvokeProcess timeout handling"
5+
authors = [{ name = "John Doe", email = "john.doe@myemail.com" }]
6+
requires-python = ">=3.11"
7+
dependencies = [
8+
"uipath",
9+
"uipath-langchain",
10+
]
11+
12+
[dependency-groups]
13+
dev = [
14+
"uipath-dev",
15+
]
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"$schema": "https://cloud.uipath.com/draft/2024-12/uipath",
3+
"runtimeOptions": {
4+
"isConversational": false
5+
},
6+
"packOptions": {
7+
"fileExtensionsIncluded": [],
8+
"filesIncluded": [],
9+
"filesExcluded": [],
10+
"directoriesExcluded": [],
11+
"includeUvLock": true
12+
},
13+
"functions": {}
14+
}

samples/wait-until-agent/README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# Wait Until Agent
2+
3+
This sample demonstrates a LangGraph agent that suspends with `WaitUntil` and resumes when Orchestrator fires the timer resume trigger.
4+
5+
The resume time is an absolute timezone-aware `datetime`. The SDK normalizes it to UTC before creating the timer trigger.
6+
7+
The sample includes an empty `bindings.json` file so the project has the same deployable shape as samples that declare resource bindings.
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"version": "2.0",
3+
"resources": []
4+
}

0 commit comments

Comments
 (0)