Add message actions (#482)
This commit is contained in:
parent
94f563f179
commit
171914941e
20 changed files with 1011 additions and 150 deletions
35
lib/drizzle/0002_wandering_riptide.sql
Normal file
35
lib/drizzle/0002_wandering_riptide.sql
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
CREATE TABLE IF NOT EXISTS "Message" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"chatId" uuid NOT NULL,
|
||||
"role" varchar NOT NULL,
|
||||
"content" json NOT NULL,
|
||||
"createdAt" timestamp NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE IF NOT EXISTS "Vote" (
|
||||
"chatId" uuid NOT NULL,
|
||||
"messageId" uuid NOT NULL,
|
||||
"isUpvoted" boolean NOT NULL,
|
||||
CONSTRAINT "Vote_chatId_messageId_pk" PRIMARY KEY("chatId","messageId")
|
||||
);
|
||||
--> statement-breakpoint
|
||||
ALTER TABLE "Chat" ADD COLUMN "title" text NOT NULL;--> statement-breakpoint
|
||||
DO $$ BEGIN
|
||||
ALTER TABLE "Message" ADD CONSTRAINT "Message_chatId_Chat_id_fk" FOREIGN KEY ("chatId") REFERENCES "public"."Chat"("id") ON DELETE no action ON UPDATE no action;
|
||||
EXCEPTION
|
||||
WHEN duplicate_object THEN null;
|
||||
END $$;
|
||||
--> statement-breakpoint
|
||||
DO $$ BEGIN
|
||||
ALTER TABLE "Vote" ADD CONSTRAINT "Vote_chatId_Chat_id_fk" FOREIGN KEY ("chatId") REFERENCES "public"."Chat"("id") ON DELETE no action ON UPDATE no action;
|
||||
EXCEPTION
|
||||
WHEN duplicate_object THEN null;
|
||||
END $$;
|
||||
--> statement-breakpoint
|
||||
DO $$ BEGIN
|
||||
ALTER TABLE "Vote" ADD CONSTRAINT "Vote_messageId_Message_id_fk" FOREIGN KEY ("messageId") REFERENCES "public"."Message"("id") ON DELETE no action ON UPDATE no action;
|
||||
EXCEPTION
|
||||
WHEN duplicate_object THEN null;
|
||||
END $$;
|
||||
--> statement-breakpoint
|
||||
ALTER TABLE "Chat" DROP COLUMN IF EXISTS "messages";
|
||||
377
lib/drizzle/meta/0002_snapshot.json
Normal file
377
lib/drizzle/meta/0002_snapshot.json
Normal file
|
|
@ -0,0 +1,377 @@
|
|||
{
|
||||
"id": "b5d8e862-936f-4419-a50f-97be3e7fe665",
|
||||
"prevId": "f3d3437c-4735-4c91-80af-1014048a904e",
|
||||
"version": "7",
|
||||
"dialect": "postgresql",
|
||||
"tables": {
|
||||
"public.Suggestion": {
|
||||
"name": "Suggestion",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "uuid",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "gen_random_uuid()"
|
||||
},
|
||||
"documentId": {
|
||||
"name": "documentId",
|
||||
"type": "uuid",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"documentCreatedAt": {
|
||||
"name": "documentCreatedAt",
|
||||
"type": "timestamp",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"originalText": {
|
||||
"name": "originalText",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"suggestedText": {
|
||||
"name": "suggestedText",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"description": {
|
||||
"name": "description",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"isResolved": {
|
||||
"name": "isResolved",
|
||||
"type": "boolean",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": false
|
||||
},
|
||||
"userId": {
|
||||
"name": "userId",
|
||||
"type": "uuid",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"createdAt": {
|
||||
"name": "createdAt",
|
||||
"type": "timestamp",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {
|
||||
"Suggestion_userId_User_id_fk": {
|
||||
"name": "Suggestion_userId_User_id_fk",
|
||||
"tableFrom": "Suggestion",
|
||||
"tableTo": "User",
|
||||
"columnsFrom": [
|
||||
"userId"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
},
|
||||
"Suggestion_documentId_documentCreatedAt_Document_id_createdAt_fk": {
|
||||
"name": "Suggestion_documentId_documentCreatedAt_Document_id_createdAt_fk",
|
||||
"tableFrom": "Suggestion",
|
||||
"tableTo": "Document",
|
||||
"columnsFrom": [
|
||||
"documentId",
|
||||
"documentCreatedAt"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id",
|
||||
"createdAt"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {
|
||||
"Suggestion_id_pk": {
|
||||
"name": "Suggestion_id_pk",
|
||||
"columns": [
|
||||
"id"
|
||||
]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {}
|
||||
},
|
||||
"public.Chat": {
|
||||
"name": "Chat",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "uuid",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"default": "gen_random_uuid()"
|
||||
},
|
||||
"createdAt": {
|
||||
"name": "createdAt",
|
||||
"type": "timestamp",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"title": {
|
||||
"name": "title",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"userId": {
|
||||
"name": "userId",
|
||||
"type": "uuid",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {
|
||||
"Chat_userId_User_id_fk": {
|
||||
"name": "Chat_userId_User_id_fk",
|
||||
"tableFrom": "Chat",
|
||||
"tableTo": "User",
|
||||
"columnsFrom": [
|
||||
"userId"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {}
|
||||
},
|
||||
"public.Document": {
|
||||
"name": "Document",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "uuid",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "gen_random_uuid()"
|
||||
},
|
||||
"createdAt": {
|
||||
"name": "createdAt",
|
||||
"type": "timestamp",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"title": {
|
||||
"name": "title",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"content": {
|
||||
"name": "content",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"userId": {
|
||||
"name": "userId",
|
||||
"type": "uuid",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {
|
||||
"Document_userId_User_id_fk": {
|
||||
"name": "Document_userId_User_id_fk",
|
||||
"tableFrom": "Document",
|
||||
"tableTo": "User",
|
||||
"columnsFrom": [
|
||||
"userId"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {
|
||||
"Document_id_createdAt_pk": {
|
||||
"name": "Document_id_createdAt_pk",
|
||||
"columns": [
|
||||
"id",
|
||||
"createdAt"
|
||||
]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {}
|
||||
},
|
||||
"public.Message": {
|
||||
"name": "Message",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "uuid",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"default": "gen_random_uuid()"
|
||||
},
|
||||
"chatId": {
|
||||
"name": "chatId",
|
||||
"type": "uuid",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"role": {
|
||||
"name": "role",
|
||||
"type": "varchar",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"content": {
|
||||
"name": "content",
|
||||
"type": "json",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"createdAt": {
|
||||
"name": "createdAt",
|
||||
"type": "timestamp",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {
|
||||
"Message_chatId_Chat_id_fk": {
|
||||
"name": "Message_chatId_Chat_id_fk",
|
||||
"tableFrom": "Message",
|
||||
"tableTo": "Chat",
|
||||
"columnsFrom": [
|
||||
"chatId"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {}
|
||||
},
|
||||
"public.User": {
|
||||
"name": "User",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "uuid",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"default": "gen_random_uuid()"
|
||||
},
|
||||
"email": {
|
||||
"name": "email",
|
||||
"type": "varchar(64)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"password": {
|
||||
"name": "password",
|
||||
"type": "varchar(64)",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {}
|
||||
},
|
||||
"public.Vote": {
|
||||
"name": "Vote",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"chatId": {
|
||||
"name": "chatId",
|
||||
"type": "uuid",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"messageId": {
|
||||
"name": "messageId",
|
||||
"type": "uuid",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"isUpvoted": {
|
||||
"name": "isUpvoted",
|
||||
"type": "boolean",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {
|
||||
"Vote_chatId_Chat_id_fk": {
|
||||
"name": "Vote_chatId_Chat_id_fk",
|
||||
"tableFrom": "Vote",
|
||||
"tableTo": "Chat",
|
||||
"columnsFrom": [
|
||||
"chatId"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
},
|
||||
"Vote_messageId_Message_id_fk": {
|
||||
"name": "Vote_messageId_Message_id_fk",
|
||||
"tableFrom": "Vote",
|
||||
"tableTo": "Message",
|
||||
"columnsFrom": [
|
||||
"messageId"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {
|
||||
"Vote_chatId_messageId_pk": {
|
||||
"name": "Vote_chatId_messageId_pk",
|
||||
"columns": [
|
||||
"chatId",
|
||||
"messageId"
|
||||
]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {}
|
||||
}
|
||||
},
|
||||
"enums": {},
|
||||
"schemas": {},
|
||||
"sequences": {},
|
||||
"_meta": {
|
||||
"columns": {},
|
||||
"schemas": {},
|
||||
"tables": {}
|
||||
}
|
||||
}
|
||||
|
|
@ -15,6 +15,13 @@
|
|||
"when": 1730207363999,
|
||||
"tag": "0001_sparkling_blue_marvel",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 2,
|
||||
"version": "7",
|
||||
"when": 1730725226313,
|
||||
"tag": "0002_wandering_riptide",
|
||||
"breakpoints": true
|
||||
}
|
||||
]
|
||||
}
|
||||
47
lib/utils.ts
47
lib/utils.ts
|
|
@ -2,14 +2,13 @@ import {
|
|||
CoreAssistantMessage,
|
||||
CoreMessage,
|
||||
CoreToolMessage,
|
||||
generateId,
|
||||
Message,
|
||||
ToolInvocation,
|
||||
} from 'ai';
|
||||
import { clsx, type ClassValue } from 'clsx';
|
||||
import { twMerge } from 'tailwind-merge';
|
||||
|
||||
import { Chat, Document } from '@/db/schema';
|
||||
import { Message as DBMessage, Document } from '@/db/schema';
|
||||
|
||||
export function cn(...inputs: ClassValue[]) {
|
||||
return twMerge(clsx(inputs));
|
||||
|
|
@ -86,7 +85,7 @@ function addToolMessageToChat({
|
|||
}
|
||||
|
||||
export function convertToUIMessages(
|
||||
messages: Array<CoreMessage>
|
||||
messages: Array<DBMessage>
|
||||
): Array<Message> {
|
||||
return messages.reduce((chatMessages: Array<Message>, message) => {
|
||||
if (message.role === 'tool') {
|
||||
|
|
@ -117,8 +116,8 @@ export function convertToUIMessages(
|
|||
}
|
||||
|
||||
chatMessages.push({
|
||||
id: generateId(),
|
||||
role: message.role,
|
||||
id: message.id,
|
||||
role: message.role as Message['role'],
|
||||
content: textContent,
|
||||
toolInvocations,
|
||||
});
|
||||
|
|
@ -127,29 +126,6 @@ export function convertToUIMessages(
|
|||
}, []);
|
||||
}
|
||||
|
||||
export function getTitleFromChat(chat: Chat) {
|
||||
const messages = convertToUIMessages(chat.messages as Array<CoreMessage>);
|
||||
const firstMessage = messages[0];
|
||||
|
||||
if (!firstMessage) {
|
||||
return 'Untitled';
|
||||
}
|
||||
|
||||
return firstMessage.content;
|
||||
}
|
||||
|
||||
const emptyAssistantMessage = [
|
||||
{
|
||||
role: 'assistant',
|
||||
content: [
|
||||
{
|
||||
type: 'text',
|
||||
text: '',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
export function sanitizeResponseMessages(
|
||||
messages: Array<CoreToolMessage | CoreAssistantMessage>
|
||||
): Array<CoreToolMessage | CoreAssistantMessage> {
|
||||
|
|
@ -222,6 +198,11 @@ export function sanitizeUIMessages(messages: Array<Message>): Array<Message> {
|
|||
);
|
||||
}
|
||||
|
||||
export function getMostRecentUserMessage(messages: Array<CoreMessage>) {
|
||||
const userMessages = messages.filter((message) => message.role === 'user');
|
||||
return userMessages.at(-1);
|
||||
}
|
||||
|
||||
export function getDocumentTimestampByIndex(
|
||||
documents: Array<Document>,
|
||||
index: number
|
||||
|
|
@ -231,3 +212,13 @@ export function getDocumentTimestampByIndex(
|
|||
|
||||
return documents[index].createdAt;
|
||||
}
|
||||
|
||||
export function getMessageIdFromAnnotations(message: Message) {
|
||||
if (!message.annotations) return message.id;
|
||||
|
||||
const [annotation] = message.annotations;
|
||||
if (!annotation) return message.id;
|
||||
|
||||
// @ts-expect-error messageIdFromServer is not defined in MessageAnnotation
|
||||
return annotation.messageIdFromServer;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue