mirror of
https://github.com/mudler/LocalAI.git
synced 2025-06-06 02:45:00 +00:00
Support file inputs
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
This commit is contained in:
parent
45c58752e5
commit
956c4ff660
3 changed files with 102 additions and 9 deletions
|
@ -312,7 +312,7 @@ func mergeOpenAIRequestAndBackendConfig(config *config.BackendConfig, input *sch
|
||||||
// Decode content as base64 either if it's an URL or base64 text
|
// Decode content as base64 either if it's an URL or base64 text
|
||||||
base64, err := utils.GetContentURIAsBase64(pp.AudioURL.URL)
|
base64, err := utils.GetContentURIAsBase64(pp.AudioURL.URL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error().Msgf("Failed encoding image: %s", err)
|
log.Error().Msgf("Failed encoding audio: %s", err)
|
||||||
continue CONTENT
|
continue CONTENT
|
||||||
}
|
}
|
||||||
input.Messages[i].StringAudios = append(input.Messages[i].StringAudios, base64) // TODO: make sure that we only return base64 stuff
|
input.Messages[i].StringAudios = append(input.Messages[i].StringAudios, base64) // TODO: make sure that we only return base64 stuff
|
||||||
|
|
|
@ -50,16 +50,91 @@ function submitSystemPrompt(event) {
|
||||||
|
|
||||||
var image = "";
|
var image = "";
|
||||||
var audio = "";
|
var audio = "";
|
||||||
|
var fileContent = "";
|
||||||
|
var currentFileName = "";
|
||||||
|
|
||||||
|
async function extractTextFromPDF(pdfData) {
|
||||||
|
try {
|
||||||
|
const pdf = await pdfjsLib.getDocument({ data: pdfData }).promise;
|
||||||
|
let fullText = '';
|
||||||
|
|
||||||
|
for (let i = 1; i <= pdf.numPages; i++) {
|
||||||
|
const page = await pdf.getPage(i);
|
||||||
|
const textContent = await page.getTextContent();
|
||||||
|
const pageText = textContent.items.map(item => item.str).join(' ');
|
||||||
|
fullText += pageText + '\n';
|
||||||
|
}
|
||||||
|
|
||||||
|
return fullText;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error extracting text from PDF:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function readInputFile() {
|
||||||
|
if (!this.files || !this.files[0]) return;
|
||||||
|
|
||||||
|
const file = this.files[0];
|
||||||
|
const FR = new FileReader();
|
||||||
|
currentFileName = file.name;
|
||||||
|
const fileExtension = file.name.split('.').pop().toLowerCase();
|
||||||
|
|
||||||
|
FR.addEventListener("load", async function(evt) {
|
||||||
|
if (fileExtension === 'pdf') {
|
||||||
|
try {
|
||||||
|
fileContent = await extractTextFromPDF(evt.target.result);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error processing PDF:', error);
|
||||||
|
fileContent = "Error processing PDF file";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// For text and markdown files
|
||||||
|
fileContent = evt.target.result;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (fileExtension === 'pdf') {
|
||||||
|
FR.readAsArrayBuffer(file);
|
||||||
|
} else {
|
||||||
|
FR.readAsText(file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function submitPrompt(event) {
|
function submitPrompt(event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
const input = document.getElementById("input").value;
|
const input = document.getElementById("input").value;
|
||||||
Alpine.store("chat").add("user", input, image, audio);
|
let fullInput = input;
|
||||||
|
|
||||||
|
// If there's file content, append it to the input for the LLM
|
||||||
|
if (fileContent) {
|
||||||
|
fullInput += "\n\nFile content:\n" + fileContent;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show file icon in chat if there's a file
|
||||||
|
let displayContent = input;
|
||||||
|
if (currentFileName) {
|
||||||
|
displayContent += `\n\n<i class="fa-solid fa-file"></i> Attached file: ${currentFileName}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the message to the chat UI with just the icon
|
||||||
|
Alpine.store("chat").add("user", displayContent, image, audio);
|
||||||
|
|
||||||
|
// Update the last message in the store with the full content
|
||||||
|
const history = Alpine.store("chat").history;
|
||||||
|
if (history.length > 0) {
|
||||||
|
history[history.length - 1].content = fullInput;
|
||||||
|
}
|
||||||
|
|
||||||
document.getElementById("input").value = "";
|
document.getElementById("input").value = "";
|
||||||
const systemPrompt = localStorage.getItem("system_prompt");
|
const systemPrompt = localStorage.getItem("system_prompt");
|
||||||
Alpine.nextTick(() => { document.getElementById('messages').scrollIntoView(false); });
|
Alpine.nextTick(() => { document.getElementById('messages').scrollIntoView(false); });
|
||||||
promptGPT(systemPrompt, input);
|
promptGPT(systemPrompt, fullInput);
|
||||||
|
|
||||||
|
// Reset file content and name after sending
|
||||||
|
fileContent = "";
|
||||||
|
currentFileName = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
function readInputImage() {
|
function readInputImage() {
|
||||||
|
@ -88,9 +163,6 @@ function readInputAudio() {
|
||||||
|
|
||||||
async function promptGPT(systemPrompt, input) {
|
async function promptGPT(systemPrompt, input) {
|
||||||
const model = document.getElementById("chat-model").value;
|
const model = document.getElementById("chat-model").value;
|
||||||
// Set class "loader" to the element with "loader" id
|
|
||||||
//document.getElementById("loader").classList.add("loader");
|
|
||||||
// Make the "loader" visible
|
|
||||||
toggleLoader(true);
|
toggleLoader(true);
|
||||||
|
|
||||||
messages = Alpine.store("chat").messages();
|
messages = Alpine.store("chat").messages();
|
||||||
|
@ -261,6 +333,7 @@ document.getElementById("prompt").addEventListener("submit", submitPrompt);
|
||||||
document.getElementById("input").focus();
|
document.getElementById("input").focus();
|
||||||
document.getElementById("input_image").addEventListener("change", readInputImage);
|
document.getElementById("input_image").addEventListener("change", readInputImage);
|
||||||
document.getElementById("input_audio").addEventListener("change", readInputAudio);
|
document.getElementById("input_audio").addEventListener("change", readInputAudio);
|
||||||
|
document.getElementById("input_file").addEventListener("change", readInputFile);
|
||||||
|
|
||||||
storesystemPrompt = localStorage.getItem("system_prompt");
|
storesystemPrompt = localStorage.getItem("system_prompt");
|
||||||
if (storesystemPrompt) {
|
if (storesystemPrompt) {
|
||||||
|
|
|
@ -29,6 +29,11 @@ SOFTWARE.
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
{{template "views/partials/head" .}}
|
{{template "views/partials/head" .}}
|
||||||
<script defer src="static/chat.js"></script>
|
<script defer src="static/chat.js"></script>
|
||||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.min.js"></script>
|
||||||
|
<script>
|
||||||
|
// Initialize PDF.js worker
|
||||||
|
pdfjsLib.GlobalWorkerOptions.workerSrc = 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.worker.min.js';
|
||||||
|
</script>
|
||||||
{{ $allGalleryConfigs:=.GalleryConfig }}
|
{{ $allGalleryConfigs:=.GalleryConfig }}
|
||||||
{{ $model:=.Model}}
|
{{ $model:=.Model}}
|
||||||
<body class="bg-slate-900 text-gray-100 flex flex-col h-screen" x-data="{ sidebarOpen: true }">
|
<body class="bg-slate-900 text-gray-100 flex flex-col h-screen" x-data="{ sidebarOpen: true }">
|
||||||
|
@ -215,11 +220,13 @@ SOFTWARE.
|
||||||
<!-- Chat messages area -->
|
<!-- Chat messages area -->
|
||||||
<div class="flex-1 p-4 overflow-auto" id="chat" x-data="{history: $store.chat.history}">
|
<div class="flex-1 p-4 overflow-auto" id="chat" x-data="{history: $store.chat.history}">
|
||||||
<p id="usage" x-show="history.length === 0" class="text-gray-300">
|
<p id="usage" x-show="history.length === 0" class="text-gray-300">
|
||||||
Start chatting with the AI by typing a prompt in the input field below and pressing Enter.
|
Start chatting with the AI by typing a prompt in the input field below and pressing Enter.<br>
|
||||||
For models that support images, you can upload an image by clicking the paperclip
|
For models that support images, you can upload an image by clicking the paperclip
|
||||||
<i class="fa-solid fa-paperclip"></i> icon.
|
<i class="fa-solid fa-paperclip"></i> icon. <br>
|
||||||
For models that support audio, you can upload an audio file by clicking the microphone
|
For models that support audio, you can upload an audio file by clicking the microphone
|
||||||
<i class="fa-solid fa-microphone"></i> icon.
|
<i class="fa-solid fa-microphone"></i> icon. <br>
|
||||||
|
To send a text, markdown or PDF file, click the file
|
||||||
|
<i class="fa-solid fa-file"></i> icon.
|
||||||
</p>
|
</p>
|
||||||
<div id="messages" class="max-w-3xl mx-auto">
|
<div id="messages" class="max-w-3xl mx-auto">
|
||||||
<template x-for="message in history">
|
<template x-for="message in history">
|
||||||
|
@ -298,6 +305,12 @@ SOFTWARE.
|
||||||
class="fa-solid fa-microphone text-gray-400 absolute right-20 top-4 text-lg p-2 hover:text-blue-400 transition-colors duration-200"
|
class="fa-solid fa-microphone text-gray-400 absolute right-20 top-4 text-lg p-2 hover:text-blue-400 transition-colors duration-200"
|
||||||
title="Attach an audio file"
|
title="Attach an audio file"
|
||||||
></button>
|
></button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onclick="document.getElementById('input_file').click()"
|
||||||
|
class="fa-solid fa-file text-gray-400 absolute right-28 top-4 text-lg p-2 hover:text-blue-400 transition-colors duration-200"
|
||||||
|
title="Upload text, markdown or PDF file"
|
||||||
|
></button>
|
||||||
|
|
||||||
<!-- Send button and loader in the same position -->
|
<!-- Send button and loader in the same position -->
|
||||||
<div class="absolute right-3 top-4">
|
<div class="absolute right-3 top-4">
|
||||||
|
@ -335,6 +348,13 @@ SOFTWARE.
|
||||||
style="display: none;"
|
style="display: none;"
|
||||||
@change="fileName = $event.target.files[0].name"
|
@change="fileName = $event.target.files[0].name"
|
||||||
/>
|
/>
|
||||||
|
<input
|
||||||
|
id="input_file"
|
||||||
|
type="file"
|
||||||
|
accept=".txt,.md,.pdf"
|
||||||
|
style="display: none;"
|
||||||
|
@change="fileName = $event.target.files[0].name"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue