### Implement Few-Shot Prompting with Exemplars Source: https://context7.com/ai-hero-dev/ai-sdk-v5-crash-course/llms.txt Shows how to use the Exemplars pattern for few-shot prompting to guide AI responses. This example uses predefined input/output pairs to ensure consistent formatting for generated titles based on research queries. ```typescript import { google } from '@ai-sdk/google'; import { streamText } from 'ai'; const INPUT = `Do some research on induction hobs and how I can replace a 100cm wide AGA cooker with an induction range cooker.`; const exemplars = [ { input: `What's the difference between TypeScript and JavaScript?`, expected: 'TypeScript vs JavaScript Comparison', }, { input: `I want to start investing but I'm a complete beginner.`, expected: 'Beginner Investment Options', }, ]; const result = await streamText({ model: google('gemini-2.5-flash-lite'), prompt: ` ${exemplars.map((e) => ` ${e.input} ${e.expected} `).join(' ')} ${INPUT} Generate a title for the conversation. `, }); for await (const chunk of result.textStream) { process.stdout.write(chunk); } // Output: "Induction Range Cooker Research" ``` -------------------------------- ### Clone Repository and Install Dependencies Source: https://github.com/ai-hero-dev/ai-sdk-v5-crash-course/blob/main/README.md Instructions for cloning the AI SDK v5 Crash Course repository and installing its dependencies using pnpm. This is a prerequisite for running the course exercises. ```bash git clone https://github.com/ai-hero-dev/ai-sdk-v5-crash-course.git cd ai-sdk-v5-crash-course pnpm install ``` -------------------------------- ### System Prompts for AI Behavior Customization (TypeScript) Source: https://context7.com/ai-hero-dev/ai-sdk-v5-crash-course/llms.txt Configures AI behavior and personality using system prompts to guide responses. This example demonstrates how to set a system prompt to ensure the AI always replies in pirate language. It integrates with the streaming text functionality. ```typescript import { google } from '@ai-sdk/google'; import { convertToModelMessages, createUIMessageStreamResponse, streamText, type UIMessage, } from 'ai'; const SYSTEM_PROMPT = ` ALWAYS reply in Pirate language. ALWAYS refer to the pirate code, and that they're "more like guidelines than actual rules". If the user asks you to use a different language, politely decline. `; export const POST = async (req: Request): Promise => { const body = await req.json(); const messages: UIMessage[] = body.messages; const streamTextResult = streamText({ model: google('gemini-2.5-flash'), messages: convertToModelMessages(messages), system: SYSTEM_PROMPT, }); return createUIMessageStreamResponse({ stream: streamTextResult.toUIMessageStream(), }); }; ``` -------------------------------- ### Correcting Stream Start and Finish Parts Source: https://github.com/ai-hero-dev/ai-sdk-v5-crash-course/blob/main/exercises/99-reference/99.09-start-and-finish-parts/explainer/readme.md This example shows how to manually write a 'start' part at the beginning of a stream to resolve issues with duplicate messages. By uncommenting `writer.write({ type: 'start' });`, you ensure the stream is properly initialized, preventing subsequent parts from being misinterpreted as separate messages. ```typescript // TODO: Try uncommenting this and see what happens // writer.write({ // type: 'start', // }); ``` -------------------------------- ### Run Course Development Server Source: https://github.com/ai-hero-dev/ai-sdk-v5-crash-course/blob/main/README.md Command to start the development server for the AI SDK v5 Crash Course. This allows users to navigate and select different course sections. ```bash pnpm dev ``` -------------------------------- ### Example Query for Testing in React Source: https://github.com/ai-hero-dev/ai-sdk-v5-crash-course/blob/main/exercises/09-advanced-patterns/09.04-research-workflow/problem/readme.md This code snippet demonstrates how to set up an example query string using the `useState` hook in a React component. It's used to test the AI solution with a specific user prompt. ```tsx const [input, setInput] = useState( `Which are better? Gas, electric, or induction hobs? Please provide a detailed answer.`, ); ``` -------------------------------- ### Configure Environment Variables Source: https://github.com/ai-hero-dev/ai-sdk-v5-crash-course/blob/main/README.md Steps to configure the project's environment variables by copying the example file and adding necessary API keys. This is essential for connecting to AI providers. ```bash cp .env.example .env # Add your API keys to .env ``` -------------------------------- ### Example: Logging Database Message Parts (TypeScript) Source: https://github.com/ai-hero-dev/ai-sdk-v5-crash-course/blob/main/exercises/04-persistence/04.04-persistence-in-a-normalized-db/explainer/readme.md Demonstrates how to call the `mapUIMessagePartsToDBParts` function and log the resulting database message parts to the console. This is useful for inspecting the structure of the converted data. ```typescript const dbMessageParts = mapUIMessagePartsToDBParts( message.parts, message.id, ); console.dir(dbMessageParts, { depth: null }); ``` -------------------------------- ### Tool Execution Log Output (Text) Source: https://github.com/ai-hero-dev/ai-sdk-v5-crash-course/blob/main/exercises/99-reference/99.02-defining-tools/explainer/readme.md Demonstrates the expected output stream when a tool is invoked and executed. This includes stages like 'start', 'start-step', 'tool-input-start', 'tool-input-delta', 'tool-input-available', 'tool-output-available', 'finish-step', and 'finish', showing the flow of data and execution. ```text { type: 'start' } { type: 'start-step' } { type: 'tool-input-start', toolCallId: 'B1iGVK2Sa3b0JRzJ', toolName: 'logToConsole', dynamic: false } { type: 'tool-input-delta', toolCallId: 'B1iGVK2Sa3b0JRzJ', inputTextDelta: '{"message":"Hello, world!"}' } Hello, world! { type: 'tool-input-available', toolCallId: 'B1iGVK2Sa3b0JRzJ', toolName: 'logToConsole', input: { message: 'Hello, world!' } } { type: 'tool-output-available', toolCallId: 'B1iGVK2Sa3b0JRzJ', output: 'Message logged to console' } { type: 'finish-step' } { type: 'finish' } ``` -------------------------------- ### Setup Multiple Model Streams with Promise.all Source: https://github.com/ai-hero-dev/ai-sdk-v5-crash-course/blob/main/exercises/09-advanced-patterns/09.03-comparing-multiple-outputs/problem/readme.md Initializes a UI message stream and concurrently streams text from two different Google Gemini models using `streamText`. It prepares for parallel execution of model calls using `Promise.all`. ```typescript const stream = createUIMessageStream({ execute: async ({ writer }) => { const firstStreamResult = streamText({ model: google('gemini-2.5-flash-lite'), messages: modelMessages, }); const secondStreamResult = streamText({ model: google('gemini-2.5-flash'), messages: modelMessages, }); // TODO: Using Promise.all, call streamModelText for each model // and pass in the appropriate model await Promise.all(TODO); }, }); ``` -------------------------------- ### ModelMessage Structure Example Source: https://github.com/ai-hero-dev/ai-sdk-v5-crash-course/blob/main/exercises/99-reference/99.01-ui-messages-vs-model-messages/explainer/readme.md Illustrates the structure of a ModelMessage after conversion from UIMessage. This format is suitable for sending to the LLM, featuring a role and a content array. ```typescript [ { role: 'user', content: [ { type: 'text', text: 'What is the capital of France?' }, ], }, { role: 'assistant', content: [ { type: 'text', text: 'The capital of France is Paris.' }, ], }, ]; ``` -------------------------------- ### Evaluate LLM Outputs with Evalite Framework Source: https://context7.com/ai-hero-dev/ai-sdk-v5-crash-course/llms.txt Demonstrates using the Evalite framework to test and evaluate AI model outputs. This example sets up a task to answer capital city questions and uses a custom 'includes' scorer to check if the output contains the expected answer. ```typescript import { google } from '@ai-sdk/google'; import { generateText } from 'ai'; import { evalite } from 'evalite'; evalite('Capitals', { data: () => [ { input: 'What is the capital of France?', expected: 'Paris' }, { input: 'What is the capital of Germany?', expected: 'Berlin' }, { input: 'What is the capital of Italy?', expected: 'Rome' }, ], task: async (input) => { const result = await generateText({ model: google('gemini-2.5-flash-lite'), prompt: ` You are a helpful assistant that answers questions about capitals. ${input} Reply only with the capital of the country. `, }); return result.text; }, scorers: [ { name: 'includes', scorer: ({ input, output, expected }) => { return output.includes(expected!) ? 1 : 0; }, }, ], }); // Run with: npx evalite ``` -------------------------------- ### Manually Start UIMessageStream Source: https://github.com/ai-hero-dev/ai-sdk-v5-crash-course/blob/main/exercises/08-agents-and-workflows/08.02-streaming-custom-data-to-the-frontend/problem/readme.md Manually initiate the `UIMessageStream` by writing a `{ type: 'start' }` message. This is necessary when merging streams to control the start signal and avoid duplication, ensuring the stream begins correctly on the frontend. ```typescript // TODO: write a { type: 'start' } message via writer.write TODO; ``` -------------------------------- ### Add XML Exemplars to Prompt - TypeScript Source: https://github.com/ai-hero-dev/ai-sdk-v5-crash-course/blob/main/exercises/05-context-engineering/05.03-exemplars/problem/readme.md This snippet demonstrates how to add exemplars to a prompt for an AI model. It defines an array of input-output pairs and then formats them into XML strings to be embedded within a larger prompt structure. This approach helps guide the model by providing concrete examples of desired input and output. ```typescript const exemplars = [ { input: `What's the difference between TypeScript and JavaScript? Should I learn TypeScript first or JavaScript?`, expected: 'TypeScript vs JavaScript Comparison', }, { input: `I want to start investing but I'm a complete beginner. What are the safest options for someone with $5000 to invest?`, expected: 'Beginner Investment Options', }, ]; const formattedExemplars = exemplars.map(e => ` ${e.input} ${e.expected} `).join('\n'); const result = await streamText({ model: google('gemini-2.5-flash-lite'), prompt: ` You are a helpful assistant that can generate titles for conversations. Find the most concise title that captures the essence of the conversation. Titles should be at most 30 characters. Titles should be formatted in sentence case, with capital letters at the start of each word. Do not provide a period at the end. ${formattedExemplars} ${INPUT} Generate a title for the conversation. Return only the title. `, }); ``` -------------------------------- ### Initialize OpenTelemetry SDK with LangFuse Exporter (TypeScript) Source: https://github.com/ai-hero-dev/ai-sdk-v5-crash-course/blob/main/exercises/06-evals/06.07-langfuse-basics/problem/readme.md Instantiate the NodeSDK from the OpenTelemetry package and configure it with the LangFuseExporter. This setup enables the collection and export of trace data to LangFuse for observability. ```typescript import { NodeSDK } from '@opentelemetry/sdk-node'; import { LangfuseExporter } from 'langfuse-vercel'; export const otelSDK = new NodeSDK({ traceExporter: new LangfuseExporter(), }); ``` -------------------------------- ### Chat Persistence with onFinish Callback (TypeScript) Source: https://context7.com/ai-hero-dev/ai-sdk-v5-crash-course/llms.txt This code demonstrates how to persist chat messages to a database using the `onFinish` callback. It handles both the initial chat creation and appending new messages to an existing chat. Dependencies include a local persistence layer (`./persistence-layer.ts`). The `POST` function handles incoming messages and streams responses, while the `GET` function retrieves chat history. ```typescript import { convertToModelMessages, streamText, type UIMessage, } from 'ai'; import { google } from '@ai-sdk/google'; import { createChat, getChat, appendToChatMessages } from './persistence-layer.ts'; export const POST = async (req: Request): Promise => { const body: { messages: UIMessage[]; id: string } = await req.json(); const { messages, id } = body; let chat = await getChat(id); const mostRecentMessage = messages[messages.length - 1]; if (!chat) { chat = await createChat(id, messages); } else { await appendToChatMessages(id, [mostRecentMessage]); } const result = streamText({ model: google('gemini-2.5-flash'), messages: convertToModelMessages(messages), }); return result.toUIMessageStreamResponse({ onFinish: async ({ responseMessage }) => { await appendToChatMessages(id, [responseMessage]); }, }); }; export const GET = async (req: Request): Promise => { const url = new URL(req.url); const chatId = url.searchParams.get('chatId'); const chat = await getChat(chatId!); return new Response(JSON.stringify(chat), { headers: { 'Content-Type': 'application/json' }, }); }; ``` -------------------------------- ### Generate Text using AI SDK Source: https://github.com/ai-hero-dev/ai-sdk-v5-crash-course/blob/main/exercises/01-ai-sdk-basics/01.04-generating-text/problem/readme.md This snippet illustrates how to use the 'generateText' function from the 'ai' package to get a response from an AI model. It takes the instantiated model and the prompt as arguments and returns the generated text after awaiting the result. ```typescript const result = await generateText({ model, prompt }); console.log(result.text); ``` -------------------------------- ### Example Status Update Sequence (Text) Source: https://github.com/ai-hero-dev/ai-sdk-v5-crash-course/blob/main/exercises/99-reference/99.06-custom-data-parts-id-reconciliation/explainer/readme.md Illustrates a sequence of status messages that would typically be updated in place using stable IDs. Instead of displaying each message as a new item, the UI would show a single, evolving status. ```text Loading... Searching the web... Scraping some pages... Summarizing... ``` -------------------------------- ### System Prompts for Slack Message Generation and Evaluation Source: https://github.com/ai-hero-dev/ai-sdk-v5-crash-course/blob/main/exercises/08-agents-and-workflows/08.01-workflow/problem/readme.md These constants define the system prompts used by the LLM for different stages of the Slack message workflow. They guide the LLM to generate a first draft, evaluate it based on specific criteria, and produce a final refined message. ```typescript const WRITE_SLACK_MESSAGE_FIRST_DRAFT_SYSTEM = `You are writing a Slack message for a user based on the conversation history. Only return the Slack message, no other text.`; const EVALUATE_SLACK_MESSAGE_SYSTEM = `You are evaluating the Slack message produced by the user. Evaluation criteria: - The Slack message should be written in a way that is easy to understand. - It should be appropriate for a professional Slack conversation. `; const WRITE_SLACK_MESSAGE_FINAL_SYSTEM = `You are writing a Slack message based on the conversation history, a first draft, and some feedback given about that draft. Return only the final Slack message, no other text. `; ``` -------------------------------- ### TypeScript: Define Thinking and Output Formatting Instructions Source: https://github.com/ai-hero-dev/ai-sdk-v5-crash-course/blob/main/exercises/05-context-engineering/05.01-the-template/explainer/readme.md This TypeScript code defines a template string that includes specific instructions for an AI's thinking process and output formatting. It uses XML-like tags to delineate these sections, aiming to guide the AI's response generation. ```typescript export const THINKING_INSTRUCTIONS_TEMPLATE = ` Think about your answer first before you respond. Put your response in tags. `; ``` -------------------------------- ### Disabling Start Part for Third Paragraph Stream Source: https://github.com/ai-hero-dev/ai-sdk-v5-crash-course/blob/main/exercises/99-reference/99.09-start-and-finish-parts/explainer/readme.md This example demonstrates the configuration for merging the third and final paragraph's stream. Here, only `sendStart: false` is uncommented, allowing the stream to proceed without sending a redundant start part, while `sendFinish: true` (implicitly or explicitly) ensures that a 'finish' part is sent at the very end of the entire stream, signaling completion. ```typescript writer.merge( thirdParagraphResult.toUIMessageStream({ // TODO: Try uncommenting these and see what happens // sendStart: false, }), ); ``` -------------------------------- ### Stream Structured Object from LLM with Zod Schema Source: https://github.com/ai-hero-dev/ai-sdk-v5-crash-course/blob/main/exercises/01-ai-sdk-basics/01.10-streaming-objects/problem/readme.md This snippet demonstrates how to use `streamObject` to get structured data back from an LLM. It requires a model, a prompt, and a Zod schema defining the desired output object structure. The example defines a schema for an object containing an array of strings. ```typescript import { streamObject } from 'ai'; import { z } from 'zod'; const schema = z.object({ facts: z.array(z.string()), }); const factsResult = await streamObject({ model, prompt: ` Write facts about the imaginary planet described in this story: ${finalText} `, schema, }); ``` -------------------------------- ### Initialize Test Cases and Select One (TypeScript) Source: https://github.com/ai-hero-dev/ai-sdk-v5-crash-course/blob/main/exercises/05-context-engineering/05.04-retrieval/problem/readme.md Sets up an array of test cases, each containing a user input question and a URL to scrape. It then selects a specific test case to process based on the `TEST_CASE_TO_TRY` index. ```typescript import { google } from '@ai-sdk/google'; import { streamText } from 'ai'; import { tavily } from '@tavily/core'; const testCases = [ { input: 'What did Guillermo Rauch say about Matt Pocock?', url: 'https://www.aihero.dev/', }, { input: "What is Matt Pocock's open source background?", url: 'https://www.aihero.dev/', }, { input: 'Why is learning TypeScript important?', url: 'https://totaltypescript.com/', }, ] as const; // Change this to try a different test case const TEST_CASE_TO_TRY = 0; const { input, url } = testCases[TEST_CASE_TO_TRY]; ``` -------------------------------- ### MCP Integration for Dynamic Tool Addition (TypeScript) Source: https://context7.com/ai-hero-dev/ai-sdk-v5-crash-course/llms.txt This snippet shows how to integrate with external Model Context Protocol (MCP) servers to dynamically add tools, such as those from GitHub. It uses 'ai' SDK for MCP client creation and 'ai/mcp-stdio' for stdio transport. The function takes UI messages, connects to an MCP server, retrieves tools, and streams text, closing the MCP client upon completion. It requires a GitHub personal access token. ```typescript import { convertToModelMessages, stepCountIs, streamText, type UIMessage, } from 'ai'; import { google } from '@ai-sdk/google'; import { experimental_createMCPClient as createMCPClient } from 'ai'; import { Experimental_StdioMCPTransport as StdioMCPTransport } from 'ai/mcp-stdio'; export const POST = async (req: Request): Promise => { const body: { messages: UIMessage[] } = await req.json(); const { messages } = body; const mcpClient = await createMCPClient({ transport: new StdioMCPTransport({ command: 'docker', args: [ 'run', '-i', '--rm', '-e', 'GITHUB_PERSONAL_ACCESS_TOKEN', 'ghcr.io/github/github-mcp-server', ], env: { GITHUB_PERSONAL_ACCESS_TOKEN: process.env.GITHUB_PERSONAL_ACCESS_TOKEN!, }, }), }); const result = streamText({ model: google('gemini-2.5-flash'), messages: convertToModelMessages(messages), system: `You are a helpful assistant that can use the GitHub API.`, tools: await mcpClient.tools(), stopWhen: [stepCountIs(10)], }); return result.toUIMessageStreamResponse({ onFinish: async () => { await mcpClient.close(); }, }); }; ``` -------------------------------- ### Instantiate OpenAI Model (TypeScript) Source: https://github.com/ai-hero-dev/ai-sdk-v5-crash-course/blob/main/exercises/01-ai-sdk-basics/01.03-choosing-your-model/explainer/readme.md Instantiates an OpenAI language model, in this case 'gpt-4o-mini'. The instantiated model object is then logged to the console for inspection. This requires the 'openai' package to be imported and the OPENAI_API_KEY environment variable to be configured. ```typescript const model = openai('gpt-4o-mini'); console.dir(model, { depth: null }); ``` -------------------------------- ### Backend GET Endpoint for Chat API (TypeScript) Source: https://github.com/ai-hero-dev/ai-sdk-v5-crash-course/blob/main/exercises/04-persistence/04.03-persistence/problem/readme.md This TypeScript code implements the GET endpoint for the `/api/chat` route. It extracts the `chatId` from the request URL's search parameters and uses the `getChat` function to retrieve chat data from the database. ```typescript export const GET = async (req: Request): Promise => { const url = new URL(req.url); const chatId = url.searchParams.get('chatId'); if (!chatId) { return new Response('No chatId provided', { status: 400 }); } const chat = await getChat(chatId); return new Response(JSON.stringify(chat), { headers: { 'Content-Type': 'application/json', }, }); }; ``` -------------------------------- ### TypeScript Mapped Type Example Source: https://github.com/ai-hero-dev/ai-sdk-v5-crash-course/blob/main/exercises/05-context-engineering/05.05-chain-of-thought/solution/output.md Demonstrates TypeScript's mapped types, which create new types by iterating over the keys of an existing type. This example transforms an object's properties to have a boolean type, illustrating how to derive new type structures from existing ones. ```typescript type SomeObject = { a: string; b: number; }; type MappedType = { [K in keyof SomeObject]: boolean; // For each key in SomeObject, create a boolean property }; ``` -------------------------------- ### Disabling Start and Finish for Second Paragraph Stream Source: https://github.com/ai-hero-dev/ai-sdk-v5-crash-course/blob/main/exercises/99-reference/99.09-start-and-finish-parts/explainer/readme.md Similar to the first paragraph, this snippet shows how to merge the second paragraph's stream while disabling both 'start' and 'finish' part transmissions. By setting `sendStart: false` and `sendFinish: false` in the `toUIMessageStream` options, it prevents the creation of duplicate message headers and footers, ensuring a cleaner stream integration. ```typescript writer.merge( secondParagraphResult.toUIMessageStream({ // TODO: Try uncommenting these and see what happens // sendStart: false, // sendFinish: false, }), ); ``` -------------------------------- ### Run Specific Exercise Source: https://github.com/ai-hero-dev/ai-sdk-v5-crash-course/blob/main/README.md Command to directly run a specific exercise from the AI SDK v5 Crash Course. This is useful for jumping to a particular topic or problem. ```bash pnpm exercise ``` -------------------------------- ### Optimizing Stream Merging with `toUIMessageStream` Options Source: https://github.com/ai-hero-dev/ai-sdk-v5-crash-course/blob/main/exercises/99-reference/99.09-start-and-finish-parts/explainer/readme.md This code illustrates how to refine the behavior of `toUIMessageStream` by controlling the sending of 'start' and 'finish' parts. For intermediate streams like the first paragraph, both `sendStart` and `sendFinish` are set to `false` to avoid redundant message parts. This approach aligns with the AI SDK's expectation of a single overall stream start and finish. ```typescript writer.merge( firstParagraphResult.toUIMessageStream({ // TODO: Try uncommenting these and see what happens // sendStart: false, // sendFinish: false, }), ); ``` -------------------------------- ### Import AI SDK Providers (TypeScript) Source: https://github.com/ai-hero-dev/ai-sdk-v5-crash-course/blob/main/exercises/01-ai-sdk-basics/01.03-choosing-your-model/explainer/readme.md Imports the necessary modules for OpenAI, Google, and Anthropic AI providers from the AI SDK. Ensure the corresponding environment variables (e.g., OPENAI_API_KEY) are set in your .env file. ```typescript // Requires an OPENAI_API_KEY environment variable in .env import { openai } from '@ai-sdk/openai'; // Requires a GOOGLE_GENERATIVE_AI_API_KEY environment variable in .env import { google } from '@ai-sdk/google'; // Requires an ANTHROPIC_API_KEY environment variable in .env import { anthropic } from '@ai-sdk/anthropic'; ``` -------------------------------- ### Configure Evalite Data for Capital Questions Source: https://github.com/ai-hero-dev/ai-sdk-v5-crash-course/blob/main/exercises/06-evals/06.01-evalite-basics/problem/readme.md This code demonstrates how to provide sample data to Evalite for testing. Each data point includes an input question and the expected output (the capital city). ```typescript evalite('Capitals', { data: () => [ { input: 'What is the capital of France?', expected: 'Paris', }, { input: 'What is the capital of Germany?', expected: 'Berlin', }, { input: 'What is the capital of Italy?', expected: 'Rome', }, ], // ...other properties }); ``` -------------------------------- ### Setting up StdioMCPTransport for GitHub MCP Server (TypeScript) Source: https://github.com/ai-hero-dev/ai-sdk-v5-crash-course/blob/main/exercises/03-agents/03.04-mcp-via-stdio/problem/readme.md Configures the StdioMCPTransport to connect to the GitHub MCP server running in a Docker container. It specifies the Docker command, arguments for running the server, and necessary environment variables, including the GitHub Personal Access Token. ```typescript const myTransport = new StdioMCPTransport({ command: 'docker', args: [ 'run', '-i', '--rm', '-e', 'GITHUB_PERSONAL_ACCESS_TOKEN', 'ghcr.io/github/github-mcp-server', ], env: { GITHUB_PERSONAL_ACCESS_TOKEN: process.env.GITHUB_PERSONAL_ACCESS_TOKEN!, }, }); ``` -------------------------------- ### Define Data Structure for LLM Input Source: https://github.com/ai-hero-dev/ai-sdk-v5-crash-course/blob/main/exercises/02-llm-fundamentals/02.03-data-represented-as-tokens/explainer/readme.md Initializes an array of objects, each containing a URL and a title. This data structure serves as the starting point for generating different formats for LLM processing. ```typescript const DATA = [ { url: 'https://aihero.dev', title: 'AI Hero', }, { url: 'https://totaltypescript.com', title: 'Total TypeScript', }, { url: 'https://mattpocock.com', title: 'Matt Pocock', }, { url: 'https://twitter.com/mattpocockuk', title: 'Twitter', }, ]; ``` -------------------------------- ### Initialize Evalite for 'Capitals' Eval Source: https://github.com/ai-hero-dev/ai-sdk-v5-crash-course/blob/main/exercises/06-evals/06.01-evalite-basics/problem/readme.md This snippet shows the basic initialization of Evalite with a title for the 'Capitals' evaluation. It sets up the framework for running AI model evaluations. ```typescript import { evalite } from 'evalite'; evalite('Capitals', { // Configuration goes here }); ``` -------------------------------- ### Consume Stream with streamTextResult.consumeStream() (TypeScript) Source: https://github.com/ai-hero-dev/ai-sdk-v5-crash-course/blob/main/exercises/99-reference/99.03-consume-stream/explainer.1/readme.md This example shows how to consume the stream directly from the result of `streamText` using `streamTextResult.consumeStream()`. This ensures that the stream is fully processed, which in turn guarantees that the `onFinish` callback is executed. ```typescript import { streamText } from 'ai/streaming'; import { ollama } from '@ai-sdk/ollama'; async function main() { const result = await ollama.chat({ model: 'llama3', messages: [ { role: 'user', content: 'Explain the importance of consuming streams.', } ], stream: true, }); const streamTextResult = await streamText({ model: ollama.chat, messages: [ { role: 'user', content: 'Explain the importance of consuming streams.', } ], onFinish: async () => { console.log('Stream finished!'); }, }); // Consume the stream to ensure onFinish is called await streamTextResult.consumeStream(); console.log('Stream consumption complete.'); } main(); ``` -------------------------------- ### Import Dependencies for File System Tools (TypeScript) Source: https://github.com/ai-hero-dev/ai-sdk-v5-crash-course/blob/main/exercises/03-agents/03.01-tool-calling/problem/readme.md Imports essential functions and types from the 'ai' SDK and Zod for defining tools and schemas. It also imports custom file system functionalities. ```typescript import { tool, stepCountIs } from 'ai'; import { z } from 'zod'; import * as fsTools from './file-system-functionality.ts'; ``` -------------------------------- ### Call Guardrail LLM with generateText (TypeScript) Source: https://github.com/ai-hero-dev/ai-sdk-v5-crash-course/blob/main/exercises/09-advanced-patterns/09.01-guardrails/problem/readme.md This snippet demonstrates how to use the `generateText` function to invoke a guardrail LLM. It passes the model messages and a system prompt to assess query safety. The result is timed for performance monitoring. ```typescript console.time('Guardrail Time'); // TODO: Use generateText to call a model, passing in the modelMessages // and the GUARDRAIL_SYSTEM prompt. // const guardrailResult = TODO; console.timeEnd('Guardrail Time'); console.log('guardrailResult', guardrailResult.text.trim()); ``` -------------------------------- ### Creating an LLM Tool with AI SDK (TypeScript) Source: https://github.com/ai-hero-dev/ai-sdk-v5-crash-course/blob/main/exercises/03-agents/03.01-tool-calling/problem/readme.md Demonstrates how to define a tool that an LLM can call using the AI SDK. It includes a description of the tool's purpose, an input schema defined with Zod for parameter validation, and an asynchronous execute function for the tool's implementation. Dependencies include the `tool` function from the AI SDK and Zod for schema definition. ```typescript // Example of creating a tool import { tool } from 'ai'; import { z } from 'zod'; // This is just an example of the tool structure const exampleTool = tool({ description: 'Description of what the tool does', inputSchema: z.object({ param1: z.string().describe('Description of parameter 1'), param2: z.number().describe('Description of parameter 2'), }), execute: async ({ param1, param2 }) => { // Implementation that uses the parameters return { result: 'some result' }; }, }); ``` -------------------------------- ### Implement Message Metadata Function (TypeScript) Source: https://github.com/ai-hero-dev/ai-sdk-v5-crash-course/blob/main/exercises/07-streaming/07.03-message-metadata/problem/readme.md Implements the `messageMetadata` function within `result.toUIMessageStreamResponse` in `api/chat.ts`. This function calculates the duration of the message stream by capturing the start time and computing the difference when a 'finish' part is encountered, returning it as metadata. ```typescript import { UIMessage } from 'ai'; import { Readable } from 'stream'; // Assuming result and other necessary imports are available // Placeholder for the actual result object and its methods const result = { toUIMessageStreamResponse: (options: { messageMetadata?: (part: any) => Partial }) => { // Mock implementation for demonstration return { stream: new Readable(), options }; } }; // Placeholder for MyUIMessage type definition type MyUIMessage = UIMessage<{ duration: number }>; // TODO: Calculate the start time of the stream const startTime = Date.now(); return result.toUIMessageStreamResponse({ // TODO: Add the messageMetadata function here // If it encounters a 'finish' part, it should return the duration // of the stream in milliseconds messageMetadata: (part) => { if (part.type === 'finish') { const endTime = Date.now(); return { duration: endTime - startTime }; } return {}; }, }); ``` -------------------------------- ### Process Stream Chunks with for-await loop (TypeScript) Source: https://github.com/ai-hero-dev/ai-sdk-v5-crash-course/blob/main/exercises/99-reference/99.03-consume-stream/explainer.1/readme.md This example demonstrates how to manually process stream chunks using a `for await...of` loop on the `textStream`. Iterating through each chunk ensures the stream is consumed, which subsequently triggers the `onFinish` callback. ```typescript import { streamText } from 'ai/streaming'; import { ollama } from '@ai-sdk/ollama'; import process from 'process'; async function main() { const streamTextResult = await streamText({ model: ollama.chat, messages: [ { role: 'user', content: 'Explain the importance of consuming streams.', } ], onFinish: async () => { console.log('Stream finished!'); }, }); // Process stream chunks using a for-await loop for await (const chunk of streamTextResult.textStream) { process.stdout.write(chunk); } console.log('\nStream processing complete.'); } main(); ``` -------------------------------- ### Initialize Langfuse Client (TypeScript) Source: https://github.com/ai-hero-dev/ai-sdk-v5-crash-course/blob/main/exercises/06-evals/06.07-langfuse-basics/problem/readme.md Instantiate the Langfuse client with essential configuration details including environment, public key, secret key, and base URL. This client is used to interact with the LangFuse service for tracing and observability. ```typescript import { Langfuse } from 'langfuse'; export const langfuse = new Langfuse({ environment: process.env.NODE_ENV, publicKey: process.env.LANGFUSE_PUBLIC_KEY, secretKey: process.env.LANGFUSE_SECRET_KEY, baseUrl: process.env.LANGFUSE_BASE_URL, }); ``` -------------------------------- ### Consume Stream with top-level consumeStream() and toUIMessageStream() (TypeScript) Source: https://github.com/ai-hero-dev/ai-sdk-v5-crash-course/blob/main/exercises/99-reference/99.03-consume-stream/explainer.1/readme.md This example utilizes the top-level `consumeStream()` function in conjunction with `toUIMessageStream()` to process the stream output. This approach also ensures that the stream is fully consumed, thereby triggering the `onFinish` callback. ```typescript import { streamText, toUIMessageStream, consumeStream } from 'ai/streaming'; import { ollama } from '@ai-sdk/ollama'; async function main() { const streamTextResult = await streamText({ model: ollama.chat, messages: [ { role: 'user', content: 'Explain the importance of consuming streams.', } ], onFinish: async () => { console.log('Stream finished!'); }, }); // Consume the stream using the top-level function await consumeStream(toUIMessageStream(streamTextResult.textStream)); console.log('Stream consumption complete.'); } main(); ``` -------------------------------- ### Implement writeFile Tool JSX Display Source: https://github.com/ai-hero-dev/ai-sdk-v5-crash-course/blob/main/exercises/03-agents/03.03-showing-tools-in-the-frontend/problem/readme.md Create the JSX structure to visually represent the `writeFile` tool interaction in the UI. This includes displaying an icon, title, file path, and content length, following established styling patterns. ```jsx
📝 Wrote to file
Path: {part.payload.path}
Content Length: {part.payload.content.length}
``` -------------------------------- ### Example Request Payload with Chat ID Source: https://github.com/ai-hero-dev/ai-sdk-v5-crash-course/blob/main/exercises/04-persistence/04.02-pass-chat-id-to-the-api/problem/readme.md Demonstrates the structure of a request payload sent by the AI SDK, which includes a generated `id` for the chat. This automatic ID generation can be problematic if custom control over chat IDs is required. ```typescript { messages: [...], id: "some-uuid-here" } ``` -------------------------------- ### AI Agents with Tool Calling and Zod Validation (TypeScript) Source: https://context7.com/ai-hero-dev/ai-sdk-v5-crash-course/llms.txt Demonstrates creating AI agents capable of using external tools. It employs Zod for schema validation of tool inputs and automatically executes these tools. Dependencies include '@ai-sdk/google', 'ai', and 'zod'. It takes a list of UI messages as input and returns a UI message stream response. ```typescript import { convertToModelMessages, stepCountIs, streamText, tool, type UIMessage, } from 'ai'; import { google } from '@ai-sdk/google'; import { z } from 'zod'; import * as fsTools from './file-system-functionality.ts'; export const POST = async (req: Request): Promise => { const body: { messages: UIMessage[] } = await req.json(); const { messages } = body; const result = streamText({ model: google('gemini-2.5-flash'), messages: convertToModelMessages(messages), system: `You are a helpful assistant that can use a sandboxed file system.`, tools: { writeFile: tool({ description: 'Write to a file', inputSchema: z.object({ path: z.string().describe('The path to the file to create'), content: z.string().describe('The content of the file to create'), }), execute: async ({ path, content }) => { return fsTools.writeFile(path, content); }, }), readFile: tool({ description: 'Read a file', inputSchema: z.object({ path: z.string().describe('The path to the file to read'), }), execute: async ({ path }) => { return fsTools.readFile(path); }, }), listDirectory: tool({ description: 'List a directory', inputSchema: z.object({ path: z.string().describe('The path to the directory to list'), }), execute: async ({ path }) => { return fsTools.listDirectory(path); }, }), }, stopWhen: [stepCountIs(10)], // Limit agent steps }); return result.toUIMessageStreamResponse(); }; ``` -------------------------------- ### Example Discriminated Union Type in TypeScript Source: https://github.com/ai-hero-dev/ai-sdk-v5-crash-course/blob/main/exercises/05-context-engineering/05.05-chain-of-thought/problem/output.md Illustrates the resulting discriminated union type generated by the IIMT pattern. Each union member represents a specific event with a 'type' discriminator and associated data. ```typescript type EventAsDiscriminatedUnion = | { type: "login"; username: string; password: string; } | { type: "logout"; } | { type: "updateUsername"; newUsername: string; } ``` -------------------------------- ### Initial TypeScript Prompt for Code Explanation Source: https://github.com/ai-hero-dev/ai-sdk-v5-crash-course/blob/main/exercises/05-context-engineering/05.05-chain-of-thought/problem/readme.md This TypeScript code snippet sets up a prompt for an LLM to explain complex TypeScript code. It includes task context, background data (the code to explain and a reference article), and rules for the LLM's response. The goal is to explain the provided code using the article as a reference. ```typescript const result = streamText({ model: google('gemini-2.5-flash-lite'), prompt: ` You are a helpful TypeScript expert that can explain complex TypeScript code for beginner TypeScript developers. You will be given a complex TypeScript code and you will need to explain it in a way that is easy to understand. Here is the complex TypeScript code: ${COMPLEX_TS_CODE} And here is an article about the IIMT pattern:
${IIMT_ARTICLE}
- Do not let the user know that you are using the article as a reference. Refer to the concepts as if you are an expert. - Use section headers to organize the explanation. Explain the code, using the article as a reference. `, }); ``` -------------------------------- ### Implement Model Router with generateText Source: https://github.com/ai-hero-dev/ai-sdk-v5-crash-course/blob/main/exercises/09-advanced-patterns/09.02-model-router/problem/readme.md This snippet demonstrates how to use the 'generateText' function to call a model for routing decisions. It involves defining model messages and a system prompt to guide the model's choice between basic (0) and advanced (1) models. ```typescript console.time('Model Calculation Time'); // TODO: Use generateText to call a model, passing in the modelMessages // and writing your own system prompt. const modelRouterResult = TODO; console.timeEnd('Model Calculation Time'); ``` -------------------------------- ### Stream Final Summary to User Source: https://github.com/ai-hero-dev/ai-sdk-v5-crash-course/blob/main/exercises/09-advanced-patterns/09.04-research-workflow/problem/readme.md Generates a final summary of the search results and streams it to the user via the `UIMessageStreamWriter`. The summary should include markdown links to the sources of information. It uses `streamText` for generation and merges the result to avoid duplicate start messages. ```typescript const streamFinalSummary = async ( searchResults: Awaited< ReturnType >, messages: ModelMessage[], writer: UIMessageStreamWriter, ) => { // TODO: Use streamText to generate a final response to the user. // The response should be a summary of the search results, // and the sources of the information. const answerResult = TODO; writer.merge( // NOTE: We send sendStart: false because we've already // sent the 'start' message part to the frontend. // Without this, we'd end up with two assistant messages // in the frontend. answerResult.toUIMessageStream({ sendStart: false }), ); }; ``` -------------------------------- ### AI SDK Stream Handling: Top-Level consumeStream Function Source: https://github.com/ai-hero-dev/ai-sdk-v5-crash-course/blob/main/exercises/99-reference/99.03-consume-stream/explainer.1/readme.md Demonstrates using the top-level consumeStream function to read a readable stream until completion. This example converts the streamTextResult to a UI message stream before consuming it, achieving the same outcome as the object method. ```typescript import { google } from '@ai-sdk/google'; import { consumeStream, streamText } from 'ai'; console.log('Process starting...'); const streamTextResult = streamText({ model: google('gemini-2.5-flash'), prompt: 'Hello, world!', onFinish: () => { console.log('Stream finished!'); }, }); // Using the top-level consumeStream function await consumeStream({ stream: streamTextResult.toUIMessageStream(), }); console.log('Process exiting...'); ``` -------------------------------- ### Stream Suggestions using partialObjectStream (TypeScript) Source: https://github.com/ai-hero-dev/ai-sdk-v5-crash-course/blob/main/exercises/07-streaming/07.02-custom-data-parts-with-stream-object/problem/readme.md Updates the streaming logic to iterate over `partialObjectStream` instead of `textStream` to handle the array of suggestions as they become available. ```typescript // TODO: Update this to iterate over the partialObjectStream for await (const chunk of followupSuggestionsResult.textStream) { fullSuggestion += chunk; // TODO: Update this to write the data part // with the suggestions array. You might need // to filter out undefined suggestions. writer.write({ id: dataPartId, type: 'data-suggestion', data: fullSuggestion, }); } ``` -------------------------------- ### Define Custom Message Metadata Type (TypeScript) Source: https://github.com/ai-hero-dev/ai-sdk-v5-crash-course/blob/main/exercises/99-reference/99.07-message-metadata/explainer/readme.md Defines a TypeScript type for custom message metadata, specifying the structure of additional information to be attached to messages. This example defines a 'length' property to track the message's character count. ```typescript type MyMetadata = { // The length of the message generated length: number; }; type MyMessage = UIMessage; ``` -------------------------------- ### Stream Text with Tool Integration (TypeScript) Source: https://github.com/ai-hero-dev/ai-sdk-v5-crash-course/blob/main/exercises/99-reference/99.02-defining-tools/explainer/readme.md Initiates a text streaming call using `streamText`, providing the model, prompt, and a `tools` object. The `tools` object contains definitions for available tools, like `logToConsole`. The result stream is then iterated to display output chunks. ```typescript const result = streamText({ model: google('gemini-2.5-flash'), prompt: 'Log the message "Hello, world!" to the console', tools: { // ...explained below }, }); for await (const chunk of result.toUIMessageStream()) { console.log(chunk); } ```