Skip to content

Commit 7e23d77

Browse files
committed
add new query index and weather card for agent workflows
1 parent 5a230be commit 7e23d77

File tree

3 files changed

+168
-0
lines changed

3 files changed

+168
-0
lines changed

templates/types/streaming/nextjs/app/components/ui/chat/chat-message-content.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
11
import { ChatMessage } from "@llamaindex/chat-ui";
22
import { DeepResearchCard } from "./custom/deep-research-card";
33
import { ToolAnnotations } from "./tools/chat-tools";
4+
import { RetrieverComponent } from "./tools/query-index";
5+
import { WeatherToolComponent } from "./tools/weather-card";
46

57
export function ChatMessageContent() {
68
return (
79
<ChatMessage.Content>
810
<ChatMessage.Content.Event />
11+
<RetrieverComponent />
12+
<WeatherToolComponent />
913
<ChatMessage.Content.AgentEvent />
1014
<DeepResearchCard />
1115
<ToolAnnotations />
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
"use client";
2+
3+
import { getCustomAnnotation, useChatMessage } from "@llamaindex/chat-ui";
4+
import { ChatEvents } from "@llamaindex/chat-ui/widgets";
5+
import { useMemo } from "react";
6+
import { z } from "zod";
7+
8+
const QueryIndexSchema = z.object({
9+
tool_name: z.literal("query_index"),
10+
tool_kwargs: z.object({
11+
input: z.string(),
12+
}),
13+
tool_id: z.string(),
14+
tool_output: z.optional(
15+
z
16+
.object({
17+
content: z.string(),
18+
tool_name: z.string(),
19+
raw_input: z.record(z.unknown()),
20+
raw_output: z.record(z.unknown()),
21+
is_error: z.boolean().optional(),
22+
})
23+
.optional(),
24+
),
25+
return_direct: z.boolean().optional(),
26+
});
27+
type QueryIndex = z.infer<typeof QueryIndexSchema>;
28+
29+
type GroupedIndexQuery = {
30+
initial: QueryIndex;
31+
output?: QueryIndex;
32+
};
33+
34+
export function RetrieverComponent() {
35+
const { message } = useChatMessage();
36+
37+
const queryIndexEvents = getCustomAnnotation<QueryIndex>(
38+
message.annotations,
39+
(annotation) => {
40+
const result = QueryIndexSchema.safeParse(annotation);
41+
return result.success;
42+
},
43+
);
44+
45+
// Group events by tool_id and render them in a single ChatEvents component
46+
const groupedIndexQueries = useMemo(() => {
47+
const groups = new Map<string, GroupedIndexQuery>();
48+
49+
queryIndexEvents?.forEach((event) => {
50+
groups.set(event.tool_id, { initial: event });
51+
});
52+
53+
return Array.from(groups.values());
54+
}, [queryIndexEvents]);
55+
56+
return (
57+
<div className="space-y-4">
58+
{groupedIndexQueries.map(({ initial }) => {
59+
const eventData = [
60+
{
61+
title: `Searching index with query: ${initial.tool_kwargs.input}`,
62+
},
63+
];
64+
65+
if (initial.tool_output) {
66+
eventData.push({
67+
title: `Got ${JSON.stringify((initial.tool_output?.raw_output as any).source_nodes?.length ?? 0)} sources for query: ${initial.tool_kwargs.input}`,
68+
});
69+
}
70+
71+
return (
72+
<ChatEvents
73+
key={initial.tool_id}
74+
data={eventData}
75+
showLoading={!initial.tool_output}
76+
/>
77+
);
78+
})}
79+
</div>
80+
);
81+
}

templates/types/streaming/nextjs/app/components/ui/chat/tools/weather-card.tsx

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
import { getCustomAnnotation, useChatMessage } from "@llamaindex/chat-ui";
2+
import { ChatEvents } from "@llamaindex/chat-ui/widgets";
3+
import { useMemo } from "react";
4+
import { z } from "zod";
5+
16
export interface WeatherData {
27
latitude: number;
38
longitude: number;
@@ -211,3 +216,81 @@ export function WeatherCard({ data }: { data: WeatherData }) {
211216
</div>
212217
);
213218
}
219+
220+
// A new component for the weather tool which uses the WeatherCard component with the new data schema from agent workflow events
221+
const WeatherToolSchema = z.object({
222+
tool_name: z.literal("get_weather_information"),
223+
tool_kwargs: z.object({
224+
location: z.string(),
225+
}),
226+
tool_id: z.string(),
227+
tool_output: z.optional(
228+
z
229+
.object({
230+
content: z.string(),
231+
tool_name: z.string(),
232+
raw_input: z.record(z.unknown()),
233+
raw_output: z.custom<WeatherData>(),
234+
is_error: z.boolean().optional(),
235+
})
236+
.optional(),
237+
),
238+
return_direct: z.boolean().optional(),
239+
});
240+
241+
type WeatherTool = z.infer<typeof WeatherToolSchema>;
242+
243+
type GroupedWeatherQuery = {
244+
initial: WeatherTool;
245+
output?: WeatherTool;
246+
};
247+
248+
export function WeatherToolComponent() {
249+
const { message } = useChatMessage();
250+
251+
const weatherEvents = getCustomAnnotation<WeatherTool>(
252+
message.annotations,
253+
(annotation: unknown) => {
254+
const result = WeatherToolSchema.safeParse(annotation);
255+
return result.success;
256+
},
257+
);
258+
259+
// Group events by tool_id
260+
const groupedWeatherQueries = useMemo(() => {
261+
const groups = new Map<string, GroupedWeatherQuery>();
262+
263+
weatherEvents?.forEach((event: WeatherTool) => {
264+
groups.set(event.tool_id, { initial: event });
265+
});
266+
267+
return Array.from(groups.values());
268+
}, [weatherEvents]);
269+
270+
return (
271+
<div className="space-y-4">
272+
{groupedWeatherQueries.map(({ initial }) => {
273+
if (!initial.tool_output?.raw_output) {
274+
return (
275+
<ChatEvents
276+
key={initial.tool_id}
277+
data={[
278+
{
279+
title: `Loading weather information for ${initial.tool_kwargs.location}...`,
280+
},
281+
]}
282+
showLoading={true}
283+
/>
284+
);
285+
}
286+
287+
return (
288+
<WeatherCard
289+
key={initial.tool_id}
290+
data={initial.tool_output.raw_output as WeatherData}
291+
/>
292+
);
293+
})}
294+
</div>
295+
);
296+
}

0 commit comments

Comments
 (0)