From 855d8c5eb0e90948def2aa33d8e76da38f209a15 Mon Sep 17 00:00:00 2001 From: starmorph Date: Mon, 19 May 2025 12:29:33 -0700 Subject: [PATCH] Anthropic pdf uploads working w metadata filename --- src/components/thread/index.tsx | 42 ++++++++++++++++++++------------- src/lib/multimodal-utils.ts | 17 ++++++------- 2 files changed, 35 insertions(+), 24 deletions(-) diff --git a/src/components/thread/index.tsx b/src/components/thread/index.tsx index 79198cb..1c0be9e 100644 --- a/src/components/thread/index.tsx +++ b/src/components/thread/index.tsx @@ -40,11 +40,9 @@ import { import { fileToImageBlock, fileToPDFBlock, - toOpenAIImageBlock, toOpenAIPDFBlock, } from "@/lib/multimodal-utils"; import type { Base64ContentBlock } from "@langchain/core/messages"; -import { convertToOpenAIImageBlock } from "@langchain/core/messages"; function StickyToBottomContent(props: { content: ReactNode; @@ -181,12 +179,12 @@ export function Thread() { e.preventDefault(); if (!input.trim() || isLoading) return; setFirstTokenReceived(false); - - // TODO: check configurable object for modelname camelcase or snakecase else do openai format - const isOpenAI = true + + // TODO: check configurable object for modelname camelcase or snakecase else do openai format + const isOpenAI = true; const pdfBlocks = pdfUrlList.map(toOpenAIPDFBlock); - + const newHumanMessage: Message = { id: uuidv4(), type: "human", @@ -223,7 +221,7 @@ export function Thread() { const files = e.target.files; if (files) { const imageBlocks = await Promise.all( - Array.from(files).map(fileToImageBlock) + Array.from(files).map(fileToImageBlock), ); setImageUrlList((prev) => [...prev, ...imageBlocks]); } @@ -234,7 +232,7 @@ export function Thread() { const files = e.target.files; if (files) { const pdfBlocks = await Promise.all( - Array.from(files).map(fileToPDFBlock) + Array.from(files).map(fileToPDFBlock), ); setPdfUrlList((prev) => [...prev, ...pdfBlocks]); } @@ -276,25 +274,26 @@ export function Thread() { const imageFiles = files.filter((file) => file.type.startsWith("image/")); const pdfFiles = files.filter((file) => file.type === "application/pdf"); const invalidFiles = files.filter( - (file) => !file.type.startsWith("image/") && file.type !== "application/pdf" + (file) => + !file.type.startsWith("image/") && file.type !== "application/pdf", ); if (invalidFiles.length > 0) { toast.error( - "You have uploaded invalid file type. Please upload an image or a PDF." + "You have uploaded invalid file type. Please upload an image or a PDF.", ); } if (imageFiles.length) { const imageBlocks: Base64ContentBlock[] = await Promise.all( - imageFiles.map(fileToImageBlock) + imageFiles.map(fileToImageBlock), ); setImageUrlList((prev) => [...prev, ...imageBlocks]); } if (pdfFiles.length) { const pdfBlocks: Base64ContentBlock[] = await Promise.all( - pdfFiles.map(fileToPDFBlock) + pdfFiles.map(fileToPDFBlock), ); setPdfUrlList((prev) => [...prev, ...pdfBlocks]); } @@ -520,7 +519,10 @@ export function Thread() { {imageUrlList.map((imageBlock, idx) => { const imageUrlString = `data:${imageBlock.mime_type};base64,${imageBlock.data}`; return ( -
+
uploaded - setImageUrlList(imageUrlList.filter((_, i) => i !== idx)) + setImageUrlList( + imageUrlList.filter((_, i) => i !== idx), + ) } />
@@ -545,12 +549,18 @@ export function Thread() { key={idx} > - {String(pdfBlock.metadata?.filename ?? pdfBlock.metadata?.name ?? "")} + {String( + pdfBlock.metadata?.filename ?? + pdfBlock.metadata?.name ?? + "", + )} - setPdfUrlList(pdfUrlList.filter((_, i) => i !== idx)) + setPdfUrlList( + pdfUrlList.filter((_, i) => i !== idx), + ) } />
diff --git a/src/lib/multimodal-utils.ts b/src/lib/multimodal-utils.ts index b6360a3..8cd2c9b 100644 --- a/src/lib/multimodal-utils.ts +++ b/src/lib/multimodal-utils.ts @@ -1,6 +1,5 @@ import type { Base64ContentBlock } from "@langchain/core/messages"; import { convertToOpenAIImageBlock } from "@langchain/core/messages"; -import { v4 as uuidv4 } from "uuid"; // Returns a Promise of a typed multimodal block for images export async function fileToImageBlock( @@ -24,18 +23,20 @@ export async function fileToPDFBlock(file: File): Promise { source_type: "base64", mime_type: "application/pdf", data, - metadata: { name: file.name, filename: file.name }, + metadata: { filename: file.name }, }; } // in lib/multimodal-utils.ts -export function toOpenAIPDFBlock(block: Base64ContentBlock) { +export function toOpenAIPDFBlock( + block: Base64ContentBlock, +): Base64ContentBlock { return { - type: "file", - source_type: "base64", - data: block.data, - mime_type: block.mime_type ?? "application/pdf", - filename: block.metadata?.name ?? block.metadata?.filename ?? "file.pdf", + type: "file", + source_type: "base64", + data: block.data, + mime_type: block.mime_type ?? "application/pdf", + metadata: { filename: block.metadata?.filename ?? "file.pdf" }, }; }