Skip to content

Commit 84515e8

Browse files
committed
fixup langgraph examples.
1 parent d3c0bb1 commit 84515e8

File tree

2 files changed

+35
-161
lines changed
  • typescript-sdk/integrations/langgraph/examples
    • python/agents/tool_based_generative_ui
    • typescript/src/agents/tool_based_generative_ui

2 files changed

+35
-161
lines changed

typescript-sdk/integrations/langgraph/examples/python/agents/tool_based_generative_ui/agent.py

Lines changed: 25 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -2,111 +2,64 @@
22
An example demonstrating tool-based generative UI using LangGraph.
33
"""
44

5-
from typing import List, Any, Optional, Annotated
6-
import os
7-
8-
# LangGraph imports
5+
from typing import Any, List
6+
from typing_extensions import Literal
97
from langchain_openai import ChatOpenAI
10-
from langchain_core.runnables import RunnableConfig
118
from langchain_core.messages import SystemMessage
12-
from langchain_core.tools import tool
13-
from langgraph.graph import StateGraph, END, START
9+
from langchain_core.runnables import RunnableConfig
10+
from langgraph.graph import StateGraph, END
1411
from langgraph.types import Command
1512
from langgraph.graph import MessagesState
1613
from langgraph.prebuilt import ToolNode
1714

18-
@tool
19-
def generate_haiku(
20-
japanese: Annotated[ # pylint: disable=unused-argument
21-
List[str],
22-
"An array of three lines of the haiku in Japanese"
23-
],
24-
english: Annotated[ # pylint: disable=unused-argument
25-
List[str],
26-
"An array of three lines of the haiku in English"
27-
]
28-
):
29-
"""
30-
Generate a haiku in Japanese and its English translation.
31-
Also select exactly 3 relevant images from the provided list based on the haiku's theme.
32-
"""
3315

3416
class AgentState(MessagesState):
3517
"""
3618
State of the agent.
3719
"""
3820
tools: List[Any]
3921

40-
async def chat_node(state: AgentState, config: Optional[RunnableConfig] = None):
41-
"""
42-
The main function handling chat and tool calls.
22+
async def chat_node(state: AgentState, config: RunnableConfig) -> Command[Literal["tool_node", "__end__"]]:
4323
"""
44-
45-
system_prompt = """
46-
You assist the user in generating a haiku.
47-
When generating a haiku using the 'generate_haiku' tool.
24+
Standard chat node based on the ReAct design pattern. It handles:
25+
- The model to use (and binds in CopilotKit actions and the tools defined above)
26+
- The system prompt
27+
- Getting a response from the model
28+
- Handling tool calls
29+
30+
For more about the ReAct design pattern, see:
31+
https://www.perplexity.ai/search/react-agents-NcXLQhreS0WDzpVaS4m9Cg
4832
"""
4933

50-
# Define the model
5134
model = ChatOpenAI(model="gpt-4o")
5235

53-
# Define config for the model
54-
if config is None:
55-
config = RunnableConfig(recursion_limit=25)
56-
57-
# Bind the tools to the model
5836
model_with_tools = model.bind_tools(
59-
[generate_haiku],
60-
# Disable parallel tool calls to avoid race conditions
37+
[
38+
*state.get("tools", []), # bind tools defined by ag-ui
39+
],
6140
parallel_tool_calls=False,
6241
)
6342

64-
# Run the model to generate a response
43+
system_message = SystemMessage(
44+
content=f"Help the user with writing Haikus. If the user asks for a haiku, use the generate_haiku tool to display the haiku to the user."
45+
)
46+
6547
response = await model_with_tools.ainvoke([
66-
SystemMessage(content=system_prompt),
48+
system_message,
6749
*state["messages"],
6850
], config)
6951

70-
if response.tool_calls:
71-
return Command(
72-
goto="tool_node",
73-
update={
74-
"messages": state["messages"] + [response]
75-
}
76-
)
77-
# Return Command to end with updated messages
7852
return Command(
7953
goto=END,
8054
update={
81-
"messages": state["messages"] + [response]
55+
"messages": [response],
8256
}
8357
)
8458

85-
# Define the graph
8659
workflow = StateGraph(AgentState)
87-
88-
# Add nodes
8960
workflow.add_node("chat_node", chat_node)
90-
workflow.add_node("tool_node", ToolNode([generate_haiku]))
91-
92-
# Add edges
61+
# This is required even though we don't have any backend tools to pass in.
62+
workflow.add_node("tool_node", ToolNode(tools=[]))
9363
workflow.set_entry_point("chat_node")
94-
workflow.add_edge(START, "chat_node")
95-
workflow.add_edge("chat_node", END)
96-
workflow.add_edge("tool_node", END)
97-
98-
99-
# Conditionally use a checkpointer based on the environment
100-
# Check for multiple indicators that we're running in LangGraph dev/API mode
101-
is_fast_api = os.environ.get("LANGGRAPH_FAST_API", "false").lower() == "true"
102-
103-
# Compile the graph
104-
if is_fast_api:
105-
# For CopilotKit and other contexts, use MemorySaver
106-
from langgraph.checkpoint.memory import MemorySaver
107-
memory = MemorySaver()
108-
graph = workflow.compile(checkpointer=memory)
109-
else:
110-
# When running in LangGraph API/dev, don't use a custom checkpointer
111-
graph = workflow.compile()
11264

65+
graph = workflow.compile()

typescript-sdk/integrations/langgraph/examples/typescript/src/agents/tool_based_generative_ui/agent.ts

Lines changed: 10 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -7,56 +7,6 @@ import { SystemMessage } from "@langchain/core/messages";
77
import { RunnableConfig } from "@langchain/core/runnables";
88
import { Command, Annotation, MessagesAnnotation, StateGraph, END, START } from "@langchain/langgraph";
99

10-
// List of available images (modify path if needed)
11-
const IMAGE_LIST = [
12-
"Osaka_Castle_Turret_Stone_Wall_Pine_Trees_Daytime.jpg",
13-
"Tokyo_Skyline_Night_Tokyo_Tower_Mount_Fuji_View.jpg",
14-
"Itsukushima_Shrine_Miyajima_Floating_Torii_Gate_Sunset_Long_Exposure.jpg",
15-
"Takachiho_Gorge_Waterfall_River_Lush_Greenery_Japan.jpg",
16-
"Bonsai_Tree_Potted_Japanese_Art_Green_Foliage.jpeg",
17-
"Shirakawa-go_Gassho-zukuri_Thatched_Roof_Village_Aerial_View.jpg",
18-
"Ginkaku-ji_Silver_Pavilion_Kyoto_Japanese_Garden_Pond_Reflection.jpg",
19-
"Senso-ji_Temple_Asakusa_Cherry_Blossoms_Kimono_Umbrella.jpg",
20-
"Cherry_Blossoms_Sakura_Night_View_City_Lights_Japan.jpg",
21-
"Mount_Fuji_Lake_Reflection_Cherry_Blossoms_Sakura_Spring.jpg"
22-
];
23-
24-
// This tool generates a haiku on the server.
25-
// The tool call will be streamed to the frontend as it is being generated.
26-
const GENERATE_HAIKU_TOOL = {
27-
type: "function",
28-
function: {
29-
name: "generate_haiku",
30-
description: "Generate a haiku in Japanese and its English translation. Also select exactly 3 relevant images from the provided list based on the haiku's theme.",
31-
parameters: {
32-
type: "object",
33-
properties: {
34-
japanese: {
35-
type: "array",
36-
items: {
37-
type: "string"
38-
},
39-
description: "An array of three lines of the haiku in Japanese"
40-
},
41-
english: {
42-
type: "array",
43-
items: {
44-
type: "string"
45-
},
46-
description: "An array of three lines of the haiku in English"
47-
},
48-
image_names: {
49-
type: "array",
50-
items: {
51-
type: "string"
52-
},
53-
description: "An array of EXACTLY THREE image filenames from the provided list that are most relevant to the haiku."
54-
}
55-
},
56-
required: ["japanese", "english", "image_names"]
57-
}
58-
}
59-
};
6010

6111
export const AgentStateAnnotation = Annotation.Root({
6212
tools: Annotation<any[]>(),
@@ -65,64 +15,35 @@ export const AgentStateAnnotation = Annotation.Root({
6515
export type AgentState = typeof AgentStateAnnotation.State;
6616

6717
async function chatNode(state: AgentState, config?: RunnableConfig): Promise<Command> {
68-
/**
69-
* The main function handling chat and tool calls.
70-
*/
71-
// Prepare the image list string for the prompt
72-
const imageListStr = IMAGE_LIST.map(img => `- ${img}`).join("\n");
73-
74-
const systemPrompt = `
75-
You assist the user in generating a haiku.
76-
When generating a haiku using the 'generate_haiku' tool, you MUST also select exactly 3 image filenames from the following list that are most relevant to the haiku's content or theme. Return the filenames in the 'image_names' parameter.
77-
78-
Available images:
79-
${imageListStr}
80-
81-
Don't provide the relevant image names in your final response to the user.
82-
`;
83-
84-
// Define the model
8518
const model = new ChatOpenAI({ model: "gpt-4o" });
86-
87-
// Define config for the model
88-
if (!config) {
89-
config = { recursionLimit: 25 };
90-
}
9119

92-
// Bind the tools to the model
9320
const modelWithTools = model.bindTools(
94-
[GENERATE_HAIKU_TOOL],
95-
{
96-
// Disable parallel tool calls to avoid race conditions
97-
parallel_tool_calls: false,
98-
}
21+
[
22+
...state.tools || []
23+
],
24+
{ parallel_tool_calls: false }
9925
);
10026

101-
// Run the model to generate a response
27+
const systemMessage = new SystemMessage({
28+
content: 'Help the user with writing Haikus. If the user asks for a haiku, use the generate_haiku tool to display the haiku to the user.'
29+
});
30+
10231
const response = await modelWithTools.invoke([
103-
new SystemMessage({ content: systemPrompt }),
32+
systemMessage,
10433
...state.messages,
10534
], config);
10635

107-
// Return Command to end with updated messages
10836
return new Command({
10937
goto: END,
11038
update: {
111-
messages: [...state.messages, response]
39+
messages: [response]
11240
}
11341
});
11442
}
11543

116-
// Define the graph
11744
const workflow = new StateGraph<AgentState>(AgentStateAnnotation);
118-
119-
// Add nodes
12045
workflow.addNode("chat_node", chatNode);
12146

122-
// Add edges
123-
workflow.setEntryPoint("chat_node");
12447
workflow.addEdge(START, "chat_node");
125-
workflow.addEdge("chat_node", END);
12648

127-
// Compile the graph
12849
export const toolBasedGenerativeUiGraph = workflow.compile();

0 commit comments

Comments
 (0)