Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
},
"packageManager": "pnpm@10.14.0",
"dependencies": {
"@ai-sdk/google": "^2.0.11",
"@ai-sdk/google": "^3.0.13",
"@assistant-ui/react": "^0.12.1",
"@assistant-ui/react-ai-sdk": "^1.3.1",
"@assistant-ui/react-devtools": "^0.2.0",
Expand Down Expand Up @@ -88,7 +88,7 @@
"@tanstack/react-query-devtools": "^5.91.2",
"@tanstack/react-virtual": "^3.13.14",
"@vercel/speed-insights": "^1.3.1",
"ai": "^5.0.116",
"ai": "^6.0.50",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

Vercel AI SDK v6 migration guide breaking changes

💡 Result:

Key breaking / migration-impact changes when upgrading Vercel AI SDK v5 → v6:

  • Packages / versions: ai^6.0.0, @ai-sdk/provider^3.0.0, @ai-sdk/provider-utils^4.0.0, @ai-sdk/*^3.0.0. Run codemods via npx @ai-sdk/codemod v6. [1]
  • Agents: Experimental_Agent replaced by ToolLoopAgent; system renamed to instructions; default stopWhen changed to stepCountIs(20). [1]
  • Core message types: CoreMessage removed; convertToCoreMessagesconvertToModelMessages (and convertToModelMessages is now async, so you’ll likely need await). [1]
  • Tools
    • Tool.toModelOutput now receives an object parameter: ({ output }) => ... (instead of output => ...). [1]
    • ToolCallOptions renamed to ToolExecutionOptions (old name deprecated). [1]
    • Strict tool calling is now per-tool: set strict: true on each tool (instead of relying on provider-level strictness applying to all tools). [1]
    • ToolCallRepairFunction’s system param can now be string | SystemModelMessage | undefined. [1]
    • “Flexible tool content” support (may affect how you structure tool outputs/results). [1]
  • Structured outputs / objects
    • generateObject and streamObject are deprecated; use generateText / streamText with an output setting instead. [1]
  • Embeddings: provider methods renamed textEmbeddingModelembeddingModel and textEmbeddingembedding; generics removed from EmbeddingModel, embed, embedMany. [1]
  • Warnings / finish reasons
    • New warning logger (disable via AI_SDK_LOG_WARNINGS=false). [1]
    • Warning types unified to a single exported Warning type. [1]
    • Finish reason unknown removed; now returned as other. [1]
  • AI SDK UI helper renames (tool UI part helpers renamed, e.g. isToolUIPartisStaticToolUIPart, etc.). [1]

Provider-specific breaking changes called out in the guide:

  • OpenAI: strictJsonSchema is now enabled by default; structuredOutputs option removed from chat models (use strictJsonSchema). [1]
  • Azure: azure() now uses the Responses API by default (azure.chat() for Chat Completions); Responses API metadata/options key changes from openaiazure (with openai still accepted for input options). [1]
  • Anthropic: adds structuredOutputMode to control native structured outputs behavior for supported Claude models. [1]
  • Google Vertex: providerOptions / providerMetadata key changes from googlevertex (output metadata uses vertex). [1]

If you want, paste a snippet of your v5 usage (agents/tools/providers/UI) and I’ll point out exactly which of the above apply.

References: AI SDK 6 migration guide and change list. [1]

Sources
[1] (ai-sdk.dev)

Citations:


Major version bump for ai package—significant breaking changes require review.

Upgrading from v5 to v6 introduces multiple breaking changes across core APIs:

  • generateObject and streamObject are deprecated; migrate to generateText/streamText with output setting
  • Tool definitions changed: Tool.toModelOutput now receives ({ output }) => ... instead of output => ...; ToolCallOptions renamed to ToolExecutionOptions
  • CoreMessage removed; convertToCoreMessagesconvertToModelMessages (now async)
  • Agents: Experimental_Agent replaced by ToolLoopAgent; system renamed to instructions
  • Provider-specific changes (OpenAI's strictJsonSchema now default-enabled; Azure, Anthropic, Google Vertex options renamed)

Review your codebase usage against the migration guide and run npx @ai-sdk/codemod v6 for automated fixes.

🤖 Prompt for AI Agents
In `@package.json` at line 91, Package.json upgrades the "ai" dependency to v6
which contains breaking API changes; update usages across the codebase: replace
generateObject/streamObject calls with generateText/streamText and set output
appropriately, update Tool implementations to use the new toModelOutput
signature ({ output }) => ... and rename ToolCallOptions → ToolExecutionOptions,
change convertToCoreMessages usages to await convertToModelMessages (now async)
and update CoreMessage references, migrate any Experimental_Agent usage to
ToolLoopAgent and rename agent "system" fields to "instructions", and review
provider-specific option renames (e.g., strictJsonSchema defaults)—run npx
`@ai-sdk/codemod` v6 and follow the v6 migration guide to automate and verify
these changes.

"assistant-cloud": "^0.1.13",
"better-auth": "^1.4.10",
"class-variance-authority": "^0.7.1",
Expand Down Expand Up @@ -129,6 +129,7 @@
"sonner": "^2.0.7",
"streamdown": "^2.1.0",
"tailwind-merge": "^3.4.0",
"tw-shimmer": "^0.4.3",
"typewriter-effect": "^2.22.0",
"zod": "^4.3.4",
"zustand": "^5.0.9"
Expand Down
5 changes: 3 additions & 2 deletions src/app/api/chat/route.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { google } from "@ai-sdk/google";
import { streamText, convertToModelMessages, stepCountIs } from "ai";
import { streamText, convertToModelMessages, stepCountIs, tool, zodSchema } from "ai";
import type { UIMessage } from "ai";
import { logger } from "@/lib/utils/logger";
import { auth } from "@/lib/auth";
import { headers } from "next/headers";
Expand Down Expand Up @@ -175,7 +176,7 @@ export async function POST(req: Request) {
const session = await auth.api.getSession({ headers: headersObj });
const userId = session?.user?.id || null;

const messages = body.messages || [];
const { messages = [] }: { messages?: UIMessage[] } = body;
const system = body.system || "";
workspaceId = extractWorkspaceId(body);
activeFolderId = body.activeFolderId;
Expand Down
4 changes: 1 addition & 3 deletions src/app/globals.css
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
@import "tailwindcss";
@import "tw-animate-css";
@import "@daveyplate/better-auth-ui/css";
@import "tw-shimmer";

/* Path to your installed `@blocknote/shadcn` package. */
@source "../node_modules/@blocknote/shadcn";
Expand Down Expand Up @@ -247,8 +248,6 @@
margin-top: -0.125rem !important;
}



.bn-block-content[data-content-type="checkListItem"] input[type="checkbox"] {
margin-top: 0.25rem !important;
flex-shrink: 0 !important;
Expand All @@ -257,7 +256,6 @@
cursor: pointer !important;
}


.bn-block-content[data-content-type="checkListItem"] .bn-inline-content {
margin-top: 0 !important;
flex: 1 !important;
Expand Down
12 changes: 6 additions & 6 deletions src/components/assistant-ui/AssistantDropzone.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"use client";

import { useDropzone } from "react-dropzone";
import { useAssistantApi } from "@assistant-ui/react";
import { useAui } from "@assistant-ui/react";
import { useWorkspaceStore } from "@/lib/stores/workspace-store";
import { Upload } from "lucide-react";
import { useCallback, useState, useRef } from "react";
Expand All @@ -16,7 +16,7 @@ interface AssistantDropzoneProps {
* Accepts all supported file types and adds them as attachments to the chat composer.
*/
export function AssistantDropzone({ children }: AssistantDropzoneProps) {
const api = useAssistantApi();
const aui = useAui();
const currentWorkspaceId = useWorkspaceStore((state) => state.currentWorkspaceId);
const [isDragging, setIsDragging] = useState(false);

Expand All @@ -31,7 +31,7 @@ export function AssistantDropzone({ children }: AssistantDropzoneProps) {

const onDrop = useCallback(
async (acceptedFiles: File[]) => {
if (!currentWorkspaceId || !api) return;
if (!currentWorkspaceId || !aui) return;

const MAX_FILES = 10;
const MAX_FILE_SIZE_MB = 10;
Expand Down Expand Up @@ -93,7 +93,7 @@ export function AssistantDropzone({ children }: AssistantDropzoneProps) {
// Add each file to the composer
const addPromises = validFiles.map(async (file) => {
try {
await api.composer().addAttachment(file);
await aui.composer().addAttachment(file);
} catch (error) {
console.error("Failed to add attachment:", error);
// Remove from processing set on error so it can be retried
Expand Down Expand Up @@ -124,7 +124,7 @@ export function AssistantDropzone({ children }: AssistantDropzoneProps) {
}, 200);
}
},
[api, currentWorkspaceId]
[aui, currentWorkspaceId]
);

// Clear processing state when drag ends (user drags away or cancels)
Expand All @@ -139,7 +139,7 @@ export function AssistantDropzone({ children }: AssistantDropzoneProps) {
onDrop,
noClick: true, // Don't trigger on click, only drag and drop
noKeyboard: true, // Don't trigger on keyboard
disabled: !currentWorkspaceId || !api, // Disable if no workspace is selected or api is not available
disabled: !currentWorkspaceId || !aui, // Disable if no workspace is selected or api is not available
accept: {
'image/*': ['.png', '.jpg', '.jpeg', '.gif', '.webp', '.svg', '.bmp'],
'video/*': ['.mp4', '.webm', '.avi', '.mov', '.mkv'],
Expand Down
6 changes: 3 additions & 3 deletions src/components/assistant-ui/AssistantTextSelectionManager.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import SelectableText, {
import { useUIStore, selectReplySelections } from "@/lib/stores/ui-store";
import { getHighlightColorById } from "@/lib/utils/highlight-colors";
import { useWorkspaceStore } from "@/lib/stores/workspace-store";
import { useAssistantState } from "@assistant-ui/react";
import { useAuiState } from "@assistant-ui/react";
import { useQueryClient } from "@tanstack/react-query";
import {
Dialog,
Expand Down Expand Up @@ -105,8 +105,8 @@ export default function AssistantTextSelectionManager({
// This component is always rendered within AssistantRuntimeProvider, so the hook is safe to use
// Use threadListItem.id if available, otherwise fall back to mainThreadId
// Using safe hooks to handle race condition during thread switching (GitHub issue #2722)
const threadListItemId = useAssistantState(({ threadListItem }) => (threadListItem as any)?.id);
const mainThreadId = useAssistantState(({ threads }) => (threads as any)?.mainThreadId);
const threadListItemId = useAuiState(({ threadListItem }) => (threadListItem as any)?.id);
const mainThreadId = useAuiState(({ threads }) => (threads as any)?.mainThreadId);
const currentThreadId = threadListItemId || mainThreadId;


Expand Down
102 changes: 0 additions & 102 deletions src/components/assistant-ui/SafeAssistantRuntimeProvider.tsx

This file was deleted.

10 changes: 5 additions & 5 deletions src/components/assistant-ui/SpeechToTextButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@ import "regenerator-runtime/runtime";
import { Mic, MicOff } from "lucide-react";
import { FC, useEffect, useState } from "react";
import SpeechRecognition, { useSpeechRecognition } from "react-speech-recognition";
import { useAssistantApi } from "@assistant-ui/react";
import { useAui } from "@assistant-ui/react";
import { TooltipIconButton } from "@/components/assistant-ui/tooltip-icon-button";
import { cn } from "@/lib/utils";

export const SpeechToTextButton: FC = () => {
const api = useAssistantApi();
const aui = useAui();
const {
transcript,
listening,
Expand All @@ -23,16 +23,16 @@ export const SpeechToTextButton: FC = () => {
// We append the transcript to the original text
// Ideally we would insert at cursor, but appending is safer for now
const separator = originalText && !originalText.endsWith(' ') ? ' ' : '';
api.composer().setText(originalText + separator + transcript);
aui.composer().setText(originalText + separator + transcript);
}
}, [transcript, listening, originalText, api]);
}, [transcript, listening, originalText, aui]);

const handleStartListening = () => {
// Reset transcript before starting a new session
resetTranscript();

// Capture current text before starting
const currentText = api.composer().getState().text;
const currentText = aui.composer().getState().text;
setOriginalText(currentText);

SpeechRecognition.startListening({ continuous: true });
Expand Down
7 changes: 3 additions & 4 deletions src/components/assistant-ui/WorkspaceRuntimeProvider.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
"use client";

import { AssistantCloud } from "@assistant-ui/react";
import { SafeAssistantRuntimeProvider } from "@/components/assistant-ui/SafeAssistantRuntimeProvider";
import { AssistantCloud, AssistantRuntimeProvider } from "@assistant-ui/react";
import { useChatRuntime, AssistantChatTransport } from "@assistant-ui/react-ai-sdk";
import { useMemo, useCallback } from "react";
import { AssistantAvailableProvider } from "@/contexts/AssistantAvailabilityContext";
Expand Down Expand Up @@ -151,10 +150,10 @@ export function WorkspaceRuntimeProvider({
});

return (
<SafeAssistantRuntimeProvider runtime={runtime}>
<AssistantRuntimeProvider runtime={runtime}>
<AssistantAvailableProvider>
{children}
</AssistantAvailableProvider>
</SafeAssistantRuntimeProvider>
</AssistantRuntimeProvider>
);
}
6 changes: 3 additions & 3 deletions src/components/assistant-ui/assistant-loader.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"use client";

import { useAssistantState } from "@assistant-ui/react";
import { useAuiState } from "@assistant-ui/react";
import { useState, useEffect } from "react";
import ShinyText from "@/components/ShinyText";

Expand All @@ -14,11 +14,11 @@ const thinkingMessages = [
];

export const AssistantLoader = () => {
const isRunning = useAssistantState(
const isRunning = useAuiState(
({ message }) => (message as { status?: { type: string } })?.status?.type === "running"
);

const isMessageEmpty = useAssistantState(({ message }) => {
const isMessageEmpty = useAuiState(({ message }) => {
const msg = message as any;
return !msg?.content || (Array.isArray(msg.content) && msg.content.length === 0);
});
Expand Down
Loading