This document provides complete migration instructions for upgrading the Townie codebase from AI SDK v4 to v5. This migration involves breaking changes in message structure, package organization, and API changes.
Update all AI SDK imports from v4 to v5:
Backend files (backend/routes/send-message.ts
, backend/tools/*.ts
):
// BEFORE (v4):
import { createAnthropic } from "npm:@ai-sdk/anthropic@1.2.12";
import {
convertToCoreMessages,
type CoreUserMessage,
streamText,
} from "npm:ai@4.3.19";
import { tool } from "npm:ai@4.3.19";
// AFTER (v5):
import { createAnthropic } from "npm:@ai-sdk/anthropic@2.0.0";
import {
convertToCoreMessages,
type CoreUserMessage,
streamText,
} from "npm:ai@5.0.0";
import { tool } from "npm:ai@5.0.0";
Frontend file (index.ts
):
// BEFORE (v4):
"@ai-sdk/react": "https://esm.sh/@ai-sdk/react@1.2.12?deps=react@18.2.0,react-dom@18.2.0,zod@3.25.76",
// AFTER (v5):
"@ai-sdk/react": "https://esm.sh/@ai-sdk/react@2.0.0?deps=react@18.2.0,react-dom@18.2.0,zod@3.25.76",
Message Content Format Change:
Messages now use a parts
array structure instead of a content
string.
Lines 120-128 - Update CoreUserMessage creation:
// BEFORE (v4):
const newUserMessage: CoreUserMessage = {
role: "user",
content: typeof lastUserMessageContent === "string"
? [{
type: "text",
text: lastUserMessageContent,
}]
: lastUserMessageContent,
};
// AFTER (v5):
const newUserMessage: CoreUserMessage = {
role: "user",
content: typeof lastUserMessageContent === "string"
? [{
type: "text",
text: lastUserMessageContent,
}]
: lastUserMessageContent, // This stays the same as it's already in parts format
};
Lines 142-147 - Update image part format:
// BEFORE (v4):
newUserMessage.content.push({
type: "image",
image: image.url,
mimeType,
});
// AFTER (v5):
newUserMessage.content.push({
type: "image",
url: image.url, // Changed from 'image' to 'url'
mimeType,
});
Line 200 - Rename maxTokens parameter:
// BEFORE (v4):
maxTokens: 4_096 * 10,
// AFTER (v5):
maxOutputTokens: 4_096 * 10, // Renamed from maxTokens
All tool files (backend/tools/text-editor.ts
, backend/tools/fetch.ts
, backend/tools/traces.ts
, backend/tools/think.ts
, backend/tools/change-val-type.ts
):
Update the tool()
function calls to use inputSchema
instead of schema
:
// BEFORE (v4):
export function makeTextEditorTool({ bearerToken, project, branch_id }: ToolConfig) {
return tool({
description: "...",
parameters: z.object({
// schema definitions
}),
// ... rest of tool
});
}
// AFTER (v5):
export function makeTextEditorTool({ bearerToken, project, branch_id }: ToolConfig) {
return tool({
description: "...",
inputSchema: z.object({ // Changed from 'parameters' to 'inputSchema'
// schema definitions - these stay the same
}),
// ... rest of tool
});
}
The useChat
hook API remains largely compatible, but message structure changes:
Lines 32-76 - The useChat hook usage stays the same, but internally messages now use parts structure:
// The useChat call itself doesn't change, but be aware that:
// - messages[].content is now a parts array
// - Image handling in the body.images mapping may need adjustment
The CoreUserMessage
type now expects content as a parts array:
// BEFORE (v4):
interface CoreUserMessage {
role: "user";
content: string | Array<TextPart | ImagePart>;
}
// AFTER (v5):
interface CoreUserMessage {
role: "user";
content: Array<TextPart | ImagePart>; // Always an array now
}
// ImagePart structure change:
// BEFORE: { type: "image", image: string, mimeType?: string }
// AFTER: { type: "image", url: string, mimeType?: string }
-
backend/routes/send-message.ts
- Main API endpoint- Package imports (lines 2, 3-7)
- Image part format (lines 142-147)
- maxTokens → maxOutputTokens (line 200)
-
index.ts
- Package version for frontend- @ai-sdk/react version update
backend/tools/text-editor.ts
- parameters → inputSchemabackend/tools/fetch.ts
- parameters → inputSchemabackend/tools/traces.ts
- parameters → inputSchemabackend/tools/think.ts
- parameters → inputSchemabackend/tools/change-val-type.ts
- parameters → inputSchema
frontend/hooks/useChatLogic.ts
- Import update only (line 1)
After making changes, verify:
- Basic chat functionality - Send text messages
- Image uploads - Send messages with images
- Tool execution - Verify each tool (text-editor, fetch, traces, think, change-val-type)
- Streaming - Ensure message streaming still works
- Error handling - Test error scenarios
- Sound effects - Verify completion sounds work
- Message formatting - Check assistant message display
Solution: Ensure all message.content references expect parts array format
Solution: Check image part format uses url
instead of image
property
Solution: Verify all tools use inputSchema
instead of parameters
Solution: Check @ai-sdk/react version compatibility with React 18.2.0
- Backend first: Update send-message.ts and tools
- Test backend: Use API directly to verify functionality
- Frontend second: Update index.ts and useChatLogic.ts
- Full integration test: Test complete user flow
If migration fails:
- Revert package versions to v4:
npm:ai@4.3.19
npm:@ai-sdk/anthropic@1.2.12
@ai-sdk/react@1.2.12
- Revert code changes:
maxOutputTokens
→maxTokens
inputSchema
→parameters
url
→image
in image parts
- Test functionality before proceeding
- Val Town Compatibility: Ensure ESM imports work with Val Town platform
- React 18.2.0 Pinning: Maintain React version pinning for compatibility
- Zod Compatibility: AI SDK v5 supports both Zod 3 and 4
- No Breaking Changes: Most streaming and error handling should remain compatible
This migration primarily affects message structure and tool definitions. The core streaming and chat functionality should work similarly once the breaking changes are addressed.