diff --git a/app/(chat)/actions.ts b/app/(chat)/actions.ts index 5752924..71e0c9b 100644 --- a/app/(chat)/actions.ts +++ b/app/(chat)/actions.ts @@ -1,6 +1,6 @@ "use server" -import { generateText, Message } from "ai" +import { generateText, UIMessage } from "ai" import { cookies } from "next/headers" import { @@ -19,7 +19,7 @@ export async function saveChatModelAsCookie(model: string) { export async function generateTitleFromUserMessage({ message, }: { - message: Message + message: UIMessage }) { const { text: title } = await generateText({ model: myProvider.languageModel("title-model"), diff --git a/app/(chat)/api/chat/route.ts b/app/(chat)/api/chat/route.ts index a82b9ae..8bcbc42 100644 --- a/app/(chat)/api/chat/route.ts +++ b/app/(chat)/api/chat/route.ts @@ -1,9 +1,4 @@ -import { - type Message, - createDataStreamResponse, - smoothStream, - streamText, -} from "ai" +import { type UIMessage, smoothStream, streamText, stepCountIs } from "ai" import { getSession } from "@/app/(auth)/server-auth" import { myProvider } from "@/lib/ai/models" @@ -34,7 +29,7 @@ export async function POST(request: Request) { // Parse request body let requestData: { id: string - messages: Array + messages: Array selectedChatModel: string } try { @@ -79,10 +74,18 @@ export async function POST(request: Request) { return new Response("Failed to create or retrieve chat", { status: 500 }) } - // Save user message + // Save user message to database (convert UIMessage to DB format) try { await saveMessages({ - messages: [{ ...userMessage, createdAt: new Date(), chatId: id }], + messages: [ + { + id: userMessage.id, + role: userMessage.role as any, + content: userMessage.parts, + createdAt: new Date(), + chatId: id, + }, + ], }) } catch (error) { console.error("Failed to save user message:", error) @@ -91,85 +94,82 @@ export async function POST(request: Request) { console.log("selectedChatModel", selectedChatModel) - return createDataStreamResponse({ - execute: (dataStream) => { - try { - const result = streamText({ - model: myProvider.languageModel(selectedChatModel), - system: systemPrompt({ - selectedChatModel, - textAttachmentStrings: getTextAttachmentStrings(messages), - }), - messages: removeTextAttachments(messages), - maxSteps: 5, - temperature: 1, // GPT-5 only supports temperature value of 1 - experimental_activeTools: selectedChatModel.includes("reasoning") - ? [] - : ["createDocument", "updateDocument", "requestSuggestions"], - experimental_transform: smoothStream({ chunking: "word" }), - experimental_generateMessageId: generateUUID, - tools: { - createDocument: createDocument({ - session, - dataStream, - selectedModelId: selectedChatModel, - }), - updateDocument: updateDocument({ - session, - dataStream, - selectedModelId: selectedChatModel, - }), - requestSuggestions: requestSuggestions({ - session, - dataStream, - }), - }, - onFinish: async ({ response, reasoning }) => { - if (session.user?.id) { - try { - const sanitizedResponseMessages = sanitizeResponseMessages({ - messages: response.messages, - reasoning, - }) + // Convert UIMessages to ModelMessage format for the AI SDK + const messagesForAI = messages.map((msg) => ({ + role: msg.role as "user" | "assistant" | "system", + content: msg.parts as any, + })) + try { + const result = streamText({ + model: myProvider.languageModel(selectedChatModel), + + system: systemPrompt({ + selectedChatModel, + textAttachmentStrings: getTextAttachmentStrings(messages), + }), + + messages: messagesForAI, + + // GPT-5 only supports temperature value of 1 + temperature: 1, + + // Note: Tools integration needs update for v5 dataStream API + // For now, disabling tools to get basic streaming working + // tools: { + // createDocument: createDocument({ + // session, + // selectedModelId: selectedChatModel, + // }), + // updateDocument: updateDocument({ + // session, + // selectedModelId: selectedChatModel, + // }), + // requestSuggestions: requestSuggestions({ + // session, + // }), + // }, + + onFinish: async ({ response }) => { + if (session.user?.id) { + try { + // In v5, save the assistant response messages + for (const message of response.messages) { + if (message.role === "assistant") { await saveMessages({ - messages: sanitizedResponseMessages.map((message) => { - return { - id: message.id, + messages: [ + { + id: generateUUID(), chatId: id, - role: message.role, - content: message.content, + role: "assistant", + content: message.content as any, createdAt: new Date(), - } - }), + }, + ], }) - } catch (error) { - console.error("Failed to save assistant messages:", error) } } - }, - experimental_telemetry: { - isEnabled: true, - functionId: "stream-text", - }, - }) - - result.mergeIntoDataStream(dataStream, { - sendReasoning: true, - }) - } catch (error) { - console.error("Error in streamText execution:", error) - dataStream.writeData({ - type: "error", - content: `Error: ${error instanceof Error ? error.message : String(error)}`, - }) - } - }, - onError: (error) => { - console.error("DataStream error:", error) - return `Error: ${error instanceof Error ? error.message : "An unknown error occurred"}` - }, - }) + } catch (error) { + console.error("Failed to save assistant messages:", error) + } + } + }, + + experimental_telemetry: { + isEnabled: true, + functionId: "stream-text", + }, + }) + + // In v5, convert the stream result to a proper response + return result.toTextStreamResponse() + } catch (error) { + console.error("Error in streamText execution:", error) + return new Response( + `Error: ${error instanceof Error ? error.message : String(error)}`, + { status: 500 }, + ) + } } catch (error) { console.error("Unexpected error in POST handler:", error) return new Response( diff --git a/app/(chat)/api/files/upload/route.ts b/app/(chat)/api/files/upload/route.ts index a698f1b..c22ae0d 100644 --- a/app/(chat)/api/files/upload/route.ts +++ b/app/(chat)/api/files/upload/route.ts @@ -1,6 +1,6 @@ import { put } from "@vercel/blob" import { NextResponse } from "next/server" -import { z } from "zod" +import { z } from "zod/v3" import { getSession } from "@/app/(auth)/server-auth" diff --git a/blocks/code/server.ts b/blocks/code/server.ts index 98dda24..f3dfe09 100644 --- a/blocks/code/server.ts +++ b/blocks/code/server.ts @@ -1,4 +1,4 @@ -import { z } from "zod" +import { z } from "zod/v3" import { streamObject } from "ai" import { myProvider } from "@/lib/ai/models" import { codePrompt, updateDocumentPrompt } from "@/lib/ai/prompts/prompts" @@ -27,9 +27,15 @@ export const codeDocumentHandler = createDocumentHandler<"code">({ const { code } = object if (code) { - dataStream.writeData({ - type: "code-delta", - content: code ?? "", + dataStream.write({ + type: "data", + + value: [ + { + type: "code-delta", + content: code ?? "", + }, + ], }) draftContent = code @@ -65,9 +71,15 @@ export const codeDocumentHandler = createDocumentHandler<"code">({ const { code } = object if (code) { - dataStream.writeData({ - type: "code-delta", - content: code ?? "", + dataStream.write({ + type: "data", + + value: [ + { + type: "code-delta", + content: code ?? "", + }, + ], }) draftContent = code diff --git a/blocks/image/server.ts b/blocks/image/server.ts index 3df2a22..5d4565c 100644 --- a/blocks/image/server.ts +++ b/blocks/image/server.ts @@ -15,9 +15,15 @@ export const imageDocumentHandler = createDocumentHandler<"image">({ draftContent = image.base64 - dataStream.writeData({ - type: "image-delta", - content: image.base64, + dataStream.write({ + type: "data", + + value: [ + { + type: "image-delta", + content: image.base64, + }, + ], }) return draftContent @@ -33,9 +39,15 @@ export const imageDocumentHandler = createDocumentHandler<"image">({ draftContent = image.base64 - dataStream.writeData({ - type: "image-delta", - content: image.base64, + dataStream.write({ + type: "data", + + value: [ + { + type: "image-delta", + content: image.base64, + }, + ], }) return draftContent diff --git a/blocks/sheet/server.ts b/blocks/sheet/server.ts index 73f50e9..afa81c0 100644 --- a/blocks/sheet/server.ts +++ b/blocks/sheet/server.ts @@ -2,7 +2,7 @@ import { myProvider } from "@/lib/ai/models" import { updateDocumentPrompt } from "@/lib/ai/prompts/prompts" import { createDocumentHandler } from "@/lib/blocks/server" import { streamObject } from "ai" -import { z } from "zod" +import { z } from "zod/v3" /** * @deprecated @@ -29,9 +29,15 @@ export const sheetDocumentHandler = createDocumentHandler<"sheet">({ const { csv } = object if (csv) { - dataStream.writeData({ - type: "sheet-delta", - content: csv, + dataStream.write({ + type: "data", + + value: [ + { + type: "sheet-delta", + content: csv, + }, + ], }) draftContent = csv @@ -39,9 +45,15 @@ export const sheetDocumentHandler = createDocumentHandler<"sheet">({ } } - dataStream.writeData({ - type: "sheet-delta", - content: draftContent, + dataStream.write({ + type: "data", + + value: [ + { + type: "sheet-delta", + content: draftContent, + }, + ], }) return draftContent @@ -66,9 +78,15 @@ export const sheetDocumentHandler = createDocumentHandler<"sheet">({ const { csv } = object if (csv) { - dataStream.writeData({ - type: "sheet-delta", - content: csv, + dataStream.write({ + type: "data", + + value: [ + { + type: "sheet-delta", + content: csv, + }, + ], }) draftContent = csv diff --git a/blocks/text/server.ts b/blocks/text/server.ts index 4539c11..8deb21d 100644 --- a/blocks/text/server.ts +++ b/blocks/text/server.ts @@ -21,13 +21,19 @@ export const textDocumentHandler = createDocumentHandler<"text">({ const { type } = delta if (type === "text-delta") { - const { textDelta } = delta + const { text: textDelta } = delta draftContent += textDelta - dataStream.writeData({ - type: "text-delta", - content: textDelta, + dataStream.write({ + type: "data", + + value: [ + { + type: "text-delta", + content: textDelta, + }, + ], }) } } @@ -48,7 +54,7 @@ export const textDocumentHandler = createDocumentHandler<"text">({ experimental_transform: smoothStream({ chunking: "word" }), prompt: description, temperature: 1, // GPT-5 only supports temperature value of 1 - experimental_providerMetadata: { + providerOptions: { openai: { prediction: { type: "content", @@ -62,12 +68,18 @@ export const textDocumentHandler = createDocumentHandler<"text">({ const { type } = delta if (type === "text-delta") { - const { textDelta } = delta + const { text: textDelta } = delta draftContent += textDelta - dataStream.writeData({ - type: "text-delta", - content: textDelta, + dataStream.write({ + type: "data", + + value: [ + { + type: "text-delta", + content: textDelta, + }, + ], }) } } diff --git a/bun.lock b/bun.lock index 5c5d3a7..3770901 100644 --- a/bun.lock +++ b/bun.lock @@ -4,9 +4,9 @@ "": { "name": "ai-chatbot", "dependencies": { - "@ai-sdk/anthropic": "^1.1.8", + "@ai-sdk/anthropic": "^2.0.0", "@ai-sdk/fireworks": "^0.1.8", - "@ai-sdk/openai": "^1.1.12", + "@ai-sdk/openai": "^2.0.0", "@codemirror/lang-javascript": "^6.2.4", "@codemirror/state": "^6.5.0", "@codemirror/theme-one-dark": "^6.1.2", @@ -24,7 +24,8 @@ "@vercel/analytics": "^1.3.1", "@vercel/blob": "^0.24.1", "@vercel/postgres": "^0.10.0", - "ai": "4.1.17", + "ai": "^5.0.0", + "ai-legacy": "npm:ai@^4.3.2", "bcrypt-ts": "^5.0.2", "class-variance-authority": "^0.7.0", "classnames": "^2.5.1", @@ -66,7 +67,7 @@ "tailwind-merge": "^2.5.2", "tailwindcss-animate": "^1.0.7", "usehooks-ts": "^3.1.0", - "zod": "^3.23.8", + "zod": "^4.1.8", "zustand": "^5.0.3", }, "devDependencies": { @@ -96,21 +97,23 @@ }, }, "packages": { - "@ai-sdk/anthropic": ["@ai-sdk/anthropic@1.1.8", "", { "dependencies": { "@ai-sdk/provider": "1.0.7", "@ai-sdk/provider-utils": "2.1.8" }, "peerDependencies": { "zod": "^3.0.0" } }, "sha512-QD8c3ShPIpXaqjCDq9tCBI9iI/GeZETnP/DxgA8wRTs13Sqx6HcLh1eKTaGyePOtJvRx5XBcR+wSdeZZcaeUQQ=="], + "@ai-sdk/anthropic": ["@ai-sdk/anthropic@2.0.40", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.15" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-aHtqlIIS8lyesSNFxevLGWozCCZ1xQ4Wy2HfQSVVQu3dWtndfPzF6hbWof2Way+qNrvRtjnBpRQyaVeu6Js4pQ=="], "@ai-sdk/fireworks": ["@ai-sdk/fireworks@0.1.10", "", { "dependencies": { "@ai-sdk/openai-compatible": "0.1.10", "@ai-sdk/provider": "1.0.7", "@ai-sdk/provider-utils": "2.1.8" }, "peerDependencies": { "zod": "^3.0.0" } }, "sha512-IbpvL5Bep8r8d14esKQwpd/ZtmsXRLPip3MGzVIo2yGo9cUmmjanlOZlatEXipSIYxliztNfuLVcvhtfWljpGw=="], - "@ai-sdk/openai": ["@ai-sdk/openai@1.1.12", "", { "dependencies": { "@ai-sdk/provider": "1.0.7", "@ai-sdk/provider-utils": "2.1.8" }, "peerDependencies": { "zod": "^3.0.0" } }, "sha512-lh3zN4J/XEqkjpZAOBajSttF1Nl2qV/7WxRbORn+4jZmQkmzWQbsEUpgQ6ME+heyRvnsRCNq3fkMGehWWxWuXg=="], + "@ai-sdk/gateway": ["@ai-sdk/gateway@2.0.5", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.15", "@vercel/oidc": "3.0.3" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-5TTDSl0USWY6YGnb4QmJGplFZhk+p9OT7hZevAaER6OGiZ17LB1GypsGYDpNo/MiVMklk8kX4gk6p1/R/EiJ8Q=="], + + "@ai-sdk/openai": ["@ai-sdk/openai@2.0.60", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.15" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-h7Bg3nY4UYyBj2HDmsFzPxuBhK4ZGGkC2ssZGXOov+81DVSiRJXR4NFfsxbWW/7c6uMCP/YmZ8MiWtvsKMSCHg=="], "@ai-sdk/openai-compatible": ["@ai-sdk/openai-compatible@0.1.10", "", { "dependencies": { "@ai-sdk/provider": "1.0.7", "@ai-sdk/provider-utils": "2.1.8" }, "peerDependencies": { "zod": "^3.0.0" } }, "sha512-AR5Acly7U64wDgVdVs9AqaMHgVps/35TMUMDS4akSZjsB4TdAhKnWdMXmACzCxmbCySHMoMnKY41XgvCyFN1pQ=="], - "@ai-sdk/provider": ["@ai-sdk/provider@1.0.7", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-q1PJEZ0qD9rVR+8JFEd01/QM++csMT5UVwYXSN2u54BrVw/D8TZLTeg2FEfKK00DgAx0UtWd8XOhhwITP9BT5g=="], + "@ai-sdk/provider": ["@ai-sdk/provider@2.0.0", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-6o7Y2SeO9vFKB8lArHXehNuusnpddKPk7xqL7T2/b+OvXMRIXUO1rR4wcv1hAFUAT9avGZshty3Wlua/XA7TvA=="], - "@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@2.1.8", "", { "dependencies": { "@ai-sdk/provider": "1.0.7", "eventsource-parser": "^3.0.0", "nanoid": "^3.3.8", "secure-json-parse": "^2.7.0" }, "peerDependencies": { "zod": "^3.0.0" }, "optionalPeers": ["zod"] }, "sha512-1j9niMUAFlCBdYRYJr1yoB5kwZcRFBVuBiL1hhrf0ONFNrDiJYA6F+gROOuP16NHhezMfTo60+GeeV1xprHFjg=="], + "@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.15", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-kOc6Pxb7CsRlNt+sLZKL7/VGQUd7ccl3/tIK+Bqf5/QhHR0Qm3qRBMz1IwU1RmjJEZA73x+KB5cUckbDl2WF7Q=="], - "@ai-sdk/react": ["@ai-sdk/react@1.1.8", "", { "dependencies": { "@ai-sdk/provider-utils": "2.1.6", "@ai-sdk/ui-utils": "1.1.8", "swr": "^2.2.5", "throttleit": "2.1.0" }, "peerDependencies": { "react": "^18 || ^19 || ^19.0.0-rc", "zod": "^3.0.0" }, "optionalPeers": ["react", "zod"] }, "sha512-buHm7hP21xEOksnRQtJX9fKbi7cAUwanEBa5niddTDibCDKd+kIXP2vaJGy8+heB3rff+XSW3BWlA8pscK+n1g=="], + "@ai-sdk/react": ["@ai-sdk/react@1.2.12", "", { "dependencies": { "@ai-sdk/provider-utils": "2.2.8", "@ai-sdk/ui-utils": "1.2.11", "swr": "^2.2.5", "throttleit": "2.1.0" }, "peerDependencies": { "react": "^18 || ^19 || ^19.0.0-rc", "zod": "^3.23.8" }, "optionalPeers": ["zod"] }, "sha512-jK1IZZ22evPZoQW3vlkZ7wvjYGYF+tRBKXtrcolduIkQ/m/sOAVcVeVDUDvh1T91xCnWCdUGCPZg2avZ90mv3g=="], - "@ai-sdk/ui-utils": ["@ai-sdk/ui-utils@1.1.8", "", { "dependencies": { "@ai-sdk/provider": "1.0.7", "@ai-sdk/provider-utils": "2.1.6", "zod-to-json-schema": "^3.24.1" }, "peerDependencies": { "zod": "^3.0.0" }, "optionalPeers": ["zod"] }, "sha512-nbok53K1EalO2sZjBLFB33cqs+8SxiL6pe7ekZ7+5f2MJTwdvpShl6d9U4O8fO3DnZ9pYLzaVC0XNMxnJt030Q=="], + "@ai-sdk/ui-utils": ["@ai-sdk/ui-utils@1.2.11", "", { "dependencies": { "@ai-sdk/provider": "1.1.3", "@ai-sdk/provider-utils": "2.2.8", "zod-to-json-schema": "^3.24.1" }, "peerDependencies": { "zod": "^3.23.8" } }, "sha512-3zcwCc8ezzFlwp3ZD15wAPjf2Au4s3vAbKsXQVyhxODHcmu0iyPO2Eua6D/vicq/AUm/BAo60r97O6HU+EI0+w=="], "@alloc/quick-lru": ["@alloc/quick-lru@5.2.0", "", {}, "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw=="], @@ -500,6 +503,8 @@ "@sindresorhus/merge-streams": ["@sindresorhus/merge-streams@2.3.0", "", {}, "sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg=="], + "@standard-schema/spec": ["@standard-schema/spec@1.0.0", "", {}, "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA=="], + "@swc/helpers": ["@swc/helpers@0.5.15", "", { "dependencies": { "tslib": "^2.8.0" } }, "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g=="], "@tailwindcss/typography": ["@tailwindcss/typography@0.5.16", "", { "dependencies": { "lodash.castarray": "^4.4.0", "lodash.isplainobject": "^4.0.6", "lodash.merge": "^4.6.2", "postcss-selector-parser": "6.0.10" }, "peerDependencies": { "tailwindcss": ">=3.0.0 || insiders || >=4.0.0-alpha.20 || >=4.0.0-beta.1" } }, "sha512-0wDLwCVF5V3x3b1SGXPCDcdsbDHMBe+lkFzBRaHeLvNi+nrrnZ1lA18u+OTWO8iSWU2GxUOCvlXtDuqftc1oiA=="], @@ -632,13 +637,17 @@ "@vercel/blob": ["@vercel/blob@0.24.1", "", { "dependencies": { "async-retry": "^1.3.3", "bytes": "^3.1.2", "is-buffer": "^2.0.5", "undici": "^5.28.4" } }, "sha512-wHzgKzvAuF4tRDoXk3wGBYzQZ9z2fLr4oftiR1hOclPEdA1aj2/0mizvO2l5w91eZlTAaFth0S1DlqrrXqpntg=="], + "@vercel/oidc": ["@vercel/oidc@3.0.3", "", {}, "sha512-yNEQvPcVrK9sIe637+I0jD6leluPxzwJKx/Haw6F4H77CdDsszUn5V3o96LPziXkSNE2B83+Z3mjqGKBK/R6Gg=="], + "@vercel/postgres": ["@vercel/postgres@0.10.0", "", { "dependencies": { "@neondatabase/serverless": "^0.9.3", "bufferutil": "^4.0.8", "ws": "^8.17.1" } }, "sha512-fSD23DxGND40IzSkXjcFcxr53t3Tiym59Is0jSYIFpG4/0f0KO9SGtcp1sXiebvPaGe7N/tU05cH4yt2S6/IPg=="], "acorn": ["acorn@8.14.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA=="], "acorn-jsx": ["acorn-jsx@5.3.2", "", { "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ=="], - "ai": ["ai@4.1.17", "", { "dependencies": { "@ai-sdk/provider": "1.0.7", "@ai-sdk/provider-utils": "2.1.6", "@ai-sdk/react": "1.1.8", "@ai-sdk/ui-utils": "1.1.8", "@opentelemetry/api": "1.9.0", "jsondiffpatch": "0.6.0" }, "peerDependencies": { "react": "^18 || ^19 || ^19.0.0-rc", "zod": "^3.0.0" }, "optionalPeers": ["react", "zod"] }, "sha512-5SW15tXDuxE/wlEOjRKxLxTOUIGD4C9bIee+FCFvXHTTAZhHiQjViC2s7RtMUW+hbFtGya302jUHY1Pe8A/YuQ=="], + "ai": ["ai@5.0.86", "", { "dependencies": { "@ai-sdk/gateway": "2.0.5", "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.15", "@opentelemetry/api": "1.9.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-ooHwNTkLdedFf98iQhtSc5btc/P4UuXuOpYneoifq0190vqosLunNdW8Hs6CiE0Am7YOGNplDK56JIPlHZIL4w=="], + + "ai-legacy": ["ai@4.3.19", "", { "dependencies": { "@ai-sdk/provider": "1.1.3", "@ai-sdk/provider-utils": "2.2.8", "@ai-sdk/react": "1.2.12", "@ai-sdk/ui-utils": "1.2.11", "@opentelemetry/api": "1.9.0", "jsondiffpatch": "0.6.0" }, "peerDependencies": { "react": "^18 || ^19 || ^19.0.0-rc", "zod": "^3.23.8" }, "optionalPeers": ["react"] }, "sha512-dIE2bfNpqHN3r6IINp9znguYdhIOheKW2LDigAMrgt/upT3B8eBGPSCblENvaZGoq+hxaN9fSMzjWpbqloP+7Q=="], "ajv": ["ajv@6.12.6", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g=="], @@ -986,7 +995,7 @@ "esutils": ["esutils@2.0.3", "", {}, "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g=="], - "eventsource-parser": ["eventsource-parser@3.0.0", "", {}, "sha512-T1C0XCUimhxVQzW4zFipdx0SficT651NnkR0ZSH3yQwh+mFMdLfgjABVi4YtMTtaL4s168593DaoaRLMqryavA=="], + "eventsource-parser": ["eventsource-parser@3.0.6", "", {}, "sha512-Vo1ab+QXPzZ4tCa8SwIHJFaSzy4R6SHf7BY79rFBDf0idraZWAkYrDjDj8uWaSm3S2TK+hJ7/t1CEmZ7jXw+pg=="], "execa": ["execa@8.0.1", "", { "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^8.0.1", "human-signals": "^5.0.0", "is-stream": "^3.0.0", "merge-stream": "^2.0.0", "npm-run-path": "^5.1.0", "onetime": "^6.0.0", "signal-exit": "^4.1.0", "strip-final-newline": "^3.0.0" } }, "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg=="], @@ -1958,7 +1967,7 @@ "yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="], - "zod": ["zod@3.24.2", "", {}, "sha512-lY7CDW43ECgW9u1TcT3IoXHflywfVqDYze4waEz812jR/bZ8FHDsl7pFQoSZTz5N+2NqRXs8GBwnAwo3ZNxqhQ=="], + "zod": ["zod@4.1.12", "", {}, "sha512-JInaHOamG8pt5+Ey8kGmdcAcg3OL9reK8ltczgHTAwNhMys/6ThXHityHxVV2p3fkw/c+MAvBHFVYHFZDmjMCQ=="], "zod-to-json-schema": ["zod-to-json-schema@3.24.1", "", { "peerDependencies": { "zod": "^3.24.1" } }, "sha512-3h08nf3Vw3Wl3PK+q3ow/lIil81IT2Oa7YpQyUUDsEWbXveMesdfK1xBd2RhCkynwZndAxixji/7SYJJowr62w=="], @@ -1966,11 +1975,19 @@ "zwitch": ["zwitch@2.0.4", "", {}, "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A=="], - "@ai-sdk/provider-utils/nanoid": ["nanoid@3.3.8", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w=="], + "@ai-sdk/fireworks/@ai-sdk/provider": ["@ai-sdk/provider@1.0.7", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-q1PJEZ0qD9rVR+8JFEd01/QM++csMT5UVwYXSN2u54BrVw/D8TZLTeg2FEfKK00DgAx0UtWd8XOhhwITP9BT5g=="], + + "@ai-sdk/fireworks/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@2.1.8", "", { "dependencies": { "@ai-sdk/provider": "1.0.7", "eventsource-parser": "^3.0.0", "nanoid": "^3.3.8", "secure-json-parse": "^2.7.0" }, "peerDependencies": { "zod": "^3.0.0" }, "optionalPeers": ["zod"] }, "sha512-1j9niMUAFlCBdYRYJr1yoB5kwZcRFBVuBiL1hhrf0ONFNrDiJYA6F+gROOuP16NHhezMfTo60+GeeV1xprHFjg=="], + + "@ai-sdk/openai-compatible/@ai-sdk/provider": ["@ai-sdk/provider@1.0.7", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-q1PJEZ0qD9rVR+8JFEd01/QM++csMT5UVwYXSN2u54BrVw/D8TZLTeg2FEfKK00DgAx0UtWd8XOhhwITP9BT5g=="], + + "@ai-sdk/openai-compatible/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@2.1.8", "", { "dependencies": { "@ai-sdk/provider": "1.0.7", "eventsource-parser": "^3.0.0", "nanoid": "^3.3.8", "secure-json-parse": "^2.7.0" }, "peerDependencies": { "zod": "^3.0.0" }, "optionalPeers": ["zod"] }, "sha512-1j9niMUAFlCBdYRYJr1yoB5kwZcRFBVuBiL1hhrf0ONFNrDiJYA6F+gROOuP16NHhezMfTo60+GeeV1xprHFjg=="], + + "@ai-sdk/react/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@2.2.8", "", { "dependencies": { "@ai-sdk/provider": "1.1.3", "nanoid": "^3.3.8", "secure-json-parse": "^2.7.0" }, "peerDependencies": { "zod": "^3.23.8" } }, "sha512-fqhG+4sCVv8x7nFzYnFo19ryhAa3w096Kmc3hWxMQfW/TubPOmt3A6tYZhl4mUfQWWQMsuSkLrtjlWuXBVSGQA=="], - "@ai-sdk/react/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@2.1.6", "", { "dependencies": { "@ai-sdk/provider": "1.0.7", "eventsource-parser": "^3.0.0", "nanoid": "^3.3.8", "secure-json-parse": "^2.7.0" }, "peerDependencies": { "zod": "^3.0.0" }, "optionalPeers": ["zod"] }, "sha512-Pfyaj0QZS22qyVn5Iz7IXcJ8nKIKlu2MeSAdKJzTwkAks7zdLaKVB+396Rqcp1bfQnxl7vaduQVMQiXUrgK8Gw=="], + "@ai-sdk/ui-utils/@ai-sdk/provider": ["@ai-sdk/provider@1.1.3", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-qZMxYJ0qqX/RfnuIaab+zp8UAeJn/ygXXAffR5I4N0n1IrvA6qBsjc8hXLmBiMV2zoXlifkacF7sEFnYnjBcqg=="], - "@ai-sdk/ui-utils/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@2.1.6", "", { "dependencies": { "@ai-sdk/provider": "1.0.7", "eventsource-parser": "^3.0.0", "nanoid": "^3.3.8", "secure-json-parse": "^2.7.0" }, "peerDependencies": { "zod": "^3.0.0" }, "optionalPeers": ["zod"] }, "sha512-Pfyaj0QZS22qyVn5Iz7IXcJ8nKIKlu2MeSAdKJzTwkAks7zdLaKVB+396Rqcp1bfQnxl7vaduQVMQiXUrgK8Gw=="], + "@ai-sdk/ui-utils/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@2.2.8", "", { "dependencies": { "@ai-sdk/provider": "1.1.3", "nanoid": "^3.3.8", "secure-json-parse": "^2.7.0" }, "peerDependencies": { "zod": "^3.23.8" } }, "sha512-fqhG+4sCVv8x7nFzYnFo19ryhAa3w096Kmc3hWxMQfW/TubPOmt3A6tYZhl4mUfQWWQMsuSkLrtjlWuXBVSGQA=="], "@auth/core/jose": ["jose@5.9.6", "", {}, "sha512-AMlnetc9+CV9asI19zHmrgS/WYsWUwCn2R7RzlbJWD7F9eWYUTGyBmU9o6PxngtLGOiDGPRu+Uc4fhKzbpteZQ=="], @@ -1992,7 +2009,9 @@ "@typescript-eslint/typescript-estree/semver": ["semver@7.7.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA=="], - "ai/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@2.1.6", "", { "dependencies": { "@ai-sdk/provider": "1.0.7", "eventsource-parser": "^3.0.0", "nanoid": "^3.3.8", "secure-json-parse": "^2.7.0" }, "peerDependencies": { "zod": "^3.0.0" }, "optionalPeers": ["zod"] }, "sha512-Pfyaj0QZS22qyVn5Iz7IXcJ8nKIKlu2MeSAdKJzTwkAks7zdLaKVB+396Rqcp1bfQnxl7vaduQVMQiXUrgK8Gw=="], + "ai-legacy/@ai-sdk/provider": ["@ai-sdk/provider@1.1.3", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-qZMxYJ0qqX/RfnuIaab+zp8UAeJn/ygXXAffR5I4N0n1IrvA6qBsjc8hXLmBiMV2zoXlifkacF7sEFnYnjBcqg=="], + + "ai-legacy/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@2.2.8", "", { "dependencies": { "@ai-sdk/provider": "1.1.3", "nanoid": "^3.3.8", "secure-json-parse": "^2.7.0" }, "peerDependencies": { "zod": "^3.23.8" } }, "sha512-fqhG+4sCVv8x7nFzYnFo19ryhAa3w096Kmc3hWxMQfW/TubPOmt3A6tYZhl4mUfQWWQMsuSkLrtjlWuXBVSGQA=="], "anymatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], @@ -2064,6 +2083,8 @@ "repomix/minimatch": ["minimatch@10.0.1", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ=="], + "repomix/zod": ["zod@3.24.2", "", {}, "sha512-lY7CDW43ECgW9u1TcT3IoXHflywfVqDYze4waEz812jR/bZ8FHDsl7pFQoSZTz5N+2NqRXs8GBwnAwo3ZNxqhQ=="], + "restore-cursor/onetime": ["onetime@7.0.0", "", { "dependencies": { "mimic-function": "^5.0.0" } }, "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ=="], "rimraf/glob": ["glob@7.2.3", "", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q=="], @@ -2096,6 +2117,16 @@ "wrap-ansi-cjs/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], + "@ai-sdk/fireworks/@ai-sdk/provider-utils/eventsource-parser": ["eventsource-parser@3.0.0", "", {}, "sha512-T1C0XCUimhxVQzW4zFipdx0SficT651NnkR0ZSH3yQwh+mFMdLfgjABVi4YtMTtaL4s168593DaoaRLMqryavA=="], + + "@ai-sdk/fireworks/@ai-sdk/provider-utils/nanoid": ["nanoid@3.3.8", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w=="], + + "@ai-sdk/openai-compatible/@ai-sdk/provider-utils/eventsource-parser": ["eventsource-parser@3.0.0", "", {}, "sha512-T1C0XCUimhxVQzW4zFipdx0SficT651NnkR0ZSH3yQwh+mFMdLfgjABVi4YtMTtaL4s168593DaoaRLMqryavA=="], + + "@ai-sdk/openai-compatible/@ai-sdk/provider-utils/nanoid": ["nanoid@3.3.8", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w=="], + + "@ai-sdk/react/@ai-sdk/provider-utils/@ai-sdk/provider": ["@ai-sdk/provider@1.1.3", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-qZMxYJ0qqX/RfnuIaab+zp8UAeJn/ygXXAffR5I4N0n1IrvA6qBsjc8hXLmBiMV2zoXlifkacF7sEFnYnjBcqg=="], + "@ai-sdk/react/@ai-sdk/provider-utils/nanoid": ["nanoid@3.3.8", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w=="], "@ai-sdk/ui-utils/@ai-sdk/provider-utils/nanoid": ["nanoid@3.3.8", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w=="], @@ -2152,7 +2183,7 @@ "@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@2.0.1", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA=="], - "ai/@ai-sdk/provider-utils/nanoid": ["nanoid@3.3.8", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w=="], + "ai-legacy/@ai-sdk/provider-utils/nanoid": ["nanoid@3.3.8", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w=="], "cytoscape-fcose/cose-base/layout-base": ["layout-base@2.0.1", "", {}, "sha512-dp3s92+uNI1hWIpPGH3jK2kxE2lMjdXdr+DH8ynZHpd6PUlH6x6cbuXnoMmiNumznqaNO31xu9e79F0uuZ0JFg=="], diff --git a/components/block-messages.tsx b/components/block-messages.tsx index cee838d..508cd5b 100644 --- a/components/block-messages.tsx +++ b/components/block-messages.tsx @@ -1,7 +1,7 @@ import { PreviewMessage } from "./message" import { useScrollToBottom } from "./use-scroll-to-bottom" import { Vote } from "@/lib/db/schema" -import { ChatRequestOptions, Message } from "ai" +import { ChatRequestOptions, UIMessage } from "ai" import { memo } from "react" import equal from "fast-deep-equal" import { UIBlock } from "./block" @@ -10,9 +10,9 @@ interface BlockMessagesProps { chatId: string isLoading: boolean votes: Array | undefined - messages: Array + messages: Array setMessages: ( - messages: Message[] | ((messages: Message[]) => Message[]), + messages: UIMessage[] | ((messages: UIMessage[]) => UIMessage[]), ) => void reload: ( chatRequestOptions?: ChatRequestOptions, diff --git a/components/block.tsx b/components/block.tsx index be04140..563c03c 100644 --- a/components/block.tsx +++ b/components/block.tsx @@ -1,4 +1,9 @@ -import type { Attachment, ChatRequestOptions, CreateMessage, Message } from "ai" +import type { + Attachment, + ChatRequestOptions, + CreateUIMessage, + UIMessage, +} from "ai" import { formatDistance } from "date-fns" import { AnimatePresence, motion } from "framer-motion" import { @@ -69,11 +74,11 @@ function PureBlock({ stop: () => void attachments: Array setAttachments: Dispatch>> - messages: Array - setMessages: Dispatch>> + messages: Array + setMessages: Dispatch>> votes: Array | undefined append: ( - message: Message | CreateMessage, + message: UIMessage | CreateUIMessage, chatRequestOptions?: ChatRequestOptions, ) => Promise handleSubmit: ( diff --git a/components/chat.tsx b/components/chat.tsx index 89c603e..aef6f86 100644 --- a/components/chat.tsx +++ b/components/chat.tsx @@ -1,7 +1,7 @@ "use client" -import type { Attachment, Message } from "ai" -import { useChat } from "ai/react" +import type { UIMessage } from "ai" +import { useChat } from "@ai-sdk/react" import { useState } from "react" import useSWR, { useSWRConfig } from "swr" @@ -16,6 +16,22 @@ import type { VisibilityType } from "./visibility-selector" import { useBlockSelector } from "@/hooks/use-block" import { toast } from "sonner" +// Convert UIMessage (with parts) to Message (with content) for useChat +function uiMessagesToMessages(uiMessages: UIMessage[]): any[] { + return uiMessages.map((msg) => { + let content = "" + if (msg.parts && msg.parts.length > 0) { + const textParts = msg.parts.filter((p: any) => p.type === "text") + content = textParts.map((p: any) => p.text).join("\n") + } + return { + id: msg.id, + role: msg.role, + content, + } + }) +} + export function Chat({ id, initialMessages, @@ -24,7 +40,7 @@ export function Chat({ isReadonly, }: { id: string - initialMessages: Array + initialMessages: Array selectedChatModel: string selectedVisibilityType: VisibilityType isReadonly: boolean @@ -46,13 +62,14 @@ export function Chat({ } = useChat({ id, body: { id, selectedChatModel: modelId }, - initialMessages, + initialMessages: uiMessagesToMessages(initialMessages), experimental_throttle: 100, - sendExtraMessageFields: true, generateId: generateUUID, + onFinish: () => { mutate("/api/history") }, + onError: (error) => { toast.error("An error occured, please try again!") }, @@ -64,9 +81,11 @@ export function Chat({ fetcher, ) - const [attachments, setAttachments] = useState>([]) const isBlockVisible = useBlockSelector((state) => state.isVisible) + // Dummy attachment state for compatibility + const [attachments, setAttachments] = useState([]) + return ( <>
@@ -82,8 +101,12 @@ export function Chat({ chatId={id} isLoading={isLoading} votes={votes} - messages={messages} - setMessages={setMessages} + messages={messages as unknown as UIMessage[]} + setMessages={ + setMessages as unknown as ( + messages: UIMessage[] | ((messages: UIMessage[]) => UIMessage[]), + ) => void + } reload={reload} isReadonly={isReadonly} isBlockVisible={isBlockVisible} @@ -100,8 +123,14 @@ export function Chat({ stop={stop} attachments={attachments} setAttachments={setAttachments} - messages={messages} - setMessages={setMessages} + messages={messages as unknown as UIMessage[]} + setMessages={ + setMessages as unknown as ( + messages: + | UIMessage[] + | ((messages: UIMessage[]) => UIMessage[]), + ) => void + } append={append} /> )} @@ -118,8 +147,12 @@ export function Chat({ attachments={attachments} setAttachments={setAttachments} append={append} - messages={messages} - setMessages={setMessages} + messages={messages as unknown as UIMessage[]} + setMessages={ + setMessages as unknown as ( + messages: UIMessage[] | ((messages: UIMessage[]) => UIMessage[]), + ) => void + } reload={reload} votes={votes} isReadonly={isReadonly} diff --git a/components/create-block.tsx b/components/create-block.tsx index db0b688..39b528e 100644 --- a/components/create-block.tsx +++ b/components/create-block.tsx @@ -1,5 +1,5 @@ import { Suggestion } from "@/lib/db/schema" -import { UseChatHelpers } from "ai/react" +import { UseChatHelpers } from "@ai-sdk/react" import { ComponentType, Dispatch, ReactNode, SetStateAction } from "react" import { DataStreamDelta } from "./data-stream-handler" import { UIBlock } from "./block" diff --git a/components/data-stream-handler.tsx b/components/data-stream-handler.tsx index 5259023..e7aeec3 100644 --- a/components/data-stream-handler.tsx +++ b/components/data-stream-handler.tsx @@ -1,6 +1,6 @@ "use client" -import { useChat } from "ai/react" +import { useChat } from "@ai-sdk/react" import { useEffect, useRef } from "react" import { blockDefinitions, BlockKind } from "./block" import { Suggestion } from "@/lib/db/schema" diff --git a/components/message-actions.tsx b/components/message-actions.tsx index c96a446..a1adb06 100644 --- a/components/message-actions.tsx +++ b/components/message-actions.tsx @@ -1,4 +1,4 @@ -import type { Message } from "ai" +import type { UIMessage } from "ai" import { toast } from "sonner" import { useSWRConfig } from "swr" import { useCopyToClipboard } from "usehooks-ts" @@ -23,7 +23,7 @@ export function PureMessageActions({ isLoading, }: { chatId: string - message: Message + message: UIMessage vote: Vote | undefined isLoading: boolean }) { diff --git a/components/message-editor.tsx b/components/message-editor.tsx index 1de7703..206b6ae 100644 --- a/components/message-editor.tsx +++ b/components/message-editor.tsx @@ -1,6 +1,6 @@ "use client" -import { ChatRequestOptions, Message } from "ai" +import { ChatRequestOptions, UIMessage } from "ai" import { Button } from "./ui/button" import { Dispatch, SetStateAction, useEffect, useRef, useState } from "react" import { Textarea } from "./ui/textarea" @@ -8,10 +8,10 @@ import { deleteTrailingMessages } from "@/app/(chat)/actions" import { toast } from "sonner" export type MessageEditorProps = { - message: Message + message: UIMessage setMode: Dispatch> setMessages: ( - messages: Message[] | ((messages: Message[]) => Message[]), + messages: UIMessage[] | ((messages: UIMessage[]) => UIMessage[]), ) => void reload: ( chatRequestOptions?: ChatRequestOptions, diff --git a/components/message-reasoning.tsx b/components/message-reasoning.tsx index 5aa3231..1e7f3ea 100644 --- a/components/message-reasoning.tsx +++ b/components/message-reasoning.tsx @@ -7,12 +7,12 @@ import { Markdown } from "./markdown" interface MessageReasoningProps { isLoading: boolean - reasoning: string + reasoningText: string } export function MessageReasoning({ isLoading, - reasoning, + reasoningText, }: MessageReasoningProps) { const [isExpanded, setIsExpanded] = useState(true) diff --git a/components/message.tsx b/components/message.tsx index da48dae..17d091e 100644 --- a/components/message.tsx +++ b/components/message.tsx @@ -1,6 +1,6 @@ "use client" -import type { ChatRequestOptions, Message } from "ai" +import type { ChatRequestOptions, UIMessage } from "ai" import cx from "classnames" import { AnimatePresence, motion } from "framer-motion" import { memo, useMemo, useState } from "react" @@ -36,11 +36,11 @@ const PurePreviewMessage = ({ isReadonly, }: { chatId: string - message: Message + message: UIMessage vote: Vote | undefined isLoading: boolean setMessages: ( - messages: Message[] | ((messages: Message[]) => Message[]), + messages: UIMessage[] | ((messages: UIMessage[]) => UIMessage[]), ) => void reload: ( chatRequestOptions?: ChatRequestOptions, @@ -49,6 +49,17 @@ const PurePreviewMessage = ({ }) => { const [mode, setMode] = useState<"view" | "edit">("view") + // Extract content from parts array (v5) + const textContent = + message.parts + ?.filter((part: any) => part.type === "text") + .map((part: any) => part.text) + .join("\n") || "" + + // Extract tool invocations from parts array (v5) + const toolParts = + message.parts?.filter((part: any) => part.type?.startsWith("tool-")) || [] + return ( - {message.experimental_attachments && ( -
- {message.experimental_attachments.map((attachment) => ( - - ))} -
- )} - - {message.reasoning && ( - - )} - - {(message.content || message.reasoning) && mode === "view" && ( + {textContent && mode === "view" && (
{message.role === "user" && !isReadonly && ( @@ -118,12 +111,12 @@ const PurePreviewMessage = ({ message.role === "user", })} > - {message.content as string} + {textContent as string}
)} - {message.content && mode === "edit" && ( + {textContent && mode === "edit" && (
@@ -137,14 +130,15 @@ const PurePreviewMessage = ({
)} - {message.toolInvocations && message.toolInvocations.length > 0 && ( + {toolParts && toolParts.length > 0 && (
- {message.toolInvocations.map((toolInvocation) => { - const { toolName, toolCallId, state, args } = toolInvocation - - if (state === "result") { - const { result } = toolInvocation + {toolParts.map((toolPart: any) => { + const toolName = toolPart.type?.replace("tool-", "") || "" + const toolCallId = toolPart.toolCallId || "" + const args = toolPart.input || {} + const result = toolPart.result + if (result) { return (
{toolName === "getWeather" ? ( @@ -222,16 +216,7 @@ export const PreviewMessage = memo( PurePreviewMessage, (prevProps, nextProps) => { if (prevProps.isLoading !== nextProps.isLoading) return false - if (prevProps.message.reasoning !== nextProps.message.reasoning) - return false - if (prevProps.message.content !== nextProps.message.content) return false - if ( - !equal( - prevProps.message.toolInvocations, - nextProps.message.toolInvocations, - ) - ) - return false + if (!equal(prevProps.message.parts, nextProps.message.parts)) return false if (!equal(prevProps.vote, nextProps.vote)) return false return true diff --git a/components/messages.tsx b/components/messages.tsx index 7f5ab5a..4b62756 100644 --- a/components/messages.tsx +++ b/components/messages.tsx @@ -1,4 +1,4 @@ -import type { ChatRequestOptions, Message } from "ai" +import type { ChatRequestOptions, UIMessage } from "ai" import { PreviewMessage, ThinkingMessage } from "./message" import { useScrollToBottom } from "./use-scroll-to-bottom" import { Overview } from "./overview" @@ -10,9 +10,9 @@ interface MessagesProps { chatId: string isLoading: boolean votes: Array | undefined - messages: Array + messages: Array setMessages: ( - messages: Message[] | ((messages: Message[]) => Message[]), + messages: UIMessage[] | ((messages: UIMessage[]) => UIMessage[]), ) => void reload: ( chatRequestOptions?: ChatRequestOptions, diff --git a/components/multimodal-input.tsx b/components/multimodal-input.tsx index d03c11f..52f5e90 100644 --- a/components/multimodal-input.tsx +++ b/components/multimodal-input.tsx @@ -1,6 +1,11 @@ "use client" -import type { Attachment, ChatRequestOptions, CreateMessage, Message } from "ai" +import type { + Attachment, + ChatRequestOptions, + CreateUIMessage, + UIMessage, +} from "ai" import cx from "classnames" import type React from "react" import { @@ -49,10 +54,10 @@ function PureMultimodalInput({ stop: () => void attachments: Array setAttachments: Dispatch>> - messages: Array - setMessages: Dispatch>> + messages: Array + setMessages: Dispatch>> append: ( - message: Message | CreateMessage, + message: UIMessage | CreateUIMessage, chatRequestOptions?: ChatRequestOptions, ) => Promise handleSubmit: ( @@ -144,6 +149,7 @@ function PureMultimodalInput({ const submitForm = useCallback(() => { window.history.replaceState({}, "", `/chat/${chatId}`) + /* FIXME(@ai-sdk-upgrade-v5): The `experimental_attachments` property has been replaced with the parts array. Please manually migrate following https://ai-sdk.dev/docs/migration-guides/migration-guide-5-0#attachments--file-parts */ handleSubmit(undefined, { experimental_attachments: attachments, }) @@ -466,7 +472,7 @@ function PureStopButton({ setMessages, }: { stop: () => void - setMessages: Dispatch>> + setMessages: Dispatch>> }) { return (