Skip to content

server : (webui) add support for .pdf file upload #11647

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file modified examples/server/public/index.html.gz
Binary file not shown.
43 changes: 33 additions & 10 deletions examples/server/webui/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -149,19 +149,42 @@ <h2 class="font-bold ml-4">Conversations</h2>

<!-- chat input -->
<div class="flex flex-row items-center mt-8 mb-6">
<textarea
class="textarea textarea-bordered w-full"
placeholder="Type a message (Shift+Enter to add a new line)"
v-model="inputMsg"
@keydown.enter.exact.prevent="sendMessage"
@keydown.enter.shift.exact.prevent="inputMsg += '\n'"
:disabled="isGenerating"
id="msg-input"
dir="auto"
></textarea>
<div class="relative w-full">
<textarea
class="textarea textarea-bordered w-full"
placeholder="Type a message (Shift+Enter to add a new line)"
v-model="inputMsg"
@keydown.enter.exact.prevent="sendMessage"
@keydown.enter.shift.exact.prevent="inputMsg += '\n'"
:disabled="isGenerating"
id="msg-input"
dir="auto"
></textarea>
<!-- file-upload button -->
<label for="pdf-upload" class="absolute top-2 right-2 cursor-pointer">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6 text-gray-500 hover:text-black">
<path stroke-linecap="round" stroke-linejoin="round" d="M18.364 5.636a4.5 4.5 0 0 1 0 6.364l-6.364 6.364a4.5 4.5 0 0 1-6.364-6.364l6.364-6.364a3 3 0 0 1 4.243 4.243l-6.364 6.364a1.5 1.5 0 0 1-2.121-2.121l6.364-6.364" />
</svg>
</label>
<input id="pdf-upload" type="file" class="hidden" accept=".pdf" @change="handlePdfUpload" />
</div>
<button v-if="!isGenerating" class="btn btn-primary ml-2" @click="sendMessage" :disabled="inputMsg.length === 0">Send</button>
<button v-else class="btn btn-neutral ml-2" @click="stopGeneration">Stop</button>
</div>
<!-- section to display uploaded files -->
<div class="flex flex-wrap">
<div v-for="([fileName, text]) in Array.from(fileTextMap.entries())" :key="fileName" class="flex items-center mr-4 mb-2">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6 text-blue-500">
<path stroke-linecap="round" stroke-linejoin="round" d="M12 3.75v16.5m4.5-4.5H7.5M9 21h6a3 3 0 003-3V6a3 3 0 00-3-3H9a3 3 0 00-3 3v12a3 3 0 003 3z" />
</svg>
<span class="text-sm ml-1">{{ fileName }}</span>
<button @click="removeFile(fileName)" class="ml-2 text-grey-500 hover:text-red-700">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-5 h-5">
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" />
</svg>
</button>
</div>
</div>
</div>

</div>
Expand Down
183 changes: 183 additions & 0 deletions examples/server/webui/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions examples/server/webui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"highlight.js": "^11.10.0",
"katex": "^0.16.15",
"markdown-it": "^14.1.0",
"pdfjs-dist": "^4.10.38",
"postcss": "^8.4.49",
"tailwindcss": "^3.4.15",
"textlinestream": "^1.1.1",
Expand Down
67 changes: 66 additions & 1 deletion examples/server/webui/src/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@ import daisyuiThemes from 'daisyui/src/theming/themes';
// ponyfill for missing ReadableStream asyncIterator on Safari
import { asyncIterator } from '@sec-ant/readable-stream/ponyfill/asyncIterator';

// pdf parsing
import * as pdfjsLib from "pdfjs-dist";
import pdfWorker from "pdfjs-dist/build/pdf.worker.mjs?url";
pdfjsLib.GlobalWorkerOptions.workerSrc = pdfWorker;

const isDev = import.meta.env.MODE === 'development';

// types
Expand Down Expand Up @@ -387,6 +392,7 @@ const mainApp = createApp({
viewingConvId: StorageUtils.getNewConvId(),
inputMsg: '',
isGenerating: false,
fileTextMap: new Map(),
/** @type {Array<Message> | null} */
pendingMsg: null, // the on-going message from assistant
stopGeneration: () => {},
Expand Down Expand Up @@ -467,18 +473,51 @@ const mainApp = createApp({
document.body.removeChild(a);
URL.revokeObjectURL(url);
},
removeFile(filename){
this.fileTextMap.delete(filename);
},
async handlePdfUpload(event) {
const file = event.target.files[0];
if (file && file.type === "application/pdf") {
try {
const arrayBuffer = await file.arrayBuffer();
const loadingTask = pdfjsLib.getDocument({ data: new Uint8Array(arrayBuffer) });

loadingTask.promise.then(pdfDocument => {
console.log("PDF loaded:", pdfDocument);
const pdfPromise = extractPdfText(file);
pdfPromise.then((data) => {
this.fileTextMap.set(file.name, data);
})
.catch((error) => {
console.log(error)
});
}).catch(error => {
console.error("Error loading PDF:", error);
});
} catch (error) {
console.error("Error extracting PDF text:", error);
}
} else {
alert("Please upload a valid PDF file.");
}
},
async sendMessage() {
if (!this.inputMsg) return;
const currConvId = this.viewingConvId;
const fileTexts = await Promise.all([...this.fileTextMap.values()]);
const combinedFileText = fileTexts.join('\n');

StorageUtils.appendMsg(currConvId, {
id: Date.now(),
role: 'user',
content: this.inputMsg,
content: combinedFileText + '\n' + this.inputMsg,
});
this.fetchConversation();
this.fetchMessages();
this.inputMsg = '';
this.fileTextMap.clear();
this.fileText = '';
this.generateMessage(currConvId);
chatScrollToBottom();
},
Expand Down Expand Up @@ -669,6 +708,32 @@ try {
<button class="btn" onClick="localStorage.clear(); window.location.reload();">Clear localStorage</button>
</div>`;
}
/**
* extracts text content from a given PDF file using pdf.js
* @param {File} file
* @returns {Promise<string>}
*/
async function extractPdfText(file) {
const fileReader = new FileReader();
return new Promise((resolve, reject) => {
fileReader.onload = async (e) => {
const pdfData = new Uint8Array(e.target.result);
try {
const pdf = await pdfjsLib.getDocument(pdfData).promise;
let textContent = "";
for (let i = 1; i <= pdf.numPages; i++) {
const page = await pdf.getPage(i);
const textContentPage = await page.getTextContent();
textContent += textContentPage.items.map(item => item.str).join(" ") + "\n";
}
resolve(textContent.trim());
} catch (error) {
reject(error);
}
};
fileReader.readAsArrayBuffer(file);
});
}

/**
* filter out redundant fields upon sending to API
Expand Down
2 changes: 1 addition & 1 deletion examples/server/webui/vite.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import path from 'path';
import fs from 'fs';
import zlib from 'zlib';

const MAX_BUNDLE_SIZE = 1.5 * 1024 * 1024; // only increase when absolutely necessary
const MAX_BUNDLE_SIZE = 2 * 1024 * 1024; // only increase when absolutely necessary

const GUIDE_FOR_FRONTEND = `
<!--
Expand Down