mirror of
https://github.com/run-llama/LlamaIndexTS.git
synced 2026-07-16 07:14:29 -04:00
fix: Use Uint8Array instead of Buffer for file type messages (works w… (#1921)
This commit is contained in:
@@ -0,0 +1,10 @@
|
||||
---
|
||||
"@llamaindex/anthropic": patch
|
||||
"@llamaindex/google": patch
|
||||
"@llamaindex/openai": patch
|
||||
"@llamaindex/core": patch
|
||||
"@llamaindex/env": patch
|
||||
"@llamaindex/examples": patch
|
||||
---
|
||||
|
||||
Use Uint8Array instead of Buffer for file type messages (works with non-NodeJS)
|
||||
@@ -25,7 +25,7 @@ async function main() {
|
||||
},
|
||||
{
|
||||
type: "file",
|
||||
data: fs.readFileSync("./data/manga.pdf"),
|
||||
data: Uint8Array.from(fs.readFileSync("./data/manga.pdf")),
|
||||
mimeType: "application/pdf",
|
||||
},
|
||||
],
|
||||
|
||||
@@ -32,7 +32,7 @@ import fs from "fs";
|
||||
},
|
||||
{
|
||||
type: "file",
|
||||
data: fs.readFileSync("./data/manga.pdf"),
|
||||
data: Uint8Array.from(fs.readFileSync("./data/manga.pdf")),
|
||||
mimeType: "application/pdf",
|
||||
},
|
||||
],
|
||||
|
||||
@@ -26,7 +26,7 @@ import fs from "fs";
|
||||
},
|
||||
{
|
||||
type: "file",
|
||||
data: fs.readFileSync("./data/manga.pdf"),
|
||||
data: new Uint8Array(fs.readFileSync("./data/manga.pdf")),
|
||||
mimeType: "application/pdf",
|
||||
},
|
||||
],
|
||||
|
||||
@@ -21,7 +21,7 @@ async function main() {
|
||||
},
|
||||
{
|
||||
type: "file",
|
||||
data: fs.readFileSync("./data/manga.pdf"),
|
||||
data: Uint8Array.from(fs.readFileSync("./data/manga.pdf")),
|
||||
mimeType: "application/pdf",
|
||||
},
|
||||
],
|
||||
|
||||
@@ -165,7 +165,7 @@ export type MessageContentImageDetail = {
|
||||
|
||||
export type MessageContentFileDetail = {
|
||||
type: "file";
|
||||
data: Buffer;
|
||||
data: Uint8Array;
|
||||
mimeType: string;
|
||||
};
|
||||
|
||||
|
||||
Vendored
+2
-1
@@ -6,7 +6,8 @@
|
||||
import "./global-check.js";
|
||||
|
||||
export * from "./als/index.web.js";
|
||||
export { consoleLogger, emptyLogger, type Logger } from "./logger/index.js";
|
||||
export * from "./logger/index.js";
|
||||
export * from "./utils/base64.js";
|
||||
export { NotSupportCurrentRuntimeClass } from "./utils/shared.js";
|
||||
export * from "./web-polyfill.js";
|
||||
if (typeof window === "undefined") {
|
||||
|
||||
Vendored
+2
-1
@@ -5,6 +5,7 @@
|
||||
*/
|
||||
|
||||
export * from "./als/index.non-node.js";
|
||||
export { consoleLogger, emptyLogger, type Logger } from "./logger/index.js";
|
||||
export * from "./logger/index.js";
|
||||
export * from "./node-polyfill.js";
|
||||
export * from "./utils/base64.js";
|
||||
export { NotSupportCurrentRuntimeClass } from "./utils/shared.js";
|
||||
|
||||
Vendored
+2
-1
@@ -37,7 +37,8 @@ export function createSHA256(): SHA256 {
|
||||
export const process = globalThis.process;
|
||||
|
||||
export * from "./als/index.node.js";
|
||||
export { consoleLogger, emptyLogger, type Logger } from "./logger/index.js";
|
||||
export * from "./logger/index.js";
|
||||
export * from "./utils/base64.js";
|
||||
export { CustomEvent, getEnv, setEnvs } from "./utils/index.js";
|
||||
export { NotSupportCurrentRuntimeClass } from "./utils/shared.js";
|
||||
export {
|
||||
|
||||
Vendored
+2
-1
@@ -11,9 +11,10 @@ export * from "./als/index.workerd.js";
|
||||
export { NotSupportCurrentRuntimeClass } from "./utils/shared.js";
|
||||
|
||||
export * from "./node-polyfill.js";
|
||||
export * from "./utils/base64.js";
|
||||
|
||||
export function getEnv(name: string): string | undefined {
|
||||
return INTERNAL_ENV[name];
|
||||
}
|
||||
|
||||
export { consoleLogger, emptyLogger, type Logger } from "./logger/index.js";
|
||||
export * from "./logger/index.js";
|
||||
|
||||
Vendored
+38
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* Converts a Uint8Array to a base64 string.
|
||||
* For large arrays, it processes the data in chunks to avoid memory issues.
|
||||
* Falls back to Buffer if available for better performance.
|
||||
*
|
||||
* @param bytes - The Uint8Array to convert
|
||||
* @returns The base64 string representation
|
||||
*/
|
||||
export function uint8ArrayToBase64(bytes: Uint8Array): string {
|
||||
// Use Buffer if available (Node.js environment)
|
||||
if (typeof Buffer !== "undefined") {
|
||||
return Buffer.from(bytes).toString("base64");
|
||||
}
|
||||
|
||||
// For browsers and other environments without Buffer
|
||||
// Process in chunks for large arrays to avoid memory issues
|
||||
const CHUNK_SIZE = 32768; // 32KB chunks
|
||||
let result = "";
|
||||
|
||||
// For small arrays, use the built-in btoa function directly
|
||||
if (bytes.length < CHUNK_SIZE) {
|
||||
const binary = Array.from(bytes)
|
||||
.map((byte) => String.fromCharCode(byte))
|
||||
.join("");
|
||||
return globalThis.btoa(binary);
|
||||
}
|
||||
|
||||
// For large arrays, process in chunks
|
||||
for (let i = 0; i < bytes.length; i += CHUNK_SIZE) {
|
||||
const chunk = bytes.subarray(i, i + CHUNK_SIZE);
|
||||
const binary = Array.from(chunk)
|
||||
.map((byte) => String.fromCharCode(byte))
|
||||
.join("");
|
||||
result += globalThis.btoa(binary);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -28,7 +28,7 @@ import type {
|
||||
} from "@llamaindex/core/llms";
|
||||
import { ToolCallLLM } from "@llamaindex/core/llms";
|
||||
import { extractText } from "@llamaindex/core/utils";
|
||||
import { getEnv } from "@llamaindex/env";
|
||||
import { getEnv, uint8ArrayToBase64 } from "@llamaindex/env";
|
||||
import { isDeepEqual } from "remeda";
|
||||
|
||||
export class AnthropicSession {
|
||||
@@ -332,7 +332,7 @@ export class Anthropic extends ToolCallLLM<
|
||||
source: {
|
||||
type: "base64" as const,
|
||||
media_type: content.mimeType,
|
||||
data: content.data.toString("base64"),
|
||||
data: uint8ArrayToBase64(content.data),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -289,7 +289,7 @@ export class GeminiHelper {
|
||||
if (fileContents.length > 0) {
|
||||
for (const file of fileContents) {
|
||||
const uploadResponse = await GeminiHelper.uploadFile(
|
||||
file.data,
|
||||
Buffer.from(file.data),
|
||||
file.mimeType,
|
||||
);
|
||||
parts.push({
|
||||
|
||||
@@ -13,7 +13,7 @@ import {
|
||||
type ToolCallLLMMessageOptions,
|
||||
} from "@llamaindex/core/llms";
|
||||
import { extractText } from "@llamaindex/core/utils";
|
||||
import { getEnv } from "@llamaindex/env";
|
||||
import { getEnv, uint8ArrayToBase64 } from "@llamaindex/env";
|
||||
import { Tokenizers } from "@llamaindex/env/tokenizers";
|
||||
import type {
|
||||
AzureClientOptions,
|
||||
@@ -217,10 +217,11 @@ export class OpenAI extends ToolCallLLM<OpenAIAdditionalChatOptions> {
|
||||
if (item.mimeType !== "application/pdf") {
|
||||
throw new Error("Only PDF files are supported");
|
||||
}
|
||||
const base64Data = uint8ArrayToBase64(item.data);
|
||||
return {
|
||||
type: "file",
|
||||
file: {
|
||||
file_data: `data:${item.mimeType};base64,${item.data.toString("base64")}`,
|
||||
file_data: `data:${item.mimeType};base64,${base64Data}`,
|
||||
filename: `part-${index}.pdf`,
|
||||
},
|
||||
} satisfies ChatCompletionContentPart.File;
|
||||
|
||||
@@ -16,7 +16,7 @@ import {
|
||||
} from "@llamaindex/core/llms";
|
||||
import type { StoredValue } from "@llamaindex/core/schema";
|
||||
import { extractText } from "@llamaindex/core/utils";
|
||||
import { getEnv } from "@llamaindex/env";
|
||||
import { getEnv, uint8ArrayToBase64 } from "@llamaindex/env";
|
||||
import {
|
||||
OpenAI as OpenAILLM,
|
||||
type AzureClientOptions,
|
||||
@@ -703,10 +703,11 @@ export class OpenAIResponses extends ToolCallLLM<OpenAIResponsesChatOptions> {
|
||||
);
|
||||
}
|
||||
|
||||
const base64Data = uint8ArrayToBase64(item.data);
|
||||
return {
|
||||
type: "input_file",
|
||||
filename: `part-${index}.pdf`,
|
||||
file_data: `data:${item.mimeType};base64,${item.data.toString("base64")}`,
|
||||
file_data: `data:${item.mimeType};base64,${base64Data}`,
|
||||
};
|
||||
}
|
||||
throw new Error("Unsupported content type");
|
||||
|
||||
@@ -205,7 +205,7 @@ describe("OpenAIResponses Unit Tests", () => {
|
||||
{
|
||||
type: "file",
|
||||
mimeType: "image/jpeg",
|
||||
data: Buffer.from("test image content"),
|
||||
data: Uint8Array.from(Buffer.from("test image content")),
|
||||
},
|
||||
];
|
||||
// @ts-expect-error accessing private method
|
||||
|
||||
@@ -2,7 +2,9 @@ import { describe, expect, test } from "vitest";
|
||||
import { duckduckgo, type DuckDuckGoToolOutput } from "../src/tools/duckduckgo";
|
||||
|
||||
describe("DuckDuckGo Tool", () => {
|
||||
test("performs search and returns results", async () => {
|
||||
// Needs to be skipped: duck-duck-scrape@2.2.7 throws an error:
|
||||
// DDG detected an anomaly in the request, you are likely making requests too quickly.
|
||||
test.skip("performs search and returns results", async () => {
|
||||
const searchTool = duckduckgo();
|
||||
const results = (await searchTool.call({
|
||||
query: "OpenAI ChatGPT",
|
||||
|
||||
Reference in New Issue
Block a user