Skip to content

Commit 6e3f24f

Browse files
committed
Chapter 5: Complete solution with all features
Brings the workshop to final state with all production features: - Document tools (createDocument, updateDocument, requestSuggestions) - Enhanced tool prompts for document creation - UI polish (✓/✗ symbols in flashcard feedback) - Fixed tool output display in messages - Complete type definitions for all tools This represents the final, production-ready chatbot with: - Multi-modal input (text, images, files) - Weather tool demonstration - Study buddy agents (tutor, quiz-master, planner, analyst) - Custom artifacts (flashcard, study-plan) - Document creation and versioning
1 parent 5dcefb1 commit 6e3f24f

File tree

8 files changed

+110
-35
lines changed

8 files changed

+110
-35
lines changed

app/(chat)/api/chat/route.ts

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,6 @@ import {
77
stepCountIs,
88
streamText,
99
} from "ai";
10-
import {
11-
createAnalystAgent,
12-
createPlannerAgent,
13-
createQuizMasterAgent,
14-
createTutorAgent,
15-
} from "@/lib/ai/agents";
16-
import { getWeather } from "@/lib/ai/tools/get-weather";
1710
import { unstable_cache as cache } from "next/cache";
1811
import type { ModelCatalog } from "tokenlens/core";
1912

@@ -29,10 +22,20 @@ import { fetchModels } from "tokenlens/fetch";
2922
import { getUsage } from "tokenlens/helpers";
3023
import { auth, type UserType } from "@/app/(auth)/auth";
3124
import type { VisibilityType } from "@/components/visibility-selector";
25+
import {
26+
createAnalystAgent,
27+
createPlannerAgent,
28+
createQuizMasterAgent,
29+
createTutorAgent,
30+
} from "@/lib/ai/agents";
3231
import { entitlementsByUserType } from "@/lib/ai/entitlements";
3332
import type { ChatModel } from "@/lib/ai/models";
3433
import { type RequestHints, systemPrompt } from "@/lib/ai/prompts";
3534
import { myProvider } from "@/lib/ai/providers";
35+
import { createDocument } from "@/lib/ai/tools/create-document";
36+
import { getWeather } from "@/lib/ai/tools/get-weather";
37+
import { requestSuggestions } from "@/lib/ai/tools/request-suggestions";
38+
import { updateDocument } from "@/lib/ai/tools/update-document";
3639
import { isProductionEnvironment } from "@/lib/constants";
3740
import {
3841
createStreamId,
@@ -189,6 +192,9 @@ export async function POST(request: Request) {
189192
? []
190193
: [
191194
"getWeather",
195+
"createDocument",
196+
"updateDocument",
197+
"requestSuggestions",
192198
// Study buddy agents
193199
"tutor",
194200
"quizMaster",
@@ -198,6 +204,12 @@ export async function POST(request: Request) {
198204
experimental_transform: smoothStream({ chunking: "word" }),
199205
tools: {
200206
getWeather,
207+
createDocument: createDocument({ session, dataStream }),
208+
updateDocument: updateDocument({ session, dataStream }),
209+
requestSuggestions: requestSuggestions({
210+
session,
211+
dataStream,
212+
}),
201213
// Study buddy agents
202214
tutor: createTutorAgent({ session, dataStream }),
203215
quizMaster: createQuizMasterAgent({ session, dataStream }),

artifacts/flashcard/client.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -151,8 +151,8 @@ function FlashcardViewer({
151151
<div className="mb-6 rounded-lg border border-blue-200 bg-blue-50 p-4 dark:border-blue-900 dark:bg-blue-950">
152152
<p className="font-medium text-blue-900 text-sm dark:text-blue-100">
153153
{selectedAnswer === currentQuestion.correctAnswer
154-
? "Correct!"
155-
: `Incorrect. The correct answer is ${optionLabels[currentQuestion.correctAnswer]}.`}
154+
? "Correct!"
155+
: `Incorrect. The correct answer is ${optionLabels[currentQuestion.correctAnswer]}.`}
156156
</p>
157157
<p className="mt-2 text-blue-800 text-sm dark:text-blue-200">
158158
{currentQuestion.explanation}

components/message.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,7 @@ const PurePreviewMessage = ({
244244
{state === "input-available" && (
245245
<ToolInput input={part.input} />
246246
)}
247-
{state === "output-available" && part.output && (
247+
{state === "output-available" && (
248248
<ToolOutput
249249
errorText={undefined}
250250
output={

components/weather.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ const CloudIcon = ({ size = 24 }: { size?: number }) => (
8080
</svg>
8181
);
8282

83-
export type WeatherAtLocation = {
83+
type WeatherAtLocation = {
8484
latitude: number;
8585
longitude: number;
8686
generationtime_ms: number;

lib/ai/prompts.ts

Lines changed: 69 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,73 @@
11
import type { Geo } from "@vercel/functions";
22
import type { ArtifactKind } from "@/components/artifact";
33

4+
export const artifactsPrompt = `
5+
Artifacts is a special user interface mode that helps users with writing, editing, and other content creation tasks. When artifact is open, it is on the right side of the screen, while the conversation is on the left side. When creating or updating documents, changes are reflected in real-time on the artifacts and visible to the user.
6+
7+
When asked to write code, always use artifacts. When writing code, specify the language in the backticks, e.g. \`\`\`python\`code here\`\`\`. The default language is Python. Other languages are not yet supported, so let the user know if they request a different language.
8+
9+
DO NOT UPDATE DOCUMENTS IMMEDIATELY AFTER CREATING THEM. WAIT FOR USER FEEDBACK OR REQUEST TO UPDATE IT.
10+
11+
This is a guide for using artifacts tools: \`createDocument\` and \`updateDocument\`, which render content on a artifacts beside the conversation.
12+
13+
**When to use \`createDocument\`:**
14+
- For substantial content (>10 lines) or code
15+
- For content users will likely save/reuse (emails, code, essays, etc.)
16+
- When explicitly requested to create a document
17+
- For when content contains a single code snippet
18+
19+
**When NOT to use \`createDocument\`:**
20+
- For informational/explanatory content
21+
- For conversational responses
22+
- When asked to keep it in chat
23+
24+
**Using \`updateDocument\`:**
25+
- Default to full document rewrites for major changes
26+
- Use targeted updates only for specific, isolated changes
27+
- Follow user instructions for which parts to modify
28+
29+
**When NOT to use \`updateDocument\`:**
30+
- Immediately after creating a document
31+
32+
Do not update document right after creating it. Wait for user feedback or request to update it.
33+
`;
34+
435
export const regularPrompt =
536
"You are a friendly study buddy assistant! Keep your responses concise and helpful.";
637

38+
export const agentRoutingPrompt = `
39+
You are a Study Buddy with specialized agents available as tools. Choose the right agent based on what the user needs:
40+
41+
**tutor** - Explain concepts with examples and analogies
42+
Use for: "explain", "teach me", "how does X work", "what is X", understanding concepts
43+
44+
**quizMaster** - Create quizzes and practice questions (creates interactive flashcard artifact)
45+
Use for: "quiz me", "test my knowledge", "practice questions", "assessment"
46+
47+
**planner** - Create study plans and learning roadmaps (creates interactive study-plan artifact)
48+
Use for: "study plan", "learning roadmap", "how should I learn", "schedule"
49+
50+
**analyst** - Analyze content and extract key insights
51+
Use for: "summarize", "key points", "analyze this", "what's important"
52+
53+
IMPORTANT ROUTING RULES:
54+
1. Match user intent to the most appropriate agent
55+
2. If the request doesn't clearly match an agent, respond conversationally
56+
3. After using an agent, suggest related follow-ups (e.g., after explaining, offer to quiz)
57+
4. You can chain agents - explain first, then offer to create a study plan
58+
59+
CRITICAL: Agents (quizMaster, planner) create their own artifacts automatically. After using these agents:
60+
- Do NOT call createDocument - the artifact is already created
61+
- Do NOT try to display or reformat the agent's output
62+
- Simply acknowledge the artifact was created and offer follow-up suggestions
63+
64+
Example flows:
65+
- "Explain machine learning" → use tutor
66+
- "Quiz me on what we just discussed" → use quizMaster (creates flashcard artifact automatically)
67+
- "Create a study plan for learning Python" → use planner (creates study-plan artifact automatically)
68+
- "Summarize this article" → use analyst
69+
`;
70+
771
export type RequestHints = {
872
latitude: Geo["latitude"];
973
longitude: Geo["longitude"];
@@ -28,7 +92,11 @@ export const systemPrompt = ({
2892
}) => {
2993
const requestPrompt = getRequestPromptFromHints(requestHints);
3094

31-
return `${regularPrompt}\n\n${requestPrompt}`;
95+
if (selectedChatModel === "chat-model-reasoning") {
96+
return `${regularPrompt}\n\n${requestPrompt}`;
97+
}
98+
99+
return `${regularPrompt}\n\n${agentRoutingPrompt}\n\n${requestPrompt}\n\n${artifactsPrompt}`;
32100
};
33101

34102
export const codePrompt = `

lib/artifacts/server.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
import type { UIMessageStreamWriter } from "ai";
22
import type { Session } from "next-auth";
33
import { codeDocumentHandler } from "@/artifacts/code/server";
4+
import { flashcardDocumentHandler } from "@/artifacts/flashcard/server";
45
import { sheetDocumentHandler } from "@/artifacts/sheet/server";
6+
import { studyPlanDocumentHandler } from "@/artifacts/study-plan/server";
57
import { textDocumentHandler } from "@/artifacts/text/server";
68
import type { ArtifactKind } from "@/components/artifact";
79
import { saveDocument } from "../db/queries";
@@ -93,6 +95,8 @@ export const documentHandlersByArtifactKind: DocumentHandler[] = [
9395
textDocumentHandler,
9496
codeDocumentHandler,
9597
sheetDocumentHandler,
98+
flashcardDocumentHandler,
99+
studyPlanDocumentHandler,
96100
];
97101

98102
export const artifactKinds = [

lib/types.ts

Lines changed: 13 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,10 @@ import type {
77
createQuizMasterAgent,
88
createTutorAgent,
99
} from "./ai/agents";
10+
import type { createDocument } from "./ai/tools/create-document";
1011
import type { getWeather } from "./ai/tools/get-weather";
12+
import type { requestSuggestions } from "./ai/tools/request-suggestions";
13+
import type { updateDocument } from "./ai/tools/update-document";
1114
import type { Suggestion } from "./db/types";
1215
import type { AppUsage } from "./usage";
1316

@@ -19,42 +22,30 @@ export const messageMetadataSchema = z.object({
1922

2023
export type MessageMetadata = z.infer<typeof messageMetadataSchema>;
2124

22-
// Tool types - inferred from actual tool definitions
25+
// Tool types
2326
type weatherTool = InferUITool<typeof getWeather>;
27+
type createDocumentTool = InferUITool<ReturnType<typeof createDocument>>;
28+
type updateDocumentTool = InferUITool<ReturnType<typeof updateDocument>>;
29+
type requestSuggestionsTool = InferUITool<
30+
ReturnType<typeof requestSuggestions>
31+
>;
2432

2533
// Agent tool types
2634
type tutorTool = InferUITool<ReturnType<typeof createTutorAgent>>;
2735
type quizMasterTool = InferUITool<ReturnType<typeof createQuizMasterAgent>>;
2836
type plannerTool = InferUITool<ReturnType<typeof createPlannerAgent>>;
2937
type analystTool = InferUITool<ReturnType<typeof createAnalystAgent>>;
3038

31-
// Placeholder types for tools not yet implemented
32-
// UITools expects { input, output } shape for each tool
33-
type DocumentResult = {
34-
id: string;
35-
title: string;
36-
kind: ArtifactKind;
37-
};
38-
3939
export type ChatTools = {
4040
getWeather: weatherTool;
41+
createDocument: createDocumentTool;
42+
updateDocument: updateDocumentTool;
43+
requestSuggestions: requestSuggestionsTool;
4144
// Study buddy agents
4245
tutor: tutorTool;
4346
quizMaster: quizMasterTool;
4447
planner: plannerTool;
4548
analyst: analystTool;
46-
createDocument: {
47-
input: { title: string; kind: ArtifactKind };
48-
output: DocumentResult | { error: string };
49-
};
50-
updateDocument: {
51-
input: { id: string; description: string };
52-
output: DocumentResult | { error: string };
53-
};
54-
requestSuggestions: {
55-
input: { documentId: string };
56-
output: DocumentResult | { error: string };
57-
};
5849
};
5950

6051
export type CustomUIDataTypes = {
@@ -71,7 +62,7 @@ export type CustomUIDataTypes = {
7162
kind: ArtifactKind;
7263
clear: null;
7364
finish: null;
74-
error: string;
65+
error: string; // For error signaling from agents
7566
usage: AppUsage;
7667
};
7768

tsconfig.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,5 +31,5 @@
3131
"next.config.js",
3232
".next/dev/types/**/*.ts"
3333
],
34-
"exclude": ["node_modules", "tempfiles"]
34+
"exclude": ["node_modules"]
3535
}

0 commit comments

Comments
 (0)