Anthropic pdf uploads working w metadata filename
This commit is contained in:
@@ -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 (
|
||||
<div className="relative" key={idx}>
|
||||
<div
|
||||
className="relative"
|
||||
key={idx}
|
||||
>
|
||||
<img
|
||||
src={imageUrlString}
|
||||
alt="uploaded"
|
||||
@@ -529,7 +531,9 @@ export function Thread() {
|
||||
<CircleX
|
||||
className="absolute top-[2px] right-[2px] size-4 cursor-pointer rounded-full bg-gray-500 text-white"
|
||||
onClick={() =>
|
||||
setImageUrlList(imageUrlList.filter((_, i) => i !== idx))
|
||||
setImageUrlList(
|
||||
imageUrlList.filter((_, i) => i !== idx),
|
||||
)
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
@@ -545,12 +549,18 @@ export function Thread() {
|
||||
key={idx}
|
||||
>
|
||||
<span className="max-w-xs truncate text-sm">
|
||||
{String(pdfBlock.metadata?.filename ?? pdfBlock.metadata?.name ?? "")}
|
||||
{String(
|
||||
pdfBlock.metadata?.filename ??
|
||||
pdfBlock.metadata?.name ??
|
||||
"",
|
||||
)}
|
||||
</span>
|
||||
<CircleX
|
||||
className="size-4 cursor-pointer text-teal-600 hover:text-teal-500"
|
||||
onClick={() =>
|
||||
setPdfUrlList(pdfUrlList.filter((_, i) => i !== idx))
|
||||
setPdfUrlList(
|
||||
pdfUrlList.filter((_, i) => i !== idx),
|
||||
)
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -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<Base64ContentBlock> {
|
||||
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" },
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user