Skip to content

Commit 2554a8d

Browse files
authored
feat: Pydantic AI support (#133)
* feat: Pydantic AI support Add support for the Pydantic AI integration. Add missing background change confirmation. Increase agentic generation ui page size to allow it to display results from the Pydantic AI integration better. Add a description for the human in the loop generate_task_steps tool, so the Pydantic AI example works as expected. Merge write_document and confirm_changes tool into one, which allows it work correctly with the Pydantic AI example, without the need for the agent to perform two separate tool calls. Sort the features, so they are easier to find when reading the code and implementing new integrations. Fixes: #5 * chore: revert sorting of features Revert the sorting of features, as per review feedback. * fix: predictive state examples Fix predictive state examples by renaming the `write_document` tool to `write_document_local` and updating the metadata accordingly. This ensures that the examples correctly reflect the intended functionality of the predictive state updates, while still allowing the Pydantic AI example to leverage the AG-UI defined `write_document` tool. Restore the `confirm_changes` tool to enable the confirmation of changes for existing examples.
1 parent 25f1c32 commit 2554a8d

File tree

20 files changed

+433
-31
lines changed

20 files changed

+433
-31
lines changed

typescript-sdk/.gitignore

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,4 +41,7 @@ yarn-error.log*
4141
packages/proto/src/generated
4242

4343
# LangGraph API
44-
**/**/.langgraph_api
44+
**/**/.langgraph_api
45+
46+
# Python
47+
__pycache__/

typescript-sdk/apps/dojo/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
"@ag-ui/llamaindex": "workspace:*",
1717
"@ag-ui/mastra": "workspace:*",
1818
"@ag-ui/middleware-starter": "workspace:*",
19+
"@ag-ui/pydantic-ai": "workspace:*",
1920
"@ag-ui/server-starter": "workspace:*",
2021
"@ag-ui/server-starter-all-features": "workspace:*",
2122
"@ag-ui/vercel-ai-sdk": "workspace:*",

typescript-sdk/apps/dojo/src/agents.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import { AgnoAgent } from "@ag-ui/agno";
1313
import { LlamaIndexAgent } from "@ag-ui/llamaindex";
1414
import { CrewAIAgent } from "@ag-ui/crewai";
1515
import { mastra } from "./mastra";
16+
import { PydanticAIAgent } from "@ag-ui/pydantic-ai";
1617

1718
export const agentsIntegrations: AgentIntegrationConfig[] = [
1819
{
@@ -23,6 +24,31 @@ export const agentsIntegrations: AgentIntegrationConfig[] = [
2324
};
2425
},
2526
},
27+
{
28+
id: "pydantic-ai",
29+
agents: async () => {
30+
return {
31+
agentic_chat: new PydanticAIAgent({
32+
url: "http://localhost:9000/agentic_chat/",
33+
}),
34+
agentic_generative_ui: new PydanticAIAgent({
35+
url: "http://localhost:9000/agentic_generative_ui/",
36+
}),
37+
human_in_the_loop: new PydanticAIAgent({
38+
url: "http://localhost:9000/human_in_the_loop/",
39+
}),
40+
predictive_state_updates: new PydanticAIAgent({
41+
url: "http://localhost:9000/predictive_state_updates/",
42+
}),
43+
shared_state: new PydanticAIAgent({
44+
url: "http://localhost:9000/shared_state/",
45+
}),
46+
tool_based_generative_ui: new PydanticAIAgent({
47+
url: "http://localhost:9000/tool_based_generative_ui/",
48+
}),
49+
};
50+
},
51+
},
2652
{
2753
id: "server-starter",
2854
agents: async () => {

typescript-sdk/apps/dojo/src/app/[integrationId]/feature/agentic_chat/page.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,10 @@ const Chat = () => {
4242
],
4343
handler: ({ background }) => {
4444
setBackground(background);
45+
return {
46+
status: "success",
47+
message: `Background changed to ${background}`,
48+
};
4549
},
4650
});
4751

typescript-sdk/apps/dojo/src/app/[integrationId]/feature/agentic_generative_ui/page.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ const Chat = () => {
4242

4343
return (
4444
<div className="flex">
45-
<div className="bg-gray-100 rounded-lg w-[500px] p-4 text-black space-y-2">
45+
<div className="bg-gray-100 rounded-lg w-[800px] p-4 text-black space-y-2">
4646
{state.steps.map((step, index) => {
4747
if (step.status === "completed") {
4848
return (
@@ -55,7 +55,7 @@ const Chat = () => {
5555
index === state.steps.findIndex((s) => s.status === "pending")
5656
) {
5757
return (
58-
<div key={index} className="text-3xl font-bold text-slate-700">
58+
<div key={index} className="text-2xl font-bold text-slate-700">
5959
<Spinner />
6060
{step.description}
6161
</div>

typescript-sdk/apps/dojo/src/app/[integrationId]/feature/human_in_the_loop/page.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ const Chat = () => {
9999
});
100100
useCopilotAction({
101101
name: "generate_task_steps",
102+
description: "Generates a list of steps for the user to perform",
102103
parameters: [
103104
{
104105
name: "steps",

typescript-sdk/apps/dojo/src/app/[integrationId]/feature/predictive_state_updates/page.tsx

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@ const DocumentEditor = () => {
127127
}
128128
}, [text]);
129129

130+
// TODO(steve): Remove this when all agents have been updated to use write_document tool.
130131
useCopilotAction({
131132
name: "confirm_changes",
132133
renderAndWaitForResponse: ({ args, respond, status }) => (
@@ -147,6 +148,40 @@ const DocumentEditor = () => {
147148
),
148149
});
149150

151+
// Action to write the document.
152+
useCopilotAction({
153+
name: "write_document",
154+
description: `Present the proposed changes to the user for review`,
155+
parameters: [
156+
{
157+
name: "document",
158+
type: "string",
159+
description: "The full updated document in markdown format",
160+
},
161+
],
162+
renderAndWaitForResponse({ args, status, respond }) {
163+
if (status === "executing") {
164+
return (
165+
<ConfirmChanges
166+
args={args}
167+
respond={respond}
168+
status={status}
169+
onReject={() => {
170+
editor?.commands.setContent(fromMarkdown(currentDocument));
171+
setAgentState({ document: currentDocument });
172+
}}
173+
onConfirm={() => {
174+
editor?.commands.setContent(fromMarkdown(agentState?.document || ""));
175+
setCurrentDocument(agentState?.document || "");
176+
setAgentState({ document: agentState?.document || "" });
177+
}}
178+
/>
179+
);
180+
}
181+
return <></>;
182+
},
183+
});
184+
150185
return (
151186
<div className="relative min-h-screen w-full">
152187
{placeholderVisible && (

typescript-sdk/apps/dojo/src/menu.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,18 @@ export const menuIntegrations: MenuIntegrationConfig[] = [
66
name: "Middleware Starter",
77
features: ["agentic_chat"],
88
},
9+
{
10+
id: "pydantic-ai",
11+
name: "Pydantic AI",
12+
features: [
13+
"agentic_chat",
14+
"human_in_the_loop",
15+
"agentic_generative_ui",
16+
"tool_based_generative_ui",
17+
"shared_state",
18+
"predictive_state_updates",
19+
],
20+
},
921
{
1022
id: "server-starter",
1123
name: "Server Starter",

typescript-sdk/integrations/crewai/python/ag_ui_crewai/examples/predictive_state_updates.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
WRITE_DOCUMENT_TOOL = {
1717
"type": "function",
1818
"function": {
19-
"name": "write_document",
19+
"name": "write_document_local",
2020
"description": " ".join("""
2121
Write a document. Use markdown formatting to format the document.
2222
It's good to format the document extensively so it's easy to read.
@@ -63,19 +63,19 @@ async def chat(self):
6363
Standard chat node.
6464
"""
6565
system_prompt = f"""
66-
You are a helpful assistant for writing documents.
67-
To write the document, you MUST use the write_document tool.
66+
You are a helpful assistant for writing documents.
67+
To write the document, you MUST use the write_document_local tool.
6868
You MUST write the full document, even when changing only a few words.
6969
When you wrote the document, DO NOT repeat it as a message.
7070
Just briefly summarize the changes you made. 2 sentences max.
7171
This is the current state of the document: ----\n {self.state.document}\n-----
7272
"""
7373

74-
# 1. Here we specify that we want to stream the tool call to write_document
74+
# 1. Here we specify that we want to stream the tool call to write_document_local
7575
# to the frontend as state.
7676
await copilotkit_predict_state({
7777
"document": {
78-
"tool_name": "write_document",
78+
"tool_name": "write_document_local",
7979
"tool_argument": "document"
8080
}
8181
})
@@ -122,7 +122,7 @@ async def chat(self):
122122
tool_call_name = tool_call["function"]["name"]
123123
tool_call_args = json.loads(tool_call["function"]["arguments"])
124124

125-
if tool_call_name == "write_document":
125+
if tool_call_name == "write_document_local":
126126
self.state.document = tool_call_args["document"]
127127

128128
# 4.1 Append the result to the messages in state

typescript-sdk/integrations/langgraph/examples/agents/predictive_state_updates/agent.py

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
WRITE_DOCUMENT_TOOL = {
1919
"type": "function",
2020
"function": {
21-
"name": "write_document",
21+
"name": "write_document_local",
2222
"description": " ".join("""
2323
Write a document. Use markdown formatting to format the document.
2424
It's good to format the document extensively so it's easy to read.
@@ -64,25 +64,25 @@ async def chat_node(state: AgentState, config: RunnableConfig):
6464
"""
6565

6666
system_prompt = f"""
67-
You are a helpful assistant for writing documents.
68-
To write the document, you MUST use the write_document tool.
67+
You are a helpful assistant for writing documents.
68+
To write the document, you MUST use the write_document_local tool.
6969
You MUST write the full document, even when changing only a few words.
70-
When you wrote the document, DO NOT repeat it as a message.
70+
When you wrote the document, DO NOT repeat it as a message.
7171
Just briefly summarize the changes you made. 2 sentences max.
7272
This is the current state of the document: ----\n {state.get('document')}\n-----
7373
"""
7474

7575
# Define the model
7676
model = ChatOpenAI(model="gpt-4o")
77-
77+
7878
# Define config for the model with emit_intermediate_state to stream tool calls to frontend
7979
if config is None:
8080
config = RunnableConfig(recursion_limit=25)
8181

82-
# Use "predict_state" metadata to set up streaming for the write_document tool
82+
# Use "predict_state" metadata to set up streaming for the write_document_local tool
8383
config["metadata"]["predict_state"] = [{
8484
"state_key": "document",
85-
"tool": "write_document",
85+
"tool": "write_document_local",
8686
"tool_argument": "document"
8787
}]
8888

@@ -104,11 +104,11 @@ async def chat_node(state: AgentState, config: RunnableConfig):
104104

105105
# Update messages with the response
106106
messages = state["messages"] + [response]
107-
107+
108108
# Extract any tool calls from the response
109109
if hasattr(response, "tool_calls") and response.tool_calls:
110110
tool_call = response.tool_calls[0]
111-
111+
112112
# Handle tool_call as a dictionary or an object
113113
if isinstance(tool_call, dict):
114114
tool_call_id = tool_call["id"]
@@ -120,14 +120,14 @@ async def chat_node(state: AgentState, config: RunnableConfig):
120120
tool_call_name = tool_call.name
121121
tool_call_args = tool_call.args
122122

123-
if tool_call_name == "write_document":
123+
if tool_call_name == "write_document_local":
124124
# Add the tool response to messages
125125
tool_response = {
126126
"role": "tool",
127127
"content": "Document written.",
128128
"tool_call_id": tool_call_id
129129
}
130-
130+
131131
# Add confirmation tool call
132132
confirm_tool_call = {
133133
"role": "assistant",
@@ -140,9 +140,9 @@ async def chat_node(state: AgentState, config: RunnableConfig):
140140
}
141141
}]
142142
}
143-
143+
144144
messages = messages + [tool_response, confirm_tool_call]
145-
145+
146146
# Return Command to route to end
147147
return Command(
148148
goto=END,
@@ -151,7 +151,7 @@ async def chat_node(state: AgentState, config: RunnableConfig):
151151
"document": tool_call_args["document"]
152152
}
153153
)
154-
154+
155155
# If no tool was called, go to end
156156
return Command(
157157
goto=END,

0 commit comments

Comments
 (0)