chore: update to ai sdk v5 beta (#1074)
This commit is contained in:
parent
7d8e71383f
commit
4c281fe09d
54 changed files with 1372 additions and 1060 deletions
|
|
@ -1,13 +1,14 @@
|
|||
import { simulateReadableStream } from 'ai';
|
||||
import { MockLanguageModelV1 } from 'ai/test';
|
||||
import { MockLanguageModelV2 } from 'ai/test';
|
||||
import { getResponseChunksByPrompt } from '@/tests/prompts/utils';
|
||||
|
||||
export const chatModel = new MockLanguageModelV1({
|
||||
export const chatModel = new MockLanguageModelV2({
|
||||
doGenerate: async () => ({
|
||||
rawCall: { rawPrompt: null, rawSettings: {} },
|
||||
finishReason: 'stop',
|
||||
usage: { promptTokens: 10, completionTokens: 20 },
|
||||
text: `Hello, world!`,
|
||||
usage: { inputTokens: 10, outputTokens: 20, totalTokens: 30 },
|
||||
content: [{ type: 'text', text: 'Hello, world!' }],
|
||||
warnings: [],
|
||||
}),
|
||||
doStream: async ({ prompt }) => ({
|
||||
stream: simulateReadableStream({
|
||||
|
|
@ -19,12 +20,13 @@ export const chatModel = new MockLanguageModelV1({
|
|||
}),
|
||||
});
|
||||
|
||||
export const reasoningModel = new MockLanguageModelV1({
|
||||
export const reasoningModel = new MockLanguageModelV2({
|
||||
doGenerate: async () => ({
|
||||
rawCall: { rawPrompt: null, rawSettings: {} },
|
||||
finishReason: 'stop',
|
||||
usage: { promptTokens: 10, completionTokens: 20 },
|
||||
text: `Hello, world!`,
|
||||
usage: { inputTokens: 10, outputTokens: 20, totalTokens: 30 },
|
||||
content: [{ type: 'text', text: 'Hello, world!' }],
|
||||
warnings: [],
|
||||
}),
|
||||
doStream: async ({ prompt }) => ({
|
||||
stream: simulateReadableStream({
|
||||
|
|
@ -36,24 +38,26 @@ export const reasoningModel = new MockLanguageModelV1({
|
|||
}),
|
||||
});
|
||||
|
||||
export const titleModel = new MockLanguageModelV1({
|
||||
export const titleModel = new MockLanguageModelV2({
|
||||
doGenerate: async () => ({
|
||||
rawCall: { rawPrompt: null, rawSettings: {} },
|
||||
finishReason: 'stop',
|
||||
usage: { promptTokens: 10, completionTokens: 20 },
|
||||
text: `This is a test title`,
|
||||
usage: { inputTokens: 10, outputTokens: 20, totalTokens: 30 },
|
||||
content: [{ type: 'text', text: 'This is a test title' }],
|
||||
warnings: [],
|
||||
}),
|
||||
doStream: async () => ({
|
||||
stream: simulateReadableStream({
|
||||
chunkDelayInMs: 500,
|
||||
initialDelayInMs: 1000,
|
||||
chunks: [
|
||||
{ type: 'text-delta', textDelta: 'This is a test title' },
|
||||
{ id: '1', type: 'text-start' },
|
||||
{ id: '1', type: 'text-delta', delta: 'This is a test title' },
|
||||
{ id: '1', type: 'text-end' },
|
||||
{
|
||||
type: 'finish',
|
||||
finishReason: 'stop',
|
||||
logprobs: undefined,
|
||||
usage: { completionTokens: 10, promptTokens: 3 },
|
||||
usage: { inputTokens: 3, outputTokens: 10, totalTokens: 13 },
|
||||
},
|
||||
],
|
||||
}),
|
||||
|
|
@ -61,12 +65,13 @@ export const titleModel = new MockLanguageModelV1({
|
|||
}),
|
||||
});
|
||||
|
||||
export const artifactModel = new MockLanguageModelV1({
|
||||
export const artifactModel = new MockLanguageModelV2({
|
||||
doGenerate: async () => ({
|
||||
rawCall: { rawPrompt: null, rawSettings: {} },
|
||||
finishReason: 'stop',
|
||||
usage: { promptTokens: 10, completionTokens: 20 },
|
||||
text: `Hello, world!`,
|
||||
usage: { inputTokens: 10, outputTokens: 20, totalTokens: 30 },
|
||||
content: [{ type: 'text', text: 'Hello, world!' }],
|
||||
warnings: [],
|
||||
}),
|
||||
doStream: async ({ prompt }) => ({
|
||||
stream: simulateReadableStream({
|
||||
|
|
|
|||
|
|
@ -4,13 +4,13 @@ import {
|
|||
wrapLanguageModel,
|
||||
} from 'ai';
|
||||
import { xai } from '@ai-sdk/xai';
|
||||
import { isTestEnvironment } from '../constants';
|
||||
import {
|
||||
artifactModel,
|
||||
chatModel,
|
||||
reasoningModel,
|
||||
titleModel,
|
||||
} from './models.test';
|
||||
import { isTestEnvironment } from '../constants';
|
||||
|
||||
export const myProvider = isTestEnvironment
|
||||
? customProvider({
|
||||
|
|
@ -32,6 +32,6 @@ export const myProvider = isTestEnvironment
|
|||
'artifact-model': xai('grok-2-1212'),
|
||||
},
|
||||
imageModels: {
|
||||
'small-model': xai.image('grok-2-image'),
|
||||
'small-model': xai.imageModel('grok-2-image'),
|
||||
},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,46 +1,51 @@
|
|||
import { generateUUID } from '@/lib/utils';
|
||||
import { DataStreamWriter, tool } from 'ai';
|
||||
import { tool, type UIMessageStreamWriter } from 'ai';
|
||||
import { z } from 'zod';
|
||||
import { Session } from 'next-auth';
|
||||
import type { Session } from 'next-auth';
|
||||
import {
|
||||
artifactKinds,
|
||||
documentHandlersByArtifactKind,
|
||||
} from '@/lib/artifacts/server';
|
||||
import type { ChatMessage } from '@/lib/types';
|
||||
|
||||
interface CreateDocumentProps {
|
||||
session: Session;
|
||||
dataStream: DataStreamWriter;
|
||||
dataStream: UIMessageStreamWriter<ChatMessage>;
|
||||
}
|
||||
|
||||
export const createDocument = ({ session, dataStream }: CreateDocumentProps) =>
|
||||
tool({
|
||||
description:
|
||||
'Create a document for a writing or content creation activities. This tool will call other functions that will generate the contents of the document based on the title and kind.',
|
||||
parameters: z.object({
|
||||
inputSchema: z.object({
|
||||
title: z.string(),
|
||||
kind: z.enum(artifactKinds),
|
||||
}),
|
||||
execute: async ({ title, kind }) => {
|
||||
const id = generateUUID();
|
||||
|
||||
dataStream.writeData({
|
||||
type: 'kind',
|
||||
content: kind,
|
||||
dataStream.write({
|
||||
type: 'data-kind',
|
||||
data: kind,
|
||||
transient: true,
|
||||
});
|
||||
|
||||
dataStream.writeData({
|
||||
type: 'id',
|
||||
content: id,
|
||||
dataStream.write({
|
||||
type: 'data-id',
|
||||
data: id,
|
||||
transient: true,
|
||||
});
|
||||
|
||||
dataStream.writeData({
|
||||
type: 'title',
|
||||
content: title,
|
||||
dataStream.write({
|
||||
type: 'data-title',
|
||||
data: title,
|
||||
transient: true,
|
||||
});
|
||||
|
||||
dataStream.writeData({
|
||||
type: 'clear',
|
||||
content: '',
|
||||
dataStream.write({
|
||||
type: 'data-clear',
|
||||
data: null,
|
||||
transient: true,
|
||||
});
|
||||
|
||||
const documentHandler = documentHandlersByArtifactKind.find(
|
||||
|
|
@ -59,7 +64,7 @@ export const createDocument = ({ session, dataStream }: CreateDocumentProps) =>
|
|||
session,
|
||||
});
|
||||
|
||||
dataStream.writeData({ type: 'finish', content: '' });
|
||||
dataStream.write({ type: 'data-finish', data: null, transient: true });
|
||||
|
||||
return {
|
||||
id,
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import { z } from 'zod';
|
|||
|
||||
export const getWeather = tool({
|
||||
description: 'Get the current weather at a location',
|
||||
parameters: z.object({
|
||||
inputSchema: z.object({
|
||||
latitude: z.number(),
|
||||
longitude: z.number(),
|
||||
}),
|
||||
|
|
|
|||
|
|
@ -1,14 +1,15 @@
|
|||
import { z } from 'zod';
|
||||
import { Session } from 'next-auth';
|
||||
import { DataStreamWriter, streamObject, tool } from 'ai';
|
||||
import type { Session } from 'next-auth';
|
||||
import { streamObject, tool, type UIMessageStreamWriter } from 'ai';
|
||||
import { getDocumentById, saveSuggestions } from '@/lib/db/queries';
|
||||
import { Suggestion } from '@/lib/db/schema';
|
||||
import type { Suggestion } from '@/lib/db/schema';
|
||||
import { generateUUID } from '@/lib/utils';
|
||||
import { myProvider } from '../providers';
|
||||
import type { ChatMessage } from '@/lib/types';
|
||||
|
||||
interface RequestSuggestionsProps {
|
||||
session: Session;
|
||||
dataStream: DataStreamWriter;
|
||||
dataStream: UIMessageStreamWriter<ChatMessage>;
|
||||
}
|
||||
|
||||
export const requestSuggestions = ({
|
||||
|
|
@ -17,7 +18,7 @@ export const requestSuggestions = ({
|
|||
}: RequestSuggestionsProps) =>
|
||||
tool({
|
||||
description: 'Request suggestions for a document',
|
||||
parameters: z.object({
|
||||
inputSchema: z.object({
|
||||
documentId: z
|
||||
.string()
|
||||
.describe('The ID of the document to request edits'),
|
||||
|
|
@ -49,7 +50,8 @@ export const requestSuggestions = ({
|
|||
});
|
||||
|
||||
for await (const element of elementStream) {
|
||||
const suggestion = {
|
||||
// @ts-ignore todo: fix type
|
||||
const suggestion: Suggestion = {
|
||||
originalText: element.originalSentence,
|
||||
suggestedText: element.suggestedSentence,
|
||||
description: element.description,
|
||||
|
|
@ -58,9 +60,10 @@ export const requestSuggestions = ({
|
|||
isResolved: false,
|
||||
};
|
||||
|
||||
dataStream.writeData({
|
||||
type: 'suggestion',
|
||||
content: suggestion,
|
||||
dataStream.write({
|
||||
type: 'data-suggestion',
|
||||
data: suggestion,
|
||||
transient: true,
|
||||
});
|
||||
|
||||
suggestions.push(suggestion);
|
||||
|
|
|
|||
|
|
@ -1,18 +1,19 @@
|
|||
import { DataStreamWriter, tool } from 'ai';
|
||||
import { Session } from 'next-auth';
|
||||
import { tool, type UIMessageStreamWriter } from 'ai';
|
||||
import type { Session } from 'next-auth';
|
||||
import { z } from 'zod';
|
||||
import { getDocumentById, saveDocument } from '@/lib/db/queries';
|
||||
import { getDocumentById } from '@/lib/db/queries';
|
||||
import { documentHandlersByArtifactKind } from '@/lib/artifacts/server';
|
||||
import type { ChatMessage } from '@/lib/types';
|
||||
|
||||
interface UpdateDocumentProps {
|
||||
session: Session;
|
||||
dataStream: DataStreamWriter;
|
||||
dataStream: UIMessageStreamWriter<ChatMessage>;
|
||||
}
|
||||
|
||||
export const updateDocument = ({ session, dataStream }: UpdateDocumentProps) =>
|
||||
tool({
|
||||
description: 'Update a document with the given description.',
|
||||
parameters: z.object({
|
||||
inputSchema: z.object({
|
||||
id: z.string().describe('The ID of the document to update'),
|
||||
description: z
|
||||
.string()
|
||||
|
|
@ -27,9 +28,10 @@ export const updateDocument = ({ session, dataStream }: UpdateDocumentProps) =>
|
|||
};
|
||||
}
|
||||
|
||||
dataStream.writeData({
|
||||
type: 'clear',
|
||||
content: document.title,
|
||||
dataStream.write({
|
||||
type: 'data-clear',
|
||||
data: null,
|
||||
transient: true,
|
||||
});
|
||||
|
||||
const documentHandler = documentHandlersByArtifactKind.find(
|
||||
|
|
@ -48,7 +50,7 @@ export const updateDocument = ({ session, dataStream }: UpdateDocumentProps) =>
|
|||
session,
|
||||
});
|
||||
|
||||
dataStream.writeData({ type: 'finish', content: '' });
|
||||
dataStream.write({ type: 'data-finish', data: null, transient: true });
|
||||
|
||||
return {
|
||||
id,
|
||||
|
|
|
|||
|
|
@ -2,11 +2,12 @@ import { codeDocumentHandler } from '@/artifacts/code/server';
|
|||
import { imageDocumentHandler } from '@/artifacts/image/server';
|
||||
import { sheetDocumentHandler } from '@/artifacts/sheet/server';
|
||||
import { textDocumentHandler } from '@/artifacts/text/server';
|
||||
import { ArtifactKind } from '@/components/artifact';
|
||||
import { DataStreamWriter } from 'ai';
|
||||
import { Document } from '../db/schema';
|
||||
import type { ArtifactKind } from '@/components/artifact';
|
||||
import type { Document } from '../db/schema';
|
||||
import { saveDocument } from '../db/queries';
|
||||
import { Session } from 'next-auth';
|
||||
import type { Session } from 'next-auth';
|
||||
import type { UIMessageStreamWriter } from 'ai';
|
||||
import type { ChatMessage } from '../types';
|
||||
|
||||
export interface SaveDocumentProps {
|
||||
id: string;
|
||||
|
|
@ -19,14 +20,14 @@ export interface SaveDocumentProps {
|
|||
export interface CreateDocumentCallbackProps {
|
||||
id: string;
|
||||
title: string;
|
||||
dataStream: DataStreamWriter;
|
||||
dataStream: UIMessageStreamWriter<ChatMessage>;
|
||||
session: Session;
|
||||
}
|
||||
|
||||
export interface UpdateDocumentCallbackProps {
|
||||
document: Document;
|
||||
description: string;
|
||||
dataStream: DataStreamWriter;
|
||||
dataStream: UIMessageStreamWriter<ChatMessage>;
|
||||
session: Session;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,251 +1,253 @@
|
|||
import { config } from 'dotenv';
|
||||
import postgres from 'postgres';
|
||||
import {
|
||||
chat,
|
||||
message,
|
||||
type MessageDeprecated,
|
||||
messageDeprecated,
|
||||
vote,
|
||||
voteDeprecated,
|
||||
} from '../schema';
|
||||
import { drizzle } from 'drizzle-orm/postgres-js';
|
||||
import { inArray } from 'drizzle-orm';
|
||||
import { appendResponseMessages, type UIMessage } from 'ai';
|
||||
// This is a helper for an older version of ai, v4.3.13
|
||||
|
||||
config({
|
||||
path: '.env.local',
|
||||
});
|
||||
// import { config } from 'dotenv';
|
||||
// import postgres from 'postgres';
|
||||
// import {
|
||||
// chat,
|
||||
// message,
|
||||
// type MessageDeprecated,
|
||||
// messageDeprecated,
|
||||
// vote,
|
||||
// voteDeprecated,
|
||||
// } from '../schema';
|
||||
// import { drizzle } from 'drizzle-orm/postgres-js';
|
||||
// import { inArray } from 'drizzle-orm';
|
||||
// import { appendResponseMessages, type UIMessage } from 'ai';
|
||||
|
||||
if (!process.env.POSTGRES_URL) {
|
||||
throw new Error('POSTGRES_URL environment variable is not set');
|
||||
}
|
||||
// config({
|
||||
// path: '.env.local',
|
||||
// });
|
||||
|
||||
const client = postgres(process.env.POSTGRES_URL);
|
||||
const db = drizzle(client);
|
||||
// if (!process.env.POSTGRES_URL) {
|
||||
// throw new Error('POSTGRES_URL environment variable is not set');
|
||||
// }
|
||||
|
||||
const BATCH_SIZE = 100; // Process 100 chats at a time
|
||||
const INSERT_BATCH_SIZE = 1000; // Insert 1000 messages at a time
|
||||
// const client = postgres(process.env.POSTGRES_URL);
|
||||
// const db = drizzle(client);
|
||||
|
||||
type NewMessageInsert = {
|
||||
id: string;
|
||||
chatId: string;
|
||||
parts: any[];
|
||||
role: string;
|
||||
attachments: any[];
|
||||
createdAt: Date;
|
||||
};
|
||||
// const BATCH_SIZE = 100; // Process 100 chats at a time
|
||||
// const INSERT_BATCH_SIZE = 1000; // Insert 1000 messages at a time
|
||||
|
||||
type NewVoteInsert = {
|
||||
messageId: string;
|
||||
chatId: string;
|
||||
isUpvoted: boolean;
|
||||
};
|
||||
// type NewMessageInsert = {
|
||||
// id: string;
|
||||
// chatId: string;
|
||||
// parts: any[];
|
||||
// role: string;
|
||||
// attachments: any[];
|
||||
// createdAt: Date;
|
||||
// };
|
||||
|
||||
interface MessageDeprecatedContentPart {
|
||||
type: string;
|
||||
content: unknown;
|
||||
}
|
||||
// type NewVoteInsert = {
|
||||
// messageId: string;
|
||||
// chatId: string;
|
||||
// isUpvoted: boolean;
|
||||
// };
|
||||
|
||||
function getMessageRank(message: MessageDeprecated): number {
|
||||
if (
|
||||
message.role === 'assistant' &&
|
||||
(message.content as MessageDeprecatedContentPart[]).some(
|
||||
(contentPart) => contentPart.type === 'tool-call',
|
||||
)
|
||||
) {
|
||||
return 0;
|
||||
}
|
||||
// interface MessageDeprecatedContentPart {
|
||||
// type: string;
|
||||
// content: unknown;
|
||||
// }
|
||||
|
||||
if (
|
||||
message.role === 'tool' &&
|
||||
(message.content as MessageDeprecatedContentPart[]).some(
|
||||
(contentPart) => contentPart.type === 'tool-result',
|
||||
)
|
||||
) {
|
||||
return 1;
|
||||
}
|
||||
// function getMessageRank(message: MessageDeprecated): number {
|
||||
// if (
|
||||
// message.role === 'assistant' &&
|
||||
// (message.content as MessageDeprecatedContentPart[]).some(
|
||||
// (contentPart) => contentPart.type === 'tool-call',
|
||||
// )
|
||||
// ) {
|
||||
// return 0;
|
||||
// }
|
||||
|
||||
if (message.role === 'assistant') {
|
||||
return 2;
|
||||
}
|
||||
// if (
|
||||
// message.role === 'tool' &&
|
||||
// (message.content as MessageDeprecatedContentPart[]).some(
|
||||
// (contentPart) => contentPart.type === 'tool-result',
|
||||
// )
|
||||
// ) {
|
||||
// return 1;
|
||||
// }
|
||||
|
||||
return 3;
|
||||
}
|
||||
// if (message.role === 'assistant') {
|
||||
// return 2;
|
||||
// }
|
||||
|
||||
function dedupeParts<T extends { type: string; [k: string]: any }>(
|
||||
parts: T[],
|
||||
): T[] {
|
||||
const seen = new Set<string>();
|
||||
return parts.filter((p) => {
|
||||
const key = `${p.type}|${JSON.stringify(p.content ?? p)}`;
|
||||
if (seen.has(key)) return false;
|
||||
seen.add(key);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
// return 3;
|
||||
// }
|
||||
|
||||
function sanitizeParts<T extends { type: string; [k: string]: any }>(
|
||||
parts: T[],
|
||||
): T[] {
|
||||
return parts.filter(
|
||||
(part) => !(part.type === 'reasoning' && part.reasoning === 'undefined'),
|
||||
);
|
||||
}
|
||||
// function dedupeParts<T extends { type: string; [k: string]: any }>(
|
||||
// parts: T[],
|
||||
// ): T[] {
|
||||
// const seen = new Set<string>();
|
||||
// return parts.filter((p) => {
|
||||
// const key = `${p.type}|${JSON.stringify(p.content ?? p)}`;
|
||||
// if (seen.has(key)) return false;
|
||||
// seen.add(key);
|
||||
// return true;
|
||||
// });
|
||||
// }
|
||||
|
||||
async function migrateMessages() {
|
||||
const chats = await db.select().from(chat);
|
||||
// function sanitizeParts<T extends { type: string; [k: string]: any }>(
|
||||
// parts: T[],
|
||||
// ): T[] {
|
||||
// return parts.filter(
|
||||
// (part) => !(part.type === 'reasoning' && part.reasoning === 'undefined'),
|
||||
// );
|
||||
// }
|
||||
|
||||
let processedCount = 0;
|
||||
// async function migrateMessages() {
|
||||
// const chats = await db.select().from(chat);
|
||||
|
||||
for (let i = 0; i < chats.length; i += BATCH_SIZE) {
|
||||
const chatBatch = chats.slice(i, i + BATCH_SIZE);
|
||||
const chatIds = chatBatch.map((chat) => chat.id);
|
||||
// let processedCount = 0;
|
||||
|
||||
const allMessages = await db
|
||||
.select()
|
||||
.from(messageDeprecated)
|
||||
.where(inArray(messageDeprecated.chatId, chatIds));
|
||||
// for (let i = 0; i < chats.length; i += BATCH_SIZE) {
|
||||
// const chatBatch = chats.slice(i, i + BATCH_SIZE);
|
||||
// const chatIds = chatBatch.map((chat) => chat.id);
|
||||
|
||||
const allVotes = await db
|
||||
.select()
|
||||
.from(voteDeprecated)
|
||||
.where(inArray(voteDeprecated.chatId, chatIds));
|
||||
// const allMessages = await db
|
||||
// .select()
|
||||
// .from(messageDeprecated)
|
||||
// .where(inArray(messageDeprecated.chatId, chatIds));
|
||||
|
||||
const newMessagesToInsert: NewMessageInsert[] = [];
|
||||
const newVotesToInsert: NewVoteInsert[] = [];
|
||||
// const allVotes = await db
|
||||
// .select()
|
||||
// .from(voteDeprecated)
|
||||
// .where(inArray(voteDeprecated.chatId, chatIds));
|
||||
|
||||
for (const chat of chatBatch) {
|
||||
processedCount++;
|
||||
console.info(`Processed ${processedCount}/${chats.length} chats`);
|
||||
// const newMessagesToInsert: NewMessageInsert[] = [];
|
||||
// const newVotesToInsert: NewVoteInsert[] = [];
|
||||
|
||||
const messages = allMessages
|
||||
.filter((message) => message.chatId === chat.id)
|
||||
.sort((a, b) => {
|
||||
const differenceInTime =
|
||||
new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime();
|
||||
if (differenceInTime !== 0) return differenceInTime;
|
||||
// for (const chat of chatBatch) {
|
||||
// processedCount++;
|
||||
// console.info(`Processed ${processedCount}/${chats.length} chats`);
|
||||
|
||||
return getMessageRank(a) - getMessageRank(b);
|
||||
});
|
||||
// const messages = allMessages
|
||||
// .filter((message) => message.chatId === chat.id)
|
||||
// .sort((a, b) => {
|
||||
// const differenceInTime =
|
||||
// new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime();
|
||||
// if (differenceInTime !== 0) return differenceInTime;
|
||||
|
||||
const votes = allVotes.filter((v) => v.chatId === chat.id);
|
||||
// return getMessageRank(a) - getMessageRank(b);
|
||||
// });
|
||||
|
||||
const messageSection: Array<UIMessage> = [];
|
||||
const messageSections: Array<Array<UIMessage>> = [];
|
||||
// const votes = allVotes.filter((v) => v.chatId === chat.id);
|
||||
|
||||
for (const message of messages) {
|
||||
const { role } = message;
|
||||
// const messageSection: Array<UIMessage> = [];
|
||||
// const messageSections: Array<Array<UIMessage>> = [];
|
||||
|
||||
if (role === 'user' && messageSection.length > 0) {
|
||||
messageSections.push([...messageSection]);
|
||||
messageSection.length = 0;
|
||||
}
|
||||
// for (const message of messages) {
|
||||
// const { role } = message;
|
||||
|
||||
// @ts-expect-error message.content has different type
|
||||
messageSection.push(message);
|
||||
}
|
||||
// if (role === 'user' && messageSection.length > 0) {
|
||||
// messageSections.push([...messageSection]);
|
||||
// messageSection.length = 0;
|
||||
// }
|
||||
|
||||
if (messageSection.length > 0) {
|
||||
messageSections.push([...messageSection]);
|
||||
}
|
||||
// // @ts-expect-error message.content has different type
|
||||
// messageSection.push(message);
|
||||
// }
|
||||
|
||||
for (const section of messageSections) {
|
||||
const [userMessage, ...assistantMessages] = section;
|
||||
// if (messageSection.length > 0) {
|
||||
// messageSections.push([...messageSection]);
|
||||
// }
|
||||
|
||||
const [firstAssistantMessage] = assistantMessages;
|
||||
// for (const section of messageSections) {
|
||||
// const [userMessage, ...assistantMessages] = section;
|
||||
|
||||
try {
|
||||
const uiSection = appendResponseMessages({
|
||||
messages: [userMessage],
|
||||
// @ts-expect-error: message.content has different type
|
||||
responseMessages: assistantMessages,
|
||||
_internal: {
|
||||
currentDate: () => firstAssistantMessage.createdAt ?? new Date(),
|
||||
},
|
||||
});
|
||||
// const [firstAssistantMessage] = assistantMessages;
|
||||
|
||||
const projectedUISection = uiSection
|
||||
.map((message) => {
|
||||
if (message.role === 'user') {
|
||||
return {
|
||||
id: message.id,
|
||||
chatId: chat.id,
|
||||
parts: [{ type: 'text', text: message.content }],
|
||||
role: message.role,
|
||||
createdAt: message.createdAt,
|
||||
attachments: [],
|
||||
} as NewMessageInsert;
|
||||
} else if (message.role === 'assistant') {
|
||||
const cleanParts = sanitizeParts(
|
||||
dedupeParts(message.parts || []),
|
||||
);
|
||||
// try {
|
||||
// const uiSection = appendResponseMessages({
|
||||
// messages: [userMessage],
|
||||
// // @ts-expect-error: message.content has different type
|
||||
// responseMessages: assistantMessages,
|
||||
// _internal: {
|
||||
// currentDate: () => firstAssistantMessage.createdAt ?? new Date(),
|
||||
// },
|
||||
// });
|
||||
|
||||
return {
|
||||
id: message.id,
|
||||
chatId: chat.id,
|
||||
parts: cleanParts,
|
||||
role: message.role,
|
||||
createdAt: message.createdAt,
|
||||
attachments: [],
|
||||
} as NewMessageInsert;
|
||||
}
|
||||
return null;
|
||||
})
|
||||
.filter((msg): msg is NewMessageInsert => msg !== null);
|
||||
// const projectedUISection = uiSection
|
||||
// .map((message) => {
|
||||
// if (message.role === 'user') {
|
||||
// return {
|
||||
// id: message.id,
|
||||
// chatId: chat.id,
|
||||
// parts: [{ type: 'text', text: message.content }],
|
||||
// role: message.role,
|
||||
// createdAt: message.createdAt,
|
||||
// attachments: [],
|
||||
// } as NewMessageInsert;
|
||||
// } else if (message.role === 'assistant') {
|
||||
// const cleanParts = sanitizeParts(
|
||||
// dedupeParts(message.parts || []),
|
||||
// );
|
||||
|
||||
for (const msg of projectedUISection) {
|
||||
newMessagesToInsert.push(msg);
|
||||
// return {
|
||||
// id: message.id,
|
||||
// chatId: chat.id,
|
||||
// parts: cleanParts,
|
||||
// role: message.role,
|
||||
// createdAt: message.createdAt,
|
||||
// attachments: [],
|
||||
// } as NewMessageInsert;
|
||||
// }
|
||||
// return null;
|
||||
// })
|
||||
// .filter((msg): msg is NewMessageInsert => msg !== null);
|
||||
|
||||
if (msg.role === 'assistant') {
|
||||
const voteByMessage = votes.find((v) => v.messageId === msg.id);
|
||||
if (voteByMessage) {
|
||||
newVotesToInsert.push({
|
||||
messageId: msg.id,
|
||||
chatId: msg.chatId,
|
||||
isUpvoted: voteByMessage.isUpvoted,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`Error processing chat ${chat.id}: ${error}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
// for (const msg of projectedUISection) {
|
||||
// newMessagesToInsert.push(msg);
|
||||
|
||||
for (let j = 0; j < newMessagesToInsert.length; j += INSERT_BATCH_SIZE) {
|
||||
const messageBatch = newMessagesToInsert.slice(j, j + INSERT_BATCH_SIZE);
|
||||
if (messageBatch.length > 0) {
|
||||
const validMessageBatch = messageBatch.map((msg) => ({
|
||||
id: msg.id,
|
||||
chatId: msg.chatId,
|
||||
parts: msg.parts,
|
||||
role: msg.role,
|
||||
attachments: msg.attachments,
|
||||
createdAt: msg.createdAt,
|
||||
}));
|
||||
// if (msg.role === 'assistant') {
|
||||
// const voteByMessage = votes.find((v) => v.messageId === msg.id);
|
||||
// if (voteByMessage) {
|
||||
// newVotesToInsert.push({
|
||||
// messageId: msg.id,
|
||||
// chatId: msg.chatId,
|
||||
// isUpvoted: voteByMessage.isUpvoted,
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// } catch (error) {
|
||||
// console.error(`Error processing chat ${chat.id}: ${error}`);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
await db.insert(message).values(validMessageBatch);
|
||||
}
|
||||
}
|
||||
// for (let j = 0; j < newMessagesToInsert.length; j += INSERT_BATCH_SIZE) {
|
||||
// const messageBatch = newMessagesToInsert.slice(j, j + INSERT_BATCH_SIZE);
|
||||
// if (messageBatch.length > 0) {
|
||||
// const validMessageBatch = messageBatch.map((msg) => ({
|
||||
// id: msg.id,
|
||||
// chatId: msg.chatId,
|
||||
// parts: msg.parts,
|
||||
// role: msg.role,
|
||||
// attachments: msg.attachments,
|
||||
// createdAt: msg.createdAt,
|
||||
// }));
|
||||
|
||||
for (let j = 0; j < newVotesToInsert.length; j += INSERT_BATCH_SIZE) {
|
||||
const voteBatch = newVotesToInsert.slice(j, j + INSERT_BATCH_SIZE);
|
||||
if (voteBatch.length > 0) {
|
||||
await db.insert(vote).values(voteBatch);
|
||||
}
|
||||
}
|
||||
}
|
||||
// await db.insert(message).values(validMessageBatch);
|
||||
// }
|
||||
// }
|
||||
|
||||
console.info(`Migration completed: ${processedCount} chats processed`);
|
||||
}
|
||||
// for (let j = 0; j < newVotesToInsert.length; j += INSERT_BATCH_SIZE) {
|
||||
// const voteBatch = newVotesToInsert.slice(j, j + INSERT_BATCH_SIZE);
|
||||
// if (voteBatch.length > 0) {
|
||||
// await db.insert(vote).values(voteBatch);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
migrateMessages()
|
||||
.then(() => {
|
||||
console.info('Script completed successfully');
|
||||
process.exit(0);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Script failed:', error);
|
||||
process.exit(1);
|
||||
});
|
||||
// console.info(`Migration completed: ${processedCount} chats processed`);
|
||||
// }
|
||||
|
||||
// migrateMessages()
|
||||
// .then(() => {
|
||||
// console.info('Script completed successfully');
|
||||
// process.exit(0);
|
||||
// })
|
||||
// .catch((error) => {
|
||||
// console.error('Script failed:', error);
|
||||
// process.exit(1);
|
||||
// });
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ export function generateHashedPassword(password: string) {
|
|||
}
|
||||
|
||||
export function generateDummyPassword() {
|
||||
const password = generateId(12);
|
||||
const password = generateId();
|
||||
const hashedPassword = generateHashedPassword(password);
|
||||
|
||||
return hashedPassword;
|
||||
|
|
|
|||
56
lib/types.ts
56
lib/types.ts
|
|
@ -1 +1,57 @@
|
|||
import { z } from 'zod';
|
||||
import type { getWeather } from './ai/tools/get-weather';
|
||||
import type { createDocument } from './ai/tools/create-document';
|
||||
import type { updateDocument } from './ai/tools/update-document';
|
||||
import type { requestSuggestions } from './ai/tools/request-suggestions';
|
||||
import type { InferUITool, UIMessage } from 'ai';
|
||||
|
||||
import type { ArtifactKind } from '@/components/artifact';
|
||||
import type { Suggestion } from './db/schema';
|
||||
|
||||
export type DataPart = { type: 'append-message'; message: string };
|
||||
|
||||
export const messageMetadataSchema = z.object({
|
||||
createdAt: z.string(),
|
||||
});
|
||||
|
||||
export type MessageMetadata = z.infer<typeof messageMetadataSchema>;
|
||||
|
||||
type weatherTool = InferUITool<typeof getWeather>;
|
||||
type createDocumentTool = InferUITool<ReturnType<typeof createDocument>>;
|
||||
type updateDocumentTool = InferUITool<ReturnType<typeof updateDocument>>;
|
||||
type requestSuggestionsTool = InferUITool<
|
||||
ReturnType<typeof requestSuggestions>
|
||||
>;
|
||||
|
||||
export type ChatTools = {
|
||||
getWeather: weatherTool;
|
||||
createDocument: createDocumentTool;
|
||||
updateDocument: updateDocumentTool;
|
||||
requestSuggestions: requestSuggestionsTool;
|
||||
};
|
||||
|
||||
export type CustomUIDataTypes = {
|
||||
textDelta: string;
|
||||
imageDelta: string;
|
||||
sheetDelta: string;
|
||||
codeDelta: string;
|
||||
suggestion: Suggestion;
|
||||
appendMessage: string;
|
||||
id: string;
|
||||
title: string;
|
||||
kind: ArtifactKind;
|
||||
clear: null;
|
||||
finish: null;
|
||||
};
|
||||
|
||||
export type ChatMessage = UIMessage<
|
||||
MessageMetadata,
|
||||
CustomUIDataTypes,
|
||||
ChatTools
|
||||
>;
|
||||
|
||||
export interface Attachment {
|
||||
name: string;
|
||||
url: string;
|
||||
contentType: string;
|
||||
}
|
||||
|
|
|
|||
29
lib/utils.ts
29
lib/utils.ts
|
|
@ -1,8 +1,15 @@
|
|||
import type { CoreAssistantMessage, CoreToolMessage, UIMessage } from 'ai';
|
||||
import type {
|
||||
CoreAssistantMessage,
|
||||
CoreToolMessage,
|
||||
UIMessage,
|
||||
UIMessagePart,
|
||||
} from 'ai';
|
||||
import { type ClassValue, clsx } from 'clsx';
|
||||
import { twMerge } from 'tailwind-merge';
|
||||
import type { Document } from '@/lib/db/schema';
|
||||
import type { DBMessage, Document } from '@/lib/db/schema';
|
||||
import { ChatSDKError, type ErrorCode } from './errors';
|
||||
import type { ChatMessage, ChatTools, CustomUIDataTypes } from './types';
|
||||
import { formatISO } from 'date-fns';
|
||||
|
||||
export function cn(...inputs: ClassValue[]) {
|
||||
return twMerge(clsx(inputs));
|
||||
|
|
@ -89,3 +96,21 @@ export function getTrailingMessageId({
|
|||
export function sanitizeText(text: string) {
|
||||
return text.replace('<has_function_call>', '');
|
||||
}
|
||||
|
||||
export function convertToUIMessages(messages: DBMessage[]): ChatMessage[] {
|
||||
return messages.map((message) => ({
|
||||
id: message.id,
|
||||
role: message.role as 'user' | 'assistant' | 'system',
|
||||
parts: message.parts as UIMessagePart<CustomUIDataTypes, ChatTools>[],
|
||||
metadata: {
|
||||
createdAt: formatISO(message.createdAt),
|
||||
},
|
||||
}));
|
||||
}
|
||||
|
||||
export function getTextFromMessage(message: ChatMessage): string {
|
||||
return message.parts
|
||||
.filter((part) => part.type === 'text')
|
||||
.map((part) => part.text)
|
||||
.join('');
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue