Skip to content

Commit d435497

Browse files
committed
Add activity_options to all graph_api samples
Demonstrates per-node activity configuration using activity_options(): Direct node metadata (graphs using add_node directly): - hello_world: Simple 30s timeout for process node - reflection: 2min timeouts for LLM nodes, 30s for finalize - deep_research: Varied timeouts (plan 2min, search 1min, synthesize 3min) - agentic_rag: 1-2min timeouts for grading/generation nodes - plan_and_execute: 2min for planning, 30s for evaluation - activity_from_node: 30s for finalize activity node - human_in_the_loop: 30s timeouts for approval nodes Documentation for pre-built graphs (create_agent/create_supervisor): - react_agent: Docstring shows per_node_activity_options usage - supervisor: Docstring shows multi-agent configuration
1 parent 9df7258 commit d435497

10 files changed

Lines changed: 214 additions & 31 deletions

File tree

langgraph_plugin/graph_api/activity_from_node/graph.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ def build_activity_from_node_graph() -> Any:
8686
The orchestrator node uses run_in_workflow=True to execute directly
8787
in the workflow context, allowing it to call Temporal activities.
8888
"""
89-
from temporalio.contrib.langgraph import temporal_node_metadata
89+
from temporalio.contrib.langgraph import activity_options, temporal_node_metadata
9090

9191
graph = StateGraph(ProcessingState)
9292

@@ -97,8 +97,14 @@ def build_activity_from_node_graph() -> Any:
9797
metadata=temporal_node_metadata(run_in_workflow=True),
9898
)
9999

100-
# Finalize runs as a regular activity
101-
graph.add_node("finalize", finalize_node)
100+
# Finalize runs as a regular activity with timeout config
101+
graph.add_node(
102+
"finalize",
103+
finalize_node,
104+
metadata=activity_options(
105+
start_to_close_timeout=timedelta(seconds=30),
106+
),
107+
)
102108

103109
graph.add_edge(START, "orchestrator")
104110
graph.add_edge("orchestrator", "finalize")

langgraph_plugin/graph_api/agentic_rag/graph.py

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
"""
2828

2929
import os
30+
from datetime import timedelta
3031
from typing import Annotated, Any, Literal, Sequence, cast
3132

3233
from langchain_core.documents import Document
@@ -39,6 +40,7 @@
3940
from langgraph.graph import END, START, StateGraph
4041
from langgraph.graph.message import add_messages
4142
from pydantic import BaseModel, Field
43+
from temporalio.contrib.langgraph import activity_options
4244
from typing_extensions import TypedDict
4345

4446
from langchain.agents import create_agent
@@ -308,12 +310,31 @@ def rewrite(state: AgentState) -> dict[str, Any]:
308310
# Build the outer graph with grading logic
309311
workflow = StateGraph(AgentState)
310312

311-
# Add nodes
313+
# Add nodes with activity options
312314
# retrieve_agent is a compiled graph from create_agent - inner nodes run as separate activities
315+
# Use per_node_activity_options in LangGraphPlugin to configure its inner nodes
313316
workflow.add_node("retrieve_agent", retrieve_agent)
314-
workflow.add_node("grade_documents", grade_documents) # LLM grading as activity
315-
workflow.add_node("generate", generate)
316-
workflow.add_node("rewrite", rewrite)
317+
workflow.add_node(
318+
"grade_documents",
319+
grade_documents,
320+
metadata=activity_options(
321+
start_to_close_timeout=timedelta(minutes=1),
322+
),
323+
)
324+
workflow.add_node(
325+
"generate",
326+
generate,
327+
metadata=activity_options(
328+
start_to_close_timeout=timedelta(minutes=2),
329+
),
330+
)
331+
workflow.add_node(
332+
"rewrite",
333+
rewrite,
334+
metadata=activity_options(
335+
start_to_close_timeout=timedelta(minutes=1),
336+
),
337+
)
317338

318339
# Add edges
319340
workflow.add_edge(START, "retrieve_agent")

langgraph_plugin/graph_api/deep_research/graph.py

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
"""
2424

2525
import os
26+
from datetime import timedelta
2627
from typing import Annotated, Any, Literal, cast
2728

2829
from langchain_community.tools import DuckDuckGoSearchRun
@@ -34,6 +35,7 @@
3435
from langgraph.graph import END, START, StateGraph
3536
from langgraph.graph.message import add_messages
3637
from pydantic import BaseModel, Field
38+
from temporalio.contrib.langgraph import activity_options
3739
from typing_extensions import TypedDict
3840

3941

@@ -287,11 +289,35 @@ def synthesize_report(state: ResearchState) -> dict[str, Any]:
287289
# Build the research graph
288290
workflow = StateGraph(ResearchState)
289291

290-
# Add nodes
291-
workflow.add_node("plan", plan_research)
292-
workflow.add_node("search", execute_search)
293-
workflow.add_node("evaluate", evaluate_results)
294-
workflow.add_node("synthesize", synthesize_report)
292+
# Add nodes with activity options
293+
workflow.add_node(
294+
"plan",
295+
plan_research,
296+
metadata=activity_options(
297+
start_to_close_timeout=timedelta(minutes=2),
298+
),
299+
)
300+
workflow.add_node(
301+
"search",
302+
execute_search,
303+
metadata=activity_options(
304+
start_to_close_timeout=timedelta(minutes=1),
305+
),
306+
)
307+
workflow.add_node(
308+
"evaluate",
309+
evaluate_results,
310+
metadata=activity_options(
311+
start_to_close_timeout=timedelta(seconds=30),
312+
),
313+
)
314+
workflow.add_node(
315+
"synthesize",
316+
synthesize_report,
317+
metadata=activity_options(
318+
start_to_close_timeout=timedelta(minutes=3),
319+
),
320+
)
295321

296322
# Add edges
297323
workflow.add_edge(START, "plan")

langgraph_plugin/graph_api/hello_world/graph.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,11 @@
44
It is imported only by the worker (not by the workflow).
55
"""
66

7+
from datetime import timedelta
78
from typing import Any
89

910
from langgraph.graph import END, START, StateGraph
11+
from temporalio.contrib.langgraph import activity_options
1012
from typing_extensions import TypedDict
1113

1214
# =============================================================================
@@ -49,8 +51,14 @@ def build_hello_graph() -> Any:
4951
"""
5052
graph = StateGraph(HelloState)
5153

52-
# Add a single processing node
53-
graph.add_node("process", process_query)
54+
# Add a single processing node with activity options
55+
graph.add_node(
56+
"process",
57+
process_query,
58+
metadata=activity_options(
59+
start_to_close_timeout=timedelta(seconds=30),
60+
),
61+
)
5462

5563
# Define edges: START -> process -> END
5664
graph.add_edge(START, "process")

langgraph_plugin/graph_api/human_in_the_loop/approval_graph_interrupt/graph.py

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,12 @@
99
3. execute_action: Processes the approved request (or rejects it)
1010
"""
1111

12+
from datetime import timedelta
1213
from typing import Any
1314

1415
from langgraph.graph import END, START, StateGraph
1516
from langgraph.types import interrupt
17+
from temporalio.contrib.langgraph import activity_options
1618
from typing_extensions import TypedDict
1719

1820

@@ -114,10 +116,28 @@ def build_approval_graph() -> Any:
114116
"""
115117
graph = StateGraph(ApprovalState)
116118

117-
# Add nodes
118-
graph.add_node("process_request", process_request)
119-
graph.add_node("request_approval", request_approval)
120-
graph.add_node("execute_action", execute_action)
119+
# Add nodes with activity options
120+
graph.add_node(
121+
"process_request",
122+
process_request,
123+
metadata=activity_options(
124+
start_to_close_timeout=timedelta(seconds=30),
125+
),
126+
)
127+
graph.add_node(
128+
"request_approval",
129+
request_approval,
130+
metadata=activity_options(
131+
start_to_close_timeout=timedelta(seconds=30),
132+
),
133+
)
134+
graph.add_node(
135+
"execute_action",
136+
execute_action,
137+
metadata=activity_options(
138+
start_to_close_timeout=timedelta(seconds=30),
139+
),
140+
)
121141

122142
# Define edges
123143
graph.add_edge(START, "process_request")

langgraph_plugin/graph_api/human_in_the_loop/approval_wait_condition/graph.py

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
from langgraph.graph import END, START, StateGraph
1616
from temporalio import activity
17+
from temporalio.contrib.langgraph import activity_options
1718
from typing_extensions import TypedDict
1819

1920

@@ -190,15 +191,27 @@ def build_approval_graph() -> Any:
190191

191192
graph = StateGraph(ApprovalState)
192193

193-
# Add nodes
194-
graph.add_node("process_request", process_request)
194+
# Add nodes with activity options
195+
graph.add_node(
196+
"process_request",
197+
process_request,
198+
metadata=activity_options(
199+
start_to_close_timeout=timedelta(seconds=30),
200+
),
201+
)
195202
# Mark request_approval as run_in_workflow - it can access Temporal operations
196203
graph.add_node(
197204
"request_approval",
198205
request_approval,
199206
metadata=temporal_node_metadata(run_in_workflow=True),
200207
)
201-
graph.add_node("execute_action", execute_action)
208+
graph.add_node(
209+
"execute_action",
210+
execute_action,
211+
metadata=activity_options(
212+
start_to_close_timeout=timedelta(seconds=30),
213+
),
214+
)
202215

203216
# Define edges
204217
graph.add_edge(START, "process_request")

langgraph_plugin/graph_api/plan_and_execute/graph.py

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
"""
2222

2323
import os
24+
from datetime import timedelta
2425
from typing import Annotated, Any, Literal
2526

2627
from langchain_core.messages import BaseMessage, HumanMessage, SystemMessage
@@ -31,6 +32,7 @@
3132
from langgraph.graph import END, START, StateGraph
3233
from langgraph.graph.message import add_messages
3334
from pydantic import BaseModel, Field
35+
from temporalio.contrib.langgraph import activity_options
3436
from typing_extensions import TypedDict
3537

3638
from langchain.agents import create_agent
@@ -427,12 +429,37 @@ def respond(state: PlanExecuteState) -> dict[str, Any]:
427429
# Build the plan-and-execute graph
428430
workflow = StateGraph(PlanExecuteState)
429431

430-
# Add nodes
431-
workflow.add_node("plan", create_plan)
432+
# Add nodes with activity options
433+
workflow.add_node(
434+
"plan",
435+
create_plan,
436+
metadata=activity_options(
437+
start_to_close_timeout=timedelta(minutes=2),
438+
),
439+
)
440+
# execute uses create_agent internally - configure via per_node_activity_options
432441
workflow.add_node("execute", execute_step)
433-
workflow.add_node("evaluate", evaluate_progress)
434-
workflow.add_node("replan", replan)
435-
workflow.add_node("respond", respond)
442+
workflow.add_node(
443+
"evaluate",
444+
evaluate_progress,
445+
metadata=activity_options(
446+
start_to_close_timeout=timedelta(seconds=30),
447+
),
448+
)
449+
workflow.add_node(
450+
"replan",
451+
replan,
452+
metadata=activity_options(
453+
start_to_close_timeout=timedelta(minutes=2),
454+
),
455+
)
456+
workflow.add_node(
457+
"respond",
458+
respond,
459+
metadata=activity_options(
460+
start_to_close_timeout=timedelta(minutes=1),
461+
),
462+
)
436463

437464
# Add edges
438465
workflow.add_edge(START, "plan")

langgraph_plugin/graph_api/react_agent/graph.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,22 @@ def build_react_agent() -> Any:
3535
3. Observe: Feed tool results back to LLM
3636
4. Repeat until done
3737
38+
Activity Configuration:
39+
Since create_agent() returns a pre-built graph, configure activity
40+
options at the plugin level using per_node_activity_options:
41+
42+
```python
43+
from temporalio.contrib.langgraph import LangGraphPlugin, activity_options
44+
45+
plugin = LangGraphPlugin(
46+
graphs={"react_agent": build_react_agent},
47+
per_node_activity_options={
48+
"agent": activity_options(start_to_close_timeout=timedelta(minutes=2)),
49+
"tools": activity_options(start_to_close_timeout=timedelta(minutes=1)),
50+
},
51+
)
52+
```
53+
3854
Returns:
3955
A compiled LangGraph that can be executed with ainvoke().
4056
"""

langgraph_plugin/graph_api/reflection/graph.py

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
"""
2121

2222
import os
23+
from datetime import timedelta
2324
from typing import Annotated, Any, Literal
2425

2526
from langchain_core.messages import BaseMessage, HumanMessage
@@ -29,6 +30,7 @@
2930
from langgraph.graph import END, START, StateGraph
3031
from langgraph.graph.message import add_messages
3132
from pydantic import BaseModel, Field
33+
from temporalio.contrib.langgraph import activity_options
3234
from typing_extensions import TypedDict
3335

3436

@@ -307,11 +309,35 @@ def finalize(state: ReflectionState) -> dict[str, Any]:
307309
# Build the reflection graph
308310
workflow = StateGraph(ReflectionState)
309311

310-
# Add nodes
311-
workflow.add_node("generate", generate)
312-
workflow.add_node("reflect", reflect)
313-
workflow.add_node("revise", revise)
314-
workflow.add_node("finalize", finalize)
312+
# Add nodes with activity options for LLM calls
313+
workflow.add_node(
314+
"generate",
315+
generate,
316+
metadata=activity_options(
317+
start_to_close_timeout=timedelta(minutes=2),
318+
),
319+
)
320+
workflow.add_node(
321+
"reflect",
322+
reflect,
323+
metadata=activity_options(
324+
start_to_close_timeout=timedelta(minutes=2),
325+
),
326+
)
327+
workflow.add_node(
328+
"revise",
329+
revise,
330+
metadata=activity_options(
331+
start_to_close_timeout=timedelta(minutes=2),
332+
),
333+
)
334+
workflow.add_node(
335+
"finalize",
336+
finalize,
337+
metadata=activity_options(
338+
start_to_close_timeout=timedelta(seconds=30),
339+
),
340+
)
315341

316342
# Add edges
317343
workflow.add_edge(START, "generate")

0 commit comments

Comments
 (0)