feat: add message attachments (wip)
This commit is contained in:
@@ -1,21 +1,27 @@
|
||||
import type schema from "#triplit/schema";
|
||||
import type { Entity } from "@triplit/client";
|
||||
import type { ModelMessage } from "ai";
|
||||
import type { FilePart, ImagePart, ModelMessage } from "ai";
|
||||
import { nanoid } from "nanoid";
|
||||
import { decrypt, base64ToUint8Array } from "~/utils/crypto";
|
||||
import { type Result, Ok, Err, attempt } from "~~/types/result";
|
||||
import { assert } from "~~/utils/assert";
|
||||
|
||||
export type BaseMessage = {
|
||||
content: string;
|
||||
fileIds: string[];
|
||||
}
|
||||
|
||||
export type MessageEntity = Entity<typeof schema, 'messages'> & {
|
||||
parts: (Entity<typeof schema, 'message_parts'> & {
|
||||
toolCall: Entity<typeof schema, 'tool_calls'> | null
|
||||
})[] | undefined
|
||||
} & { generation: Entity<typeof schema, 'generations'> | null }
|
||||
& { attachments: Entity<typeof schema, 'attachments'>[] }
|
||||
|
||||
export type Message =
|
||||
MessageEntity & {
|
||||
children: (MessageEntity | undefined)[];
|
||||
};
|
||||
}
|
||||
|
||||
export enum ChatErrorType {
|
||||
NoModel = 0,
|
||||
@@ -67,10 +73,32 @@ export const useChat = (agentId: string) => {
|
||||
messages.forEach((message) => {
|
||||
switch (message.role) {
|
||||
case 'user':
|
||||
const attachments = message.attachments.map(attachment => {
|
||||
if (attachment.mimeType.startsWith('image/')) {
|
||||
return {
|
||||
type: 'image',
|
||||
image: attachment.url,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
type: 'file',
|
||||
data: attachment.url,
|
||||
filename: attachment.name,
|
||||
mediaType: attachment.mimeType,
|
||||
};
|
||||
}) as (FilePart | ImagePart)[];
|
||||
|
||||
marshalledMessages.push({
|
||||
role: 'user',
|
||||
// TODO: when we have images or files, this is where we need to handle them
|
||||
content: message.content,
|
||||
content: [
|
||||
{
|
||||
type: 'text',
|
||||
text: message.content
|
||||
},
|
||||
...attachments,
|
||||
],
|
||||
});
|
||||
break;
|
||||
case 'assistant':
|
||||
@@ -243,7 +271,7 @@ export const useChat = (agentId: string) => {
|
||||
}
|
||||
|
||||
const sendMessage = async (
|
||||
message: string,
|
||||
message: BaseMessage,
|
||||
topic: Entity<typeof schema, 'topics'>,
|
||||
topicMessages: MessageEntity[],
|
||||
agent: Entity<typeof schema, 'agents'>,
|
||||
@@ -257,12 +285,28 @@ export const useChat = (agentId: string) => {
|
||||
}
|
||||
|
||||
const messageId = nanoid();
|
||||
const attachmentsPromise = message.fileIds.map(async fileId => {
|
||||
const file = await triplit.fetchOne(triplit.query('files').Where('id', '=', fileId));
|
||||
assert(file !== null);
|
||||
|
||||
return await triplit.insert('attachments', {
|
||||
userId: user.value!.id,
|
||||
topicId: topic.id,
|
||||
messageId: messageId,
|
||||
fileId: fileId,
|
||||
name: file.name,
|
||||
mimeType: file.mimeType,
|
||||
url: file.url,
|
||||
createdAt: file.createdAt,
|
||||
})!;
|
||||
});
|
||||
|
||||
const newMessage = await triplit.insert('messages', {
|
||||
id: messageId,
|
||||
userId: user.value.id,
|
||||
topicId: topic.id,
|
||||
createdAt: new Date().toISOString(),
|
||||
content: message,
|
||||
content: message.content,
|
||||
role: 'user',
|
||||
}).catch(async error => {
|
||||
console.error('Failed to insert message:', error);
|
||||
@@ -270,6 +314,10 @@ export const useChat = (agentId: string) => {
|
||||
return Err(ChatErrorType.DatabaseOperationFailed);
|
||||
}) as Message;
|
||||
|
||||
const attachments = await Promise.all(attachmentsPromise);
|
||||
|
||||
newMessage.attachments = attachments;
|
||||
|
||||
const messages = marshallMessages(
|
||||
agent,
|
||||
topicMessages.concat(newMessage)
|
||||
|
||||
Reference in New Issue
Block a user