Skip to content

Commit 5560ac2

Browse files
dcramerclaude
andcommitted
fix(cloudflare): Migrate legacy AI SDK 4.x messages to new parts format
Added migration logic to convert persisted messages from AI SDK 4.x format (using `content` string) to AI SDK 6 format (using `parts` array). This prevents silent data loss when users upgrade, as their chat history will be automatically migrated instead of being filtered out. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 8d6720c commit 5560ac2

1 file changed

Lines changed: 32 additions & 3 deletions

File tree

‎packages/mcp-cloudflare/src/client/hooks/use-persisted-chat.ts‎

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,34 @@ const TIMESTAMP_STORAGE_KEY = "sentry_chat_timestamp";
66
const MAX_STORED_MESSAGES = 100; // Limit storage size
77
const CACHE_EXPIRY_MS = 60 * 60 * 1000; // 1 hour in milliseconds
88

9+
// Legacy AI SDK 4.x message format (before migration to parts-based format)
10+
interface LegacyMessage {
11+
id: string;
12+
role: "user" | "assistant" | "system";
13+
content?: string;
14+
parts?: UIMessage["parts"];
15+
}
16+
17+
// Migrate legacy messages (AI SDK 4.x format with `content`) to new format (with `parts`)
18+
function migrateMessage(msg: LegacyMessage): UIMessage {
19+
// If message already has parts, use it as-is
20+
if (msg.parts && Array.isArray(msg.parts) && msg.parts.length > 0) {
21+
return msg as UIMessage;
22+
}
23+
24+
// If message has legacy content string, convert to parts format
25+
if (typeof msg.content === "string" && msg.content.length > 0) {
26+
return {
27+
id: msg.id,
28+
role: msg.role,
29+
parts: [{ type: "text", text: msg.content }],
30+
} as UIMessage;
31+
}
32+
33+
// Return as-is if neither format matches (will be filtered by validation)
34+
return msg as UIMessage;
35+
}
36+
937
export function usePersistedChat(isAuthenticated: boolean) {
1038
// Check if cache is expired
1139
const isCacheExpired = useCallback(() => {
@@ -87,11 +115,12 @@ export function usePersistedChat(isAuthenticated: boolean) {
87115
try {
88116
const stored = localStorage.getItem(CHAT_STORAGE_KEY);
89117
if (stored) {
90-
const parsed = JSON.parse(stored) as UIMessage[];
118+
const parsed = JSON.parse(stored) as LegacyMessage[];
91119
// Validate the data structure
92120
if (Array.isArray(parsed) && parsed.length > 0) {
93-
// Filter out any invalid or incomplete messages
94-
const validMessages = parsed.filter(isValidMessage);
121+
// Migrate legacy messages and filter out any invalid ones
122+
const migratedMessages = parsed.map(migrateMessage);
123+
const validMessages = migratedMessages.filter(isValidMessage);
95124
if (validMessages.length > 0) {
96125
// Update timestamp since we're loading existing messages
97126
updateTimestamp();

0 commit comments

Comments
 (0)