- {@html content}
+
{#if message.references.length > 0}
@@ -383,16 +385,20 @@
max-width: 100%;
}
- .message-text-user {
+ .message-text-user-container {
max-height: 15vh;
overflow: scroll;
padding-top: var(--size-4-2);
white-space: pre-wrap;
}
- .message-text-user::-webkit-scrollbar {
+ .message-text-user-container::-webkit-scrollbar {
display: none;
}
+
+ .message-text-user-container {
+ padding-bottom: var(--size-4-2);
+ }
.conversation-empty-state {
margin: auto;
diff --git a/Conversations/Conversation.ts b/Conversations/Conversation.ts
index 4bf3d04..e49d661 100644
--- a/Conversations/Conversation.ts
+++ b/Conversations/Conversation.ts
@@ -6,6 +6,7 @@ import { Role } from "Enums/Role";
import { AITool } from "Enums/AITool";
import { isTextFile, toFileType } from "Enums/FileType";
import { FileTypeToMimeType } from "Enums/FileTypeMimeTypeMapping";
+import { isDocumentMimeType, MimeType } from "Enums/MimeType";
export class Conversation {
@@ -73,7 +74,7 @@ export class Conversation {
name: functionResponse.name,
response: {
results: responseResults,
- ...(binaryResults.length > 0 && {message: "Binary files follow in next message"})
+ ...(binaryResults.length > 0 && {message: "The contents of the files are provided below."})
}
}
};
@@ -84,7 +85,7 @@ export class Conversation {
functionResponse: {
name: functionResponse.name,
response: {
- message: "Files retrieved successfully. Binary content follows in next message.",
+ message: "Files retrieved successfully. The contents of the files are provided below.",
count: binaryResults.length
}
}
@@ -104,7 +105,12 @@ export class Conversation {
.filter(file => file.type && file.contents !== undefined)
.map(file => {
const fileName = file.path.split('/').pop() || file.path;
- const mimeType = FileTypeToMimeType[toFileType(file.type as string)];
+ let mimeType = FileTypeToMimeType[toFileType(file.type as string)];
+
+ if (isDocumentMimeType(mimeType)) {
+ mimeType = MimeType.TEXT_PLAIN;
+ return new Attachment(fileName, mimeType, StringTools.toBase64(file.contents as string));
+ }
return new Attachment(fileName, mimeType, file.contents as string);
});
diff --git a/Enums/FileType.ts b/Enums/FileType.ts
index 465c6b7..2e10c2c 100644
--- a/Enums/FileType.ts
+++ b/Enums/FileType.ts
@@ -136,6 +136,14 @@ export enum FileType {
DIFF = "diff",
PATCH = "patch",
+ // Document formats
+ DOCX = "docx",
+ PPTX = "pptx",
+ XLSX = "xlsx",
+ ODT = "odt",
+ ODP = "odp",
+ ODS = "ods",
+
// Logs
LOG = "log",
@@ -158,7 +166,16 @@ export function isFileType(value: unknown, fileType: FileType): value is FileTyp
}
export function isBinaryFile(extension: string) {
- return isKnownFileType(extension) && !isTextFile(extension);
+ return isKnownFileType(extension) && !isTextFile(extension) && !isDocumentFile(extension);
+}
+
+export function isDocumentFile(extension: string) {
+ return isFileType(extension, FileType.DOCX)
+ || isFileType(extension, FileType.PPTX)
+ || isFileType(extension, FileType.XLSX)
+ || isFileType(extension, FileType.ODT)
+ || isFileType(extension, FileType.ODP)
+ || isFileType(extension, FileType.ODS);
}
export function isTextFile(extension: string) {
diff --git a/Enums/FileTypeMimeTypeMapping.ts b/Enums/FileTypeMimeTypeMapping.ts
index 75f1239..6f6ba4a 100644
--- a/Enums/FileTypeMimeTypeMapping.ts
+++ b/Enums/FileTypeMimeTypeMapping.ts
@@ -143,6 +143,14 @@ export const FileTypeToMimeType: Record
= {
[FileType.DIFF]: MimeType.TEXT_PLAIN,
[FileType.PATCH]: MimeType.TEXT_PLAIN,
+ // Document formats
+ [FileType.DOCX]: MimeType.APPLICATION_DOCX,
+ [FileType.PPTX]: MimeType.APPLICATION_PPTX,
+ [FileType.XLSX]: MimeType.APPLICATION_XLSX,
+ [FileType.ODT]: MimeType.APPLICATION_ODT,
+ [FileType.ODP]: MimeType.APPLICATION_ODP,
+ [FileType.ODS]: MimeType.APPLICATION_ODS,
+
// Logs
[FileType.LOG]: MimeType.TEXT_PLAIN,
@@ -245,5 +253,13 @@ export const MimeTypeToFileTypes: Record = {
[MimeType.VIDEO_OGG]: [FileType.OGV],
[MimeType.VIDEO_WEBM]: [FileType.WEBM, FileType.WEBM_VIDEO],
+ // Office document formats
+ [MimeType.APPLICATION_DOCX]: [FileType.DOCX],
+ [MimeType.APPLICATION_PPTX]: [FileType.PPTX],
+ [MimeType.APPLICATION_XLSX]: [FileType.XLSX],
+ [MimeType.APPLICATION_ODT]: [FileType.ODT],
+ [MimeType.APPLICATION_ODP]: [FileType.ODP],
+ [MimeType.APPLICATION_ODS]: [FileType.ODS],
+
[MimeType.UNKNOWN]: [FileType.UNKNOWN]
};
diff --git a/Enums/MimeType.ts b/Enums/MimeType.ts
index d7908df..b125409 100644
--- a/Enums/MimeType.ts
+++ b/Enums/MimeType.ts
@@ -63,6 +63,14 @@ export enum MimeType {
APPLICATION_TYPESCRIPT = "application/x-typescript",
APPLICATION_SH = "application/x-sh",
+ // Office document formats
+ APPLICATION_DOCX = "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
+ APPLICATION_PPTX = "application/vnd.openxmlformats-officedocument.presentationml.presentation",
+ APPLICATION_XLSX = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
+ APPLICATION_ODT = "application/vnd.oasis.opendocument.text",
+ APPLICATION_ODP = "application/vnd.oasis.opendocument.presentation",
+ APPLICATION_ODS = "application/vnd.oasis.opendocument.spreadsheet",
+
// Markup formats
TEXT_RST = "text/x-rst",
TEXT_ASCIIDOC = "text/x-asciidoc",
@@ -108,6 +116,15 @@ export function isKnownMimeType(value: string): value is MimeType {
return Object.values(MimeType).includes(value as MimeType) && value !== MimeType.UNKNOWN.toString();
}
+export function isDocumentMimeType(mimeType: MimeType) {
+ return mimeType === MimeType.APPLICATION_DOCX ||
+ mimeType === MimeType.APPLICATION_PPTX ||
+ mimeType === MimeType.APPLICATION_XLSX ||
+ mimeType === MimeType.APPLICATION_ODT ||
+ mimeType === MimeType.APPLICATION_ODP ||
+ mimeType === MimeType.APPLICATION_ODS;
+}
+
export function isImageMimeType(mimeType: MimeType) {
return mimeType === MimeType.IMAGE_AVIF ||
mimeType === MimeType.IMAGE_BMP ||
diff --git a/Helpers/PDFHelper.ts b/Helpers/DocumentHelper.ts
similarity index 65%
rename from Helpers/PDFHelper.ts
rename to Helpers/DocumentHelper.ts
index 9db6538..0f8bc66 100644
--- a/Helpers/PDFHelper.ts
+++ b/Helpers/DocumentHelper.ts
@@ -1,7 +1,9 @@
import { extractText, getDocumentProxy } from 'unpdf';
import type { IPageText } from '../Types/SearchTypes';
import { Exception } from './Exception';
+import OfficeParser from 'officeparser';
+// Handles PDF format
export async function readPDF(arrayBuffer: ArrayBuffer): Promise {
try {
const pdf = await getDocumentProxy(new Uint8Array(arrayBuffer));
@@ -27,4 +29,20 @@ export async function readPDF(arrayBuffer: ArrayBuffer): Promise {
}
return [{ text: `Failed to read PDF: ${Exception.messageFrom(error)}`, pageNumber: 1 }] as IPageText[];
}
+}
+
+// Handles document formats: DOCX, PPTX, XLSX, ODT, ODP, ODS
+export async function readDocument(arrayBuffer: ArrayBuffer): Promise {
+ try {
+ const ast = await OfficeParser.parseOffice(arrayBuffer, {
+ extractAttachments: true,
+ ocr: true,
+ ocrLanguage: "eng+esp"
+ });
+
+ // OfficeParser doesn't currently expose page data (page number etc)
+ return [{ text: ast.toText(), pageNumber: 1 }] as IPageText[];
+ } catch (error) {
+ return [{ text: `Failed to read document: ${Exception.messageFrom(error)}`, pageNumber: 1 }] as IPageText[];
+ }
}
\ No newline at end of file
diff --git a/Helpers/StringTools.ts b/Helpers/StringTools.ts
index 7f821b3..e242aef 100644
--- a/Helpers/StringTools.ts
+++ b/Helpers/StringTools.ts
@@ -68,6 +68,15 @@ export abstract class StringTools {
return regex;
}
+ public static toBase64(text: string): string {
+ const bytes = new TextEncoder().encode(text);
+ let binary = "";
+ for (let i = 0; i < bytes.length; i++) {
+ binary += String.fromCharCode(bytes[i]);
+ }
+ return btoa(binary);
+ }
+
public static toBytes(input: string): Uint8Array {
const binaryString = atob(input);
const bytes = new Uint8Array(binaryString.length);
diff --git a/Services/InputService.ts b/Services/InputService.ts
index 04b6e54..3b1f402 100644
--- a/Services/InputService.ts
+++ b/Services/InputService.ts
@@ -5,10 +5,13 @@ import { arrayBufferToBase64, Notice } from "obsidian";
import { FileTypeToMimeType } from "Enums/FileTypeMimeTypeMapping";
import * as path from "path-browserify";
import { pathExtname } from "Helpers/Helpers";
-import { FileType, toFileType } from "Enums/FileType";
+import { FileType, isDocumentFile, toFileType } from "Enums/FileType";
import type { FileSystemService } from "./FileSystemService";
import { Resolve } from "./DependencyService";
import { Services } from "./Services";
+import { readDocument } from "Helpers/DocumentHelper";
+import { MimeType } from "Enums/MimeType";
+import { StringTools } from "Helpers/StringTools";
export class InputService {
@@ -43,12 +46,21 @@ export class InputService {
new Notice(`Unsupported file '${file.name}'`);
continue;
}
-
- attachments.push(new Attachment(
- file.name,
- FileTypeToMimeType[fileType],
- arrayBufferToBase64(await file.arrayBuffer())
- ));
+
+ if (isDocumentFile(fileType)) {
+ const content = await readDocument(await file.arrayBuffer());
+ attachments.push(new Attachment(
+ file.name,
+ MimeType.TEXT_PLAIN,
+ StringTools.toBase64(content[0].text)
+ ));
+ } else {
+ attachments.push(new Attachment(
+ file.name,
+ FileTypeToMimeType[fileType],
+ arrayBufferToBase64(await file.arrayBuffer())
+ ));
+ }
}
}
diff --git a/Services/VaultService.ts b/Services/VaultService.ts
index 3588917..1efa629 100644
--- a/Services/VaultService.ts
+++ b/Services/VaultService.ts
@@ -16,8 +16,8 @@ import * as path from "path-browserify";
import { Event } from "Enums/Event";
import { AbortService } from "./AbortService";
import { AIToolResponse } from "AIClasses/ToolDefinitions/AIToolResponse";
-import { FileType, isBinaryFile, isFileType } from "Enums/FileType";
-import { readPDF } from "Helpers/PDFHelper";
+import { FileType, isBinaryFile, isDocumentFile, isFileType } from "Enums/FileType";
+import { readDocument, readPDF } from "Helpers/DocumentHelper";
interface IFileEventArgs {
oldPath: string;
@@ -86,13 +86,22 @@ export class VaultService {
return Exception.new(`File does not exist: ${filePath}`);
}
- if (isBinaryFile(file.extension.toLowerCase())) {
+ const fileExtension = file.extension.toLowerCase();
+
+ if (isBinaryFile(fileExtension)) {
const arrayBuffer = await this.readBinaryData(file, allowAccessToPluginRoot);
if (arrayBuffer) {
return arrayBufferToBase64(arrayBuffer);
}
}
+ if (isDocumentFile(fileExtension)) {
+ const arrayBuffer = await this.readBinaryData(file, allowAccessToPluginRoot);
+ if (arrayBuffer) {
+ return (await readDocument(arrayBuffer))[0].text;
+ }
+ }
+
return await this.vault.read(file);
}
@@ -380,9 +389,14 @@ export class VaultService {
const batchPromises = batch.map(async (file) => {
try {
let content;
- if (isFileType(file.extension.toLocaleLowerCase(), FileType.PDF)) {
+ const fileExtension = file.extension.toLocaleLowerCase();
+
+ if (isFileType(fileExtension, FileType.PDF)) {
const arrayBuffer = await this.vault.readBinary(file);
content = await readPDF(arrayBuffer);
+ } else if (isDocumentFile(fileExtension)) {
+ const arrayBuffer = await this.vault.readBinary(file);
+ content = await readDocument(arrayBuffer);
} else {
content = [{ text: await this.vault.cachedRead(file), pageNumber: 1 }] as IPageText[];
}
diff --git a/__tests__/AIClasses/Claude.test.ts b/__tests__/AIClasses/Claude.test.ts
index f4510f7..d684dfa 100644
--- a/__tests__/AIClasses/Claude.test.ts
+++ b/__tests__/AIClasses/Claude.test.ts
@@ -795,7 +795,7 @@ describe('Claude', () => {
expect(result[0].content.length).toBeGreaterThan(1);
// Should have filename text block from formatBinaryFiles
- const filenameBlock = result[0].content.find((block: any) => block.type === 'text' && block.text === 'Binary data for test-image.png follows in next message');
+ const filenameBlock = result[0].content.find((block: any) => block.type === 'text' && block.text === `The contents of the file 'test-image.png' are provided below.`);
expect(filenameBlock).toBeDefined();
// Should have image content blocks from formatBinaryFiles
@@ -828,7 +828,7 @@ describe('Claude', () => {
expect(result[0].content.length).toBeGreaterThan(1);
// Should have filename text block from formatBinaryFiles
- const filenameBlock = result[0].content.find((block: any) => block.type === 'text' && block.text === 'Binary data for document.pdf follows in next message');
+ const filenameBlock = result[0].content.find((block: any) => block.type === 'text' && block.text === `The contents of the file 'document.pdf' are provided below.`);
expect(filenameBlock).toBeDefined();
// Should have document content blocks from formatBinaryFiles
@@ -1241,7 +1241,7 @@ describe('Claude', () => {
expect(parsed).toHaveLength(2);
expect(parsed[0]).toEqual({
type: 'text',
- text: 'Binary data for report.pdf follows in next message'
+ text: `The contents of the file 'report.pdf' are provided below.`
});
expect(parsed[1]).toEqual({
type: 'document',
@@ -1269,7 +1269,7 @@ describe('Claude', () => {
expect(parsed).toHaveLength(2);
expect(parsed[0]).toEqual({
type: 'text',
- text: 'Binary data for photo.jpg follows in next message'
+ text: `The contents of the file 'photo.jpg' are provided below.`
});
expect(parsed[1]).toEqual({
type: 'image',
@@ -1410,7 +1410,7 @@ describe('Claude', () => {
expect(parsed).toHaveLength(6);
// PDF file
- expect(parsed[0]).toEqual({ type: 'text', text: 'Binary data for doc.pdf follows in next message' });
+ expect(parsed[0]).toEqual({ type: 'text', text: `The contents of the file 'doc.pdf' are provided below.` });
expect(parsed[1]).toEqual({
type: 'document',
source: {
@@ -1420,7 +1420,7 @@ describe('Claude', () => {
});
// JPEG image
- expect(parsed[2]).toEqual({ type: 'text', text: 'Binary data for image.jpg follows in next message' });
+ expect(parsed[2]).toEqual({ type: 'text', text: `The contents of the file 'image.jpg' are provided below.` });
expect(parsed[3]).toEqual({
type: 'image',
source: {
@@ -1430,7 +1430,7 @@ describe('Claude', () => {
});
// PNG image
- expect(parsed[4]).toEqual({ type: 'text', text: 'Binary data for screenshot.png follows in next message' });
+ expect(parsed[4]).toEqual({ type: 'text', text: `The contents of the file 'screenshot.png' are provided below.` });
expect(parsed[5]).toEqual({
type: 'image',
source: {
@@ -1477,14 +1477,14 @@ describe('Claude', () => {
expect(parsed).toHaveLength(5);
expect(parsed[0].type).toBe('text');
- expect(parsed[0].text).toBe('Binary data for good.jpg follows in next message');
+ expect(parsed[0].text).toBe(`The contents of the file 'good.jpg' are provided below.`);
expect(parsed[1].type).toBe('image');
expect(parsed[2]).toEqual({
type: 'text',
text: 'Unsupported mime type \'image/bmp\': bad.bmp'
});
expect(parsed[3].type).toBe('text');
- expect(parsed[3].text).toBe('Binary data for doc.pdf follows in next message');
+ expect(parsed[3].text).toBe(`The contents of the file 'doc.pdf' are provided below.`);
expect(parsed[4].type).toBe('document');
});
@@ -1532,9 +1532,9 @@ describe('Claude', () => {
const parsed = JSON.parse(result);
expect(parsed).toHaveLength(4);
- expect(parsed[0].text).toBe('Binary data for document.PDF follows in next message');
+ expect(parsed[0].text).toBe(`The contents of the file 'document.PDF' are provided below.`);
expect(parsed[1].type).toBe('document');
- expect(parsed[2].text).toBe('Binary data for photo.JPG follows in next message');
+ expect(parsed[2].text).toBe(`The contents of the file 'photo.JPG' are provided below.`);
expect(parsed[3].type).toBe('image');
});
@@ -1561,7 +1561,7 @@ describe('Claude', () => {
const result = claude.formatBinaryFiles([attachment as any]);
const parsed = JSON.parse(result);
- expect(parsed[0].text).toBe('Binary data for report (final) v2.pdf follows in next message');
+ expect(parsed[0].text).toBe(`The contents of the file 'report (final) v2.pdf' are provided below.`);
});
it('should handle JPEG files with .jpeg extension', () => {
diff --git a/__tests__/AIClasses/Gemini.test.ts b/__tests__/AIClasses/Gemini.test.ts
index a158d3f..8033e30 100644
--- a/__tests__/AIClasses/Gemini.test.ts
+++ b/__tests__/AIClasses/Gemini.test.ts
@@ -1135,7 +1135,7 @@ describe('Gemini', () => {
expect(parsed).toHaveLength(2);
expect(parsed[0]).toEqual({
- text: 'Binary data for report.pdf follows in next message'
+ text: `The contents of the file 'report.pdf' are provided below.`
});
expect(parsed[1]).toEqual({
fileData: {
@@ -1161,7 +1161,7 @@ describe('Gemini', () => {
expect(parsed).toHaveLength(2);
expect(parsed[0]).toEqual({
- text: 'Binary data for photo.jpg follows in next message'
+ text: `The contents of the file 'photo.jpg' are provided below.`
});
expect(parsed[1]).toEqual({
fileData: {
@@ -1271,7 +1271,7 @@ describe('Gemini', () => {
expect(parsed).toHaveLength(6);
// PDF file
- expect(parsed[0]).toEqual({ text: 'Binary data for doc.pdf follows in next message' });
+ expect(parsed[0]).toEqual({ text: `The contents of the file 'doc.pdf' are provided below.` });
expect(parsed[1]).toEqual({
fileData: {
mimeType: 'application/pdf',
@@ -1280,7 +1280,7 @@ describe('Gemini', () => {
});
// JPEG image
- expect(parsed[2]).toEqual({ text: 'Binary data for image.jpg follows in next message' });
+ expect(parsed[2]).toEqual({ text: `The contents of the file 'image.jpg' are provided below.` });
expect(parsed[3]).toEqual({
fileData: {
mimeType: 'image/jpeg',
@@ -1289,7 +1289,7 @@ describe('Gemini', () => {
});
// PNG image
- expect(parsed[4]).toEqual({ text: 'Binary data for screenshot.png follows in next message' });
+ expect(parsed[4]).toEqual({ text: `The contents of the file 'screenshot.png' are provided below.` });
expect(parsed[5]).toEqual({
fileData: {
mimeType: 'image/png',
@@ -1334,12 +1334,12 @@ describe('Gemini', () => {
expect(parsed).toHaveLength(5);
- expect(parsed[0]).toEqual({ text: 'Binary data for good.jpg follows in next message' });
+ expect(parsed[0]).toEqual({ text: `The contents of the file 'good.jpg' are provided below.` });
expect(parsed[1]).toHaveProperty('fileData');
expect(parsed[2]).toEqual({
text: 'Unsupported mime type \'image/bmp\': bad.bmp'
});
- expect(parsed[3]).toEqual({ text: 'Binary data for doc.pdf follows in next message' });
+ expect(parsed[3]).toEqual({ text: `The contents of the file 'doc.pdf' are provided below.` });
expect(parsed[4]).toHaveProperty('fileData');
});
@@ -1369,7 +1369,7 @@ describe('Gemini', () => {
const parsed = JSON.parse(result);
expect(parsed).toHaveLength(2); // Only successful upload
- expect(parsed[0]).toEqual({ text: 'Binary data for success.pdf follows in next message' });
+ expect(parsed[0]).toEqual({ text: `The contents of the file 'success.pdf' are provided below.` });
expect(parsed[1]).toEqual({
fileData: {
mimeType: 'application/pdf',
@@ -1399,7 +1399,7 @@ describe('Gemini', () => {
const result = gemini.formatBinaryFiles([attachment as any]);
const parsed = JSON.parse(result);
- expect(parsed[0].text).toBe('Binary data for report (final) v2.pdf follows in next message');
+ expect(parsed[0].text).toBe(`The contents of the file 'report (final) v2.pdf' are provided below.`);
});
it('should handle JPEG files with .jpeg extension', () => {
diff --git a/__tests__/AIClasses/Mistral.test.ts b/__tests__/AIClasses/Mistral.test.ts
index 925a315..ba77cd9 100644
--- a/__tests__/AIClasses/Mistral.test.ts
+++ b/__tests__/AIClasses/Mistral.test.ts
@@ -705,7 +705,7 @@ describe('Mistral', () => {
expect(parsed).toHaveLength(2);
expect(parsed[0]).toEqual({
type: 'text',
- text: 'Document: report.pdf'
+ text: `The contents of the file 'report.pdf' are provided below.`
});
expect(parsed[1]).toEqual({
type: 'document_url',
@@ -730,7 +730,7 @@ describe('Mistral', () => {
expect(parsed).toHaveLength(2);
expect(parsed[0]).toEqual({
type: 'text',
- text: 'Image: photo.jpg'
+ text: `The contents of the file 'photo.jpg' are provided below.`
});
expect(parsed[1]).toEqual({
type: 'image_url',
@@ -808,14 +808,14 @@ describe('Mistral', () => {
expect(parsed).toHaveLength(4);
// PDF file
- expect(parsed[0]).toEqual({ type: 'text', text: 'Document: doc.pdf' });
+ expect(parsed[0]).toEqual({ type: 'text', text: `The contents of the file 'doc.pdf' are provided below.` });
expect(parsed[1]).toEqual({
type: 'document_url',
document_url: 'https://signed-url.com/file_1'
});
// JPEG image
- expect(parsed[2]).toEqual({ type: 'text', text: 'Image: image.jpg' });
+ expect(parsed[2]).toEqual({ type: 'text', text: `The contents of the file 'image.jpg' are provided below.` });
expect(parsed[3]).toEqual({
type: 'image_url',
image_url: 'https://signed-url.com/file_2'
diff --git a/__tests__/AIClasses/OpenAI.test.ts b/__tests__/AIClasses/OpenAI.test.ts
index 2bdb62d..0085292 100644
--- a/__tests__/AIClasses/OpenAI.test.ts
+++ b/__tests__/AIClasses/OpenAI.test.ts
@@ -1016,7 +1016,7 @@ describe('OpenAI', () => {
expect(parsed[0].content).toHaveLength(2);
expect(parsed[0].content[0]).toEqual({
type: 'input_text',
- text: 'Binary data for report.pdf follows in next message'
+ text: `The contents of the file 'report.pdf' are provided below.`
});
expect(parsed[0].content[1]).toEqual({
type: 'input_file',
@@ -1043,7 +1043,7 @@ describe('OpenAI', () => {
expect(parsed[0].content).toHaveLength(2);
expect(parsed[0].content[0]).toEqual({
type: 'input_text',
- text: 'Binary data for photo.jpg follows in next message'
+ text: `The contents of the file 'photo.jpg' are provided below.`
});
expect(parsed[0].content[1]).toEqual({
type: 'input_image',
@@ -1069,7 +1069,7 @@ describe('OpenAI', () => {
expect(parsed[0].content).toHaveLength(2);
expect(parsed[0].content[0]).toEqual({
type: 'input_text',
- text: 'Binary data for diagram.png follows in next message'
+ text: `The contents of the file 'diagram.png' are provided below.`
});
expect(parsed[0].content[1]).toEqual({
type: 'input_image',
@@ -1095,7 +1095,7 @@ describe('OpenAI', () => {
expect(parsed[0].content).toHaveLength(2);
expect(parsed[0].content[0]).toEqual({
type: 'input_text',
- text: 'Binary data for modern.webp follows in next message'
+ text: `The contents of the file 'modern.webp' are provided below.`
});
expect(parsed[0].content[1]).toEqual({
type: 'input_image',
@@ -1184,7 +1184,7 @@ describe('OpenAI', () => {
expect(parsed[0].content[0]).toEqual({
type: 'input_text',
- text: 'Binary data for doc.pdf follows in next message'
+ text: `The contents of the file 'doc.pdf' are provided below.`
});
expect(parsed[0].content[1]).toEqual({
type: 'input_file',
@@ -1193,7 +1193,7 @@ describe('OpenAI', () => {
expect(parsed[0].content[2]).toEqual({
type: 'input_text',
- text: 'Binary data for image.jpg follows in next message'
+ text: `The contents of the file 'image.jpg' are provided below.`
});
expect(parsed[0].content[3]).toEqual({
type: 'input_image',
@@ -1202,7 +1202,7 @@ describe('OpenAI', () => {
expect(parsed[0].content[4]).toEqual({
type: 'input_text',
- text: 'Binary data for screenshot.png follows in next message'
+ text: `The contents of the file 'screenshot.png' are provided below.`
});
expect(parsed[0].content[5]).toEqual({
type: 'input_image',
@@ -1249,7 +1249,7 @@ describe('OpenAI', () => {
expect(parsed[0].content[0]).toEqual({
type: 'input_text',
- text: 'Binary data for good.jpg follows in next message'
+ text: `The contents of the file 'good.jpg' are provided below.`
});
expect(parsed[0].content[1].type).toBe('input_image');
expect(parsed[0].content[2]).toEqual({
@@ -1258,7 +1258,7 @@ describe('OpenAI', () => {
});
expect(parsed[0].content[3]).toEqual({
type: 'input_text',
- text: 'Binary data for doc.pdf follows in next message'
+ text: `The contents of the file 'doc.pdf' are provided below.`
});
expect(parsed[0].content[4].type).toBe('input_file');
});
@@ -1292,7 +1292,7 @@ describe('OpenAI', () => {
expect(parsed[0].content).toHaveLength(2); // Text + file reference
expect(parsed[0].content[0]).toEqual({
type: 'input_text',
- text: 'Binary data for success.pdf follows in next message'
+ text: `The contents of the file 'success.pdf' are provided below.`
});
expect(parsed[0].content[1]).toEqual({
type: 'input_file',
diff --git a/__tests__/Conversations/Conversation.test.ts b/__tests__/Conversations/Conversation.test.ts
index eb0e904..8d3c92c 100644
--- a/__tests__/Conversations/Conversation.test.ts
+++ b/__tests__/Conversations/Conversation.test.ts
@@ -4,6 +4,8 @@ import { ConversationContent } from '../../Conversations/ConversationContent';
import { Role } from '../../Enums/Role';
import { AIToolResponse } from '../../AIClasses/ToolDefinitions/AIToolResponse';
import { AITool } from '../../Enums/AITool';
+import { MimeType } from '../../Enums/MimeType';
+import { StringTools } from '../../Helpers/StringTools';
describe('Conversation', () => {
describe('constructor', () => {
@@ -544,7 +546,72 @@ describe('Conversation', () => {
});
});
- describe('non-read_vault_files functions', () => {
+ describe('read_vault_files with document files', () => {
+ it('should base64-encode document file contents and set mime type to text/plain', () => {
+ const conversation = new Conversation();
+ const documentText = 'This is the extracted text from a document file.';
+ const functionResponse = new AIToolResponse(
+ AITool.ReadVaultFiles,
+ {
+ results: [
+ { path: 'report.odt', type: 'odt', contents: documentText }
+ ]
+ },
+ 'tool-123'
+ );
+
+ conversation.addFunctionResponse(functionResponse);
+
+ // Should have 2 items: function response + attachments
+ expect(conversation.contents).toHaveLength(2);
+ expect(conversation.contents[1].attachments).toHaveLength(1);
+
+ const attachment = conversation.contents[1].attachments[0];
+ expect(attachment.fileName).toBe('report.odt');
+ expect(attachment.mimeType).toBe(MimeType.TEXT_PLAIN);
+ // Content should be base64-encoded, not plain text
+ expect(attachment.base64).toBe(StringTools.toBase64(documentText));
+ expect(attachment.base64).not.toBe(documentText);
+ });
+
+ it('should handle document files alongside text and binary files', () => {
+ const conversation = new Conversation();
+ const functionResponse = new AIToolResponse(
+ AITool.ReadVaultFiles,
+ {
+ results: [
+ { path: 'notes.md', type: 'md', contents: '# Notes' },
+ { path: 'spreadsheet.xlsx', type: 'xlsx', contents: 'Extracted spreadsheet text' },
+ { path: 'photo.png', type: 'png', contents: 'base64imagedata...' }
+ ]
+ },
+ 'tool-123'
+ );
+
+ conversation.addFunctionResponse(functionResponse);
+
+ // Should have 2 items: function response (text) + attachments (document + binary)
+ expect(conversation.contents).toHaveLength(2);
+
+ // Text file in function response
+ const parsedResponse = JSON.parse(conversation.contents[0].functionResponse!);
+ expect(parsedResponse.functionResponse.response.results).toHaveLength(1);
+ expect(parsedResponse.functionResponse.response.results[0].path).toBe('notes.md');
+
+ // Document and binary as attachments
+ const attachments = conversation.contents[1].attachments;
+ expect(attachments).toHaveLength(2);
+
+ const docAttachment = attachments.find(a => a.fileName === 'spreadsheet.xlsx')!;
+ expect(docAttachment.mimeType).toBe(MimeType.TEXT_PLAIN);
+ expect(docAttachment.base64).toBe(StringTools.toBase64('Extracted spreadsheet text'));
+
+ const imgAttachment = attachments.find(a => a.fileName === 'photo.png')!;
+ expect(imgAttachment.base64).toBe('base64imagedata...');
+ });
+ });
+
+ describe('non-read_vault_files functions', () => {
it('should handle other functions normally', () => {
const conversation = new Conversation();
const functionResponse = new AIToolResponse(
diff --git a/__tests__/Services/VaultService.pdf.test.ts b/__tests__/Services/VaultService.pdf.test.ts
index 9517bb5..e665a77 100644
--- a/__tests__/Services/VaultService.pdf.test.ts
+++ b/__tests__/Services/VaultService.pdf.test.ts
@@ -7,7 +7,7 @@ import { SanitiserService } from '../../Services/SanitiserService';
import { SettingsService, type IVaultkeeperAISettings } from '../../Services/SettingsService';
import { AIProviderModel } from '../../Enums/ApiProvider';
import { Exception } from '../../Helpers/Exception';
-import * as PDFHelper from '../../Helpers/PDFHelper';
+import * as PDFHelper from '../../Helpers/DocumentHelper';
import type { IPageText } from '../../Types/SearchTypes';
/**
diff --git a/package-lock.json b/package-lock.json
index 56daf39..aeaa79b 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -20,6 +20,7 @@
"highlight.js": "^11.11.1",
"katex": "^0.16.33",
"lowlight": "^3.3.0",
+ "officeparser": "^6.0.4",
"openai": "^6.25.0",
"path-browserify": "^1.0.1",
"regex-parser": "^2.3.1",
@@ -1002,6 +1003,256 @@
"ret": "~0.1.10"
}
},
+ "node_modules/@napi-rs/canvas": {
+ "version": "0.1.96",
+ "resolved": "https://registry.npmjs.org/@napi-rs/canvas/-/canvas-0.1.96.tgz",
+ "integrity": "sha512-6NNmNxvoJKeucVjxaaRUt3La2i5jShgiAbaY3G/72s1Vp3U06XPrAIxkAjBxpDcamEn/t+WJ4OOlGmvILo4/Ew==",
+ "license": "MIT",
+ "optional": true,
+ "workspaces": [
+ "e2e/*"
+ ],
+ "engines": {
+ "node": ">= 10"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/Brooooooklyn"
+ },
+ "optionalDependencies": {
+ "@napi-rs/canvas-android-arm64": "0.1.96",
+ "@napi-rs/canvas-darwin-arm64": "0.1.96",
+ "@napi-rs/canvas-darwin-x64": "0.1.96",
+ "@napi-rs/canvas-linux-arm-gnueabihf": "0.1.96",
+ "@napi-rs/canvas-linux-arm64-gnu": "0.1.96",
+ "@napi-rs/canvas-linux-arm64-musl": "0.1.96",
+ "@napi-rs/canvas-linux-riscv64-gnu": "0.1.96",
+ "@napi-rs/canvas-linux-x64-gnu": "0.1.96",
+ "@napi-rs/canvas-linux-x64-musl": "0.1.96",
+ "@napi-rs/canvas-win32-arm64-msvc": "0.1.96",
+ "@napi-rs/canvas-win32-x64-msvc": "0.1.96"
+ }
+ },
+ "node_modules/@napi-rs/canvas-android-arm64": {
+ "version": "0.1.96",
+ "resolved": "https://registry.npmjs.org/@napi-rs/canvas-android-arm64/-/canvas-android-arm64-0.1.96.tgz",
+ "integrity": "sha512-ew1sPrN3dGdZ3L4FoohPfnjq0f9/Jk7o+wP7HkQZokcXgIUD6FIyICEWGhMYzv53j63wUcPvZeAwgewX58/egg==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">= 10"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/Brooooooklyn"
+ }
+ },
+ "node_modules/@napi-rs/canvas-darwin-arm64": {
+ "version": "0.1.96",
+ "resolved": "https://registry.npmjs.org/@napi-rs/canvas-darwin-arm64/-/canvas-darwin-arm64-0.1.96.tgz",
+ "integrity": "sha512-Q/wOXZ5PzTqpdmA5eUOcegCf4Go/zz3aZ5DlzSeDpOjFmfwMKh8EzLAoweQ+mJVagcHQyzoJhaTEnrO68TNyNg==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 10"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/Brooooooklyn"
+ }
+ },
+ "node_modules/@napi-rs/canvas-darwin-x64": {
+ "version": "0.1.96",
+ "resolved": "https://registry.npmjs.org/@napi-rs/canvas-darwin-x64/-/canvas-darwin-x64-0.1.96.tgz",
+ "integrity": "sha512-UrXiQz28tQEvGM1qvyptewOAfmUrrd5+wvi6Rzjj2VprZI8iZ2KIvBD2lTTG1bVF95AbeDeG7PJA0D9sLKaOFA==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 10"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/Brooooooklyn"
+ }
+ },
+ "node_modules/@napi-rs/canvas-linux-arm-gnueabihf": {
+ "version": "0.1.96",
+ "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-arm-gnueabihf/-/canvas-linux-arm-gnueabihf-0.1.96.tgz",
+ "integrity": "sha512-I90ODxweD8aEP6XKU/NU+biso95MwCtQ2F46dUvhec1HesFi0tq/tAJkYic/1aBSiO/1kGKmSeD1B0duOHhEHQ==",
+ "cpu": [
+ "arm"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/Brooooooklyn"
+ }
+ },
+ "node_modules/@napi-rs/canvas-linux-arm64-gnu": {
+ "version": "0.1.96",
+ "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-arm64-gnu/-/canvas-linux-arm64-gnu-0.1.96.tgz",
+ "integrity": "sha512-Dx/0+RFV++w3PcRy+4xNXkghhXjA5d0Mw1bs95emn5Llinp1vihMaA6WJt3oYv2LAHc36+gnrhIBsPhUyI2SGw==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/Brooooooklyn"
+ }
+ },
+ "node_modules/@napi-rs/canvas-linux-arm64-musl": {
+ "version": "0.1.96",
+ "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-arm64-musl/-/canvas-linux-arm64-musl-0.1.96.tgz",
+ "integrity": "sha512-UvOi7fii3IE2KDfEfhh8m+LpzSRvhGK7o1eho99M2M0HTik11k3GX+2qgVx9EtujN3/bhFFS1kSO3+vPMaJ0Mg==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/Brooooooklyn"
+ }
+ },
+ "node_modules/@napi-rs/canvas-linux-riscv64-gnu": {
+ "version": "0.1.96",
+ "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-riscv64-gnu/-/canvas-linux-riscv64-gnu-0.1.96.tgz",
+ "integrity": "sha512-MBSukhGCQ5nRtf9NbFYWOU080yqkZU1PbuH4o1ROvB4CbPl12fchDR35tU83Wz8gWIM9JTn99lBn9DenPIv7Ig==",
+ "cpu": [
+ "riscv64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/Brooooooklyn"
+ }
+ },
+ "node_modules/@napi-rs/canvas-linux-x64-gnu": {
+ "version": "0.1.96",
+ "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-x64-gnu/-/canvas-linux-x64-gnu-0.1.96.tgz",
+ "integrity": "sha512-I/ccu2SstyKiV3HIeVzyBIWfrJo8cN7+MSQZPnabewWV6hfJ2nY7Df2WqOHmobBRUw84uGR6zfQHsUEio/m5Vg==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/Brooooooklyn"
+ }
+ },
+ "node_modules/@napi-rs/canvas-linux-x64-musl": {
+ "version": "0.1.96",
+ "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-x64-musl/-/canvas-linux-x64-musl-0.1.96.tgz",
+ "integrity": "sha512-H3uov7qnTl73GDT4h52lAqpJPsl1tIUyNPWJyhQ6gHakohNqqRq3uf80+NEpzcytKGEOENP1wX3yGwZxhjiWEQ==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/Brooooooklyn"
+ }
+ },
+ "node_modules/@napi-rs/canvas-win32-arm64-msvc": {
+ "version": "0.1.96",
+ "resolved": "https://registry.npmjs.org/@napi-rs/canvas-win32-arm64-msvc/-/canvas-win32-arm64-msvc-0.1.96.tgz",
+ "integrity": "sha512-ATp6Y+djOjYtkfV/VRH7CZ8I1MEtkUQBmKUbuWw5zWEHHqfL0cEcInE4Cxgx7zkNAhEdBbnH8HMVrqNp+/gwxA==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 10"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/Brooooooklyn"
+ }
+ },
+ "node_modules/@napi-rs/canvas-win32-x64-msvc": {
+ "version": "0.1.96",
+ "resolved": "https://registry.npmjs.org/@napi-rs/canvas-win32-x64-msvc/-/canvas-win32-x64-msvc-0.1.96.tgz",
+ "integrity": "sha512-UYGdTltVd+Z8mcIuoqGmAXXUvwH5CLf2M6mIB5B0/JmX5J041jETjqtSYl7gN+aj3k1by/SG6sS0hAwCqyK7zw==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 10"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/Brooooooklyn"
+ }
+ },
"node_modules/@pkgjs/parseargs": {
"version": "0.11.0",
"resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
@@ -1671,6 +1922,12 @@
"svelte": "^3 || ^4 || ^5 || ^5.0.0-next.0"
}
},
+ "node_modules/@tokenizer/token": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz",
+ "integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==",
+ "license": "MIT"
+ },
"node_modules/@types/aria-query": {
"version": "5.0.4",
"resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz",
@@ -2299,12 +2556,33 @@
"url": "https://opencollective.com/vitest"
}
},
+ "node_modules/@xmldom/xmldom": {
+ "version": "0.8.11",
+ "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.11.tgz",
+ "integrity": "sha512-cQzWCtO6C8TQiYl1ruKNn2U6Ao4o4WBBcbL61yJl84x+j5sOWWFU9X7DpND8XZG3daDppSsigMdfAIl2upQBRw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=10.0.0"
+ }
+ },
"node_modules/abbrev": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
"integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==",
"license": "ISC"
},
+ "node_modules/abort-controller": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz",
+ "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==",
+ "license": "MIT",
+ "dependencies": {
+ "event-target-shim": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=6.5"
+ }
+ },
"node_modules/accepts": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz",
@@ -2663,6 +2941,12 @@
"node": "*"
}
},
+ "node_modules/bmp-js": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/bmp-js/-/bmp-js-0.1.0.tgz",
+ "integrity": "sha512-vHdS19CnY3hwiNdkaqk93DvjVLfbEcI8mys4UjuWrlX1haDmroo8o4xCzh4wD6DGV6HxRCyauwhHRqMTfERtjw==",
+ "license": "MIT"
+ },
"node_modules/body-parser": {
"version": "2.2.2",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.2.tgz",
@@ -2700,12 +2984,51 @@
"node": "18 || 20 || >=22"
}
},
+ "node_modules/buffer": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz",
+ "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "base64-js": "^1.3.1",
+ "ieee754": "^1.2.1"
+ }
+ },
+ "node_modules/buffer-crc32": {
+ "version": "0.2.13",
+ "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz",
+ "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==",
+ "license": "MIT",
+ "engines": {
+ "node": "*"
+ }
+ },
"node_modules/buffer-equal-constant-time": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
"integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==",
"license": "BSD-3-Clause"
},
+ "node_modules/buffer-from": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
+ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
+ "license": "MIT"
+ },
"node_modules/builtin-modules": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-5.0.0.tgz",
@@ -2942,6 +3265,21 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/concat-stream": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz",
+ "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==",
+ "engines": [
+ "node >= 6.0"
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "buffer-from": "^1.0.0",
+ "inherits": "^2.0.3",
+ "readable-stream": "^3.0.2",
+ "typedarray": "^0.0.6"
+ }
+ },
"node_modules/content-disposition": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.1.tgz",
@@ -4433,6 +4771,24 @@
"node": ">= 0.6"
}
},
+ "node_modules/event-target-shim": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz",
+ "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/events": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz",
+ "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.8.x"
+ }
+ },
"node_modules/expect-type": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz",
@@ -4591,6 +4947,23 @@
"node": ">=16.0.0"
}
},
+ "node_modules/file-type": {
+ "version": "16.5.4",
+ "resolved": "https://registry.npmjs.org/file-type/-/file-type-16.5.4.tgz",
+ "integrity": "sha512-/yFHK0aGjFEgDJjEKP0pWCplsPFPhwyfwevf/pVxiN0tmE4L9LmwWxWukdJSHdoCli4VgQLehjJtwQBnqmsKcw==",
+ "license": "MIT",
+ "dependencies": {
+ "readable-web-to-node-stream": "^3.0.0",
+ "strtok3": "^6.2.4",
+ "token-types": "^4.1.1"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sindresorhus/file-type?sponsor=1"
+ }
+ },
"node_modules/finalhandler": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz",
@@ -5374,6 +5747,32 @@
"url": "https://opencollective.com/express"
}
},
+ "node_modules/idb-keyval": {
+ "version": "6.2.2",
+ "resolved": "https://registry.npmjs.org/idb-keyval/-/idb-keyval-6.2.2.tgz",
+ "integrity": "sha512-yjD9nARJ/jb1g+CvD0tlhUHOrJ9Sy0P8T9MF3YaLlHnSRpwPfpTX0XIvpmw3gAJUmEu3FiICLBDPXVwyEvrleg==",
+ "license": "Apache-2.0"
+ },
+ "node_modules/ieee754": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
+ "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "BSD-3-Clause"
+ },
"node_modules/ignore": {
"version": "7.0.5",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz",
@@ -5858,6 +6257,12 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/is-url": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/is-url/-/is-url-1.2.4.tgz",
+ "integrity": "sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww==",
+ "license": "MIT"
+ },
"node_modules/is-weakmap": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz",
@@ -7593,6 +7998,26 @@
],
"license": "MIT"
},
+ "node_modules/officeparser": {
+ "version": "6.0.4",
+ "resolved": "https://registry.npmjs.org/officeparser/-/officeparser-6.0.4.tgz",
+ "integrity": "sha512-zmxst4xbrUxCCY6w5BLmmtewJY5eHkLKNJ+2Z5OKY0JPh5qmtUSslFkg0fC3xc+0RG4DFdfQ36107B99yLMfuQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@xmldom/xmldom": "^0.8.10",
+ "concat-stream": "^2.0.0",
+ "file-type": "^16.5.4",
+ "pdfjs-dist": "5.4.530",
+ "tesseract.js": "^6.0.0",
+ "yauzl": "^3.1.3"
+ },
+ "bin": {
+ "officeparser": "dist/index.js"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
"node_modules/on-finished": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
@@ -7652,6 +8077,15 @@
}
}
},
+ "node_modules/opencollective-postinstall": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/opencollective-postinstall/-/opencollective-postinstall-2.0.3.tgz",
+ "integrity": "sha512-8AV/sCtuzUeTo8gQK5qDZzARrulB3egtLzFgteqB2tcT4Mw7B8Kt7JcDHmltjz6FOAHsvTevk70gZEbhM4ZS9Q==",
+ "license": "MIT",
+ "bin": {
+ "opencollective-postinstall": "index.js"
+ }
+ },
"node_modules/optionator": {
"version": "0.9.4",
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
@@ -7888,6 +8322,37 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/pdfjs-dist": {
+ "version": "5.4.530",
+ "resolved": "https://registry.npmjs.org/pdfjs-dist/-/pdfjs-dist-5.4.530.tgz",
+ "integrity": "sha512-r1hWsSIGGmyYUAHR26zSXkxYWLXLMd6AwqcaFYG9YUZ0GBf5GvcjJSeo512tabM4GYFhxhl5pMCmPr7Q72Rq2Q==",
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=20.16.0 || >=22.3.0"
+ },
+ "optionalDependencies": {
+ "@napi-rs/canvas": "^0.1.84"
+ }
+ },
+ "node_modules/peek-readable": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/peek-readable/-/peek-readable-4.1.0.tgz",
+ "integrity": "sha512-ZI3LnwUv5nOGbQzD9c2iDG6toheuXSZP5esSHBjopsXH4dg19soufvpUGA3uohi5anFtGb2lhAVdHzH6R/Evvg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/Borewit"
+ }
+ },
+ "node_modules/pend": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz",
+ "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==",
+ "license": "MIT"
+ },
"node_modules/picocolors": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
@@ -7985,6 +8450,15 @@
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
}
},
+ "node_modules/process": {
+ "version": "0.11.10",
+ "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
+ "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6.0"
+ }
+ },
"node_modules/prop-types": {
"version": "15.8.1",
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
@@ -8107,6 +8581,52 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/readable-stream": {
+ "version": "3.6.2",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
+ "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
+ "license": "MIT",
+ "dependencies": {
+ "inherits": "^2.0.3",
+ "string_decoder": "^1.1.1",
+ "util-deprecate": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/readable-web-to-node-stream": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/readable-web-to-node-stream/-/readable-web-to-node-stream-3.0.4.tgz",
+ "integrity": "sha512-9nX56alTf5bwXQ3ZDipHJhusu9NTQJ/CVPtb/XHAJCXihZeitfJvIRS4GqQ/mfIoOE3IelHMrpayVrosdHBuLw==",
+ "license": "MIT",
+ "dependencies": {
+ "readable-stream": "^4.7.0"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/Borewit"
+ }
+ },
+ "node_modules/readable-web-to-node-stream/node_modules/readable-stream": {
+ "version": "4.7.0",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz",
+ "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==",
+ "license": "MIT",
+ "dependencies": {
+ "abort-controller": "^3.0.0",
+ "buffer": "^6.0.3",
+ "events": "^3.3.0",
+ "process": "^0.11.10",
+ "string_decoder": "^1.3.0"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ }
+ },
"node_modules/readdirp": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz",
@@ -8144,6 +8664,12 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/regenerator-runtime": {
+ "version": "0.13.11",
+ "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz",
+ "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==",
+ "license": "MIT"
+ },
"node_modules/regex": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/regex/-/regex-6.1.0.tgz",
@@ -8952,6 +9478,15 @@
"node": ">= 0.4"
}
},
+ "node_modules/string_decoder": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
+ "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
+ "license": "MIT",
+ "dependencies": {
+ "safe-buffer": "~5.2.0"
+ }
+ },
"node_modules/string-width": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz",
@@ -9177,6 +9712,23 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/strtok3": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-6.3.0.tgz",
+ "integrity": "sha512-fZtbhtvI9I48xDSywd/somNqgUHl2L2cstmXCCif0itOf96jeW18MBSyrLuNicYQVkvpOxkZtkzujiTJ9LW5Jw==",
+ "license": "MIT",
+ "dependencies": {
+ "@tokenizer/token": "^0.3.0",
+ "peek-readable": "^4.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/Borewit"
+ }
+ },
"node_modules/style-mod": {
"version": "4.1.3",
"resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.1.3.tgz",
@@ -9360,6 +9912,50 @@
"url": "https://opencollective.com/webpack"
}
},
+ "node_modules/tesseract.js": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/tesseract.js/-/tesseract.js-6.0.1.tgz",
+ "integrity": "sha512-/sPvMvrCtgxnNRCjbTYbr7BRu0yfWDsMZQ2a/T5aN/L1t8wUQN6tTWv6p6FwzpoEBA0jrN2UD2SX4QQFRdoDbA==",
+ "hasInstallScript": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "bmp-js": "^0.1.0",
+ "idb-keyval": "^6.2.0",
+ "is-url": "^1.2.4",
+ "node-fetch": "^2.6.9",
+ "opencollective-postinstall": "^2.0.3",
+ "regenerator-runtime": "^0.13.3",
+ "tesseract.js-core": "^6.0.0",
+ "wasm-feature-detect": "^1.2.11",
+ "zlibjs": "^0.3.1"
+ }
+ },
+ "node_modules/tesseract.js-core": {
+ "version": "6.1.2",
+ "resolved": "https://registry.npmjs.org/tesseract.js-core/-/tesseract.js-core-6.1.2.tgz",
+ "integrity": "sha512-pv4GjmramjdObhDyR1q85Td8X60Puu/lGQn7Kw2id05LLgHhAcWgnz6xSdMCSxBMWjQDmMyDXPTC2aqADdpiow==",
+ "license": "Apache-2.0"
+ },
+ "node_modules/tesseract.js/node_modules/node-fetch": {
+ "version": "2.7.0",
+ "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
+ "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==",
+ "license": "MIT",
+ "dependencies": {
+ "whatwg-url": "^5.0.0"
+ },
+ "engines": {
+ "node": "4.x || >=6.0.0"
+ },
+ "peerDependencies": {
+ "encoding": "^0.1.0"
+ },
+ "peerDependenciesMeta": {
+ "encoding": {
+ "optional": true
+ }
+ }
+ },
"node_modules/tinybench": {
"version": "2.9.0",
"resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz",
@@ -9413,6 +10009,23 @@
"node": ">=0.6"
}
},
+ "node_modules/token-types": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/token-types/-/token-types-4.2.1.tgz",
+ "integrity": "sha512-6udB24Q737UD/SDsKAHI9FCRP7Bqc9D/MQUV02ORQg5iskjtLJlZJNdN4kKtcdtwCeWIwIHDGaUsTsCCAa8sFQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@tokenizer/token": "^0.3.0",
+ "ieee754": "^1.2.1"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/Borewit"
+ }
+ },
"node_modules/toml-eslint-parser": {
"version": "0.9.3",
"resolved": "https://registry.npmjs.org/toml-eslint-parser/-/toml-eslint-parser-0.9.3.tgz",
@@ -9439,6 +10052,12 @@
"node": ">=6"
}
},
+ "node_modules/tr46": {
+ "version": "0.0.3",
+ "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
+ "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==",
+ "license": "MIT"
+ },
"node_modules/trim-lines": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz",
@@ -9616,6 +10235,12 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/typedarray": {
+ "version": "0.0.6",
+ "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
+ "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==",
+ "license": "MIT"
+ },
"node_modules/typescript": {
"version": "5.9.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
@@ -9837,6 +10462,12 @@
"punycode": "^2.1.0"
}
},
+ "node_modules/util-deprecate": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
+ "license": "MIT"
+ },
"node_modules/uuid": {
"version": "13.0.0",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-13.0.0.tgz",
@@ -10062,6 +10693,12 @@
"license": "MIT",
"peer": true
},
+ "node_modules/wasm-feature-detect": {
+ "version": "1.8.0",
+ "resolved": "https://registry.npmjs.org/wasm-feature-detect/-/wasm-feature-detect-1.8.0.tgz",
+ "integrity": "sha512-zksaLKM2fVlnB5jQQDqKXXwYHLQUVH9es+5TOOHwGOVJOCeRBCiPjwSg+3tN2AdTCzjgli4jijCH290kXb/zWQ==",
+ "license": "Apache-2.0"
+ },
"node_modules/web-namespaces": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/web-namespaces/-/web-namespaces-2.0.1.tgz",
@@ -10081,6 +10718,12 @@
"node": ">= 8"
}
},
+ "node_modules/webidl-conversions": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
+ "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==",
+ "license": "BSD-2-Clause"
+ },
"node_modules/whatwg-mimetype": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz",
@@ -10091,6 +10734,16 @@
"node": ">=12"
}
},
+ "node_modules/whatwg-url": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
+ "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
+ "license": "MIT",
+ "dependencies": {
+ "tr46": "~0.0.3",
+ "webidl-conversions": "^3.0.0"
+ }
+ },
"node_modules/which": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
@@ -10361,6 +11014,19 @@
"url": "https://github.com/sponsors/ota-meshi"
}
},
+ "node_modules/yauzl": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-3.2.1.tgz",
+ "integrity": "sha512-k1isifdbpNSFEHFJ1ZY4YDewv0IH9FR61lDetaRMD3j2ae3bIXGV+7c+LHCqtQGofSd8PIyV4X6+dHMAnSr60A==",
+ "license": "MIT",
+ "dependencies": {
+ "buffer-crc32": "~0.2.3",
+ "pend": "~1.2.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
"node_modules/yocto-queue": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
@@ -10381,6 +11047,15 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/zlibjs": {
+ "version": "0.3.1",
+ "resolved": "https://registry.npmjs.org/zlibjs/-/zlibjs-0.3.1.tgz",
+ "integrity": "sha512-+J9RrgTKOmlxFSDHo0pI1xM6BLVUv+o0ZT9ANtCxGkjIVCCUdx9alUF8Gm+dGLKbkkkidWIHFDZHDMpfITt4+w==",
+ "license": "MIT",
+ "engines": {
+ "node": "*"
+ }
+ },
"node_modules/zod": {
"version": "4.3.6",
"resolved": "https://registry.npmjs.org/zod/-/zod-4.3.6.tgz",
diff --git a/package.json b/package.json
index 1ace3fb..33992e7 100644
--- a/package.json
+++ b/package.json
@@ -55,6 +55,7 @@
"highlight.js": "^11.11.1",
"katex": "^0.16.33",
"lowlight": "^3.3.0",
+ "officeparser": "^6.0.4",
"openai": "^6.25.0",
"path-browserify": "^1.0.1",
"regex-parser": "^2.3.1",