feat: add message attachments (wip)

This commit is contained in:
Zoe
2026-02-28 17:19:48 -06:00
parent 5decf9939b
commit 874f0f7397
16 changed files with 780 additions and 50 deletions
+53 -5
View File
@@ -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)