summaryrefslogtreecommitdiff
path: root/llama.cpp/tools/server/webui/src/lib/utils/process-uploaded-files.ts
diff options
context:
space:
mode:
authorMitja Felicijan <mitja.felicijan@gmail.com>2026-02-12 20:57:17 +0100
committerMitja Felicijan <mitja.felicijan@gmail.com>2026-02-12 20:57:17 +0100
commitb333b06772c89d96aacb5490d6a219fba7c09cc6 (patch)
tree211df60083a5946baa2ed61d33d8121b7e251b06 /llama.cpp/tools/server/webui/src/lib/utils/process-uploaded-files.ts
downloadllmnpc-b333b06772c89d96aacb5490d6a219fba7c09cc6.tar.gz
Engage!
Diffstat (limited to 'llama.cpp/tools/server/webui/src/lib/utils/process-uploaded-files.ts')
-rw-r--r--llama.cpp/tools/server/webui/src/lib/utils/process-uploaded-files.ts136
1 files changed, 136 insertions, 0 deletions
diff --git a/llama.cpp/tools/server/webui/src/lib/utils/process-uploaded-files.ts b/llama.cpp/tools/server/webui/src/lib/utils/process-uploaded-files.ts
new file mode 100644
index 0000000..0342dce
--- /dev/null
+++ b/llama.cpp/tools/server/webui/src/lib/utils/process-uploaded-files.ts
@@ -0,0 +1,136 @@
+import { isSvgMimeType, svgBase64UrlToPngDataURL } from './svg-to-png';
+import { isWebpMimeType, webpBase64UrlToPngDataURL } from './webp-to-png';
+import { FileTypeCategory } from '$lib/enums';
+import { modelsStore } from '$lib/stores/models.svelte';
+import { settingsStore } from '$lib/stores/settings.svelte';
+import { toast } from 'svelte-sonner';
+import { getFileTypeCategory } from '$lib/utils';
+import { convertPDFToText } from './pdf-processing';
+
+/**
+ * Read a file as a data URL (base64 encoded)
+ * @param file - The file to read
+ * @returns Promise resolving to the data URL string
+ */
+function readFileAsDataURL(file: File): Promise<string> {
+ return new Promise((resolve, reject) => {
+ const reader = new FileReader();
+ reader.onload = () => resolve(reader.result as string);
+ reader.onerror = () => reject(reader.error);
+ reader.readAsDataURL(file);
+ });
+}
+
+/**
+ * Read a file as UTF-8 text
+ * @param file - The file to read
+ * @returns Promise resolving to the text content
+ */
+function readFileAsUTF8(file: File): Promise<string> {
+ return new Promise((resolve, reject) => {
+ const reader = new FileReader();
+ reader.onload = () => resolve(reader.result as string);
+ reader.onerror = () => reject(reader.error);
+ reader.readAsText(file);
+ });
+}
+
+/**
+ * Process uploaded files into ChatUploadedFile format with previews and content
+ *
+ * This function processes various file types and generates appropriate previews:
+ * - Images: Base64 data URLs with format normalization (SVG/WebP → PNG)
+ * - Text files: UTF-8 content extraction
+ * - PDFs: Metadata only (processed later in conversion pipeline)
+ * - Audio: Base64 data URLs for preview
+ *
+ * @param files - Array of File objects to process
+ * @returns Promise resolving to array of ChatUploadedFile objects
+ */
+export async function processFilesToChatUploaded(
+ files: File[],
+ activeModelId?: string
+): Promise<ChatUploadedFile[]> {
+ const results: ChatUploadedFile[] = [];
+
+ for (const file of files) {
+ const id = Date.now().toString() + Math.random().toString(36).substr(2, 9);
+ const base: ChatUploadedFile = {
+ id,
+ name: file.name,
+ size: file.size,
+ type: file.type,
+ file
+ };
+
+ try {
+ if (getFileTypeCategory(file.type) === FileTypeCategory.IMAGE) {
+ let preview = await readFileAsDataURL(file);
+
+ // Normalize SVG and WebP to PNG in previews
+ if (isSvgMimeType(file.type)) {
+ try {
+ preview = await svgBase64UrlToPngDataURL(preview);
+ } catch (err) {
+ console.error('Failed to convert SVG to PNG:', err);
+ }
+ } else if (isWebpMimeType(file.type)) {
+ try {
+ preview = await webpBase64UrlToPngDataURL(preview);
+ } catch (err) {
+ console.error('Failed to convert WebP to PNG:', err);
+ }
+ }
+
+ results.push({ ...base, preview });
+ } else if (getFileTypeCategory(file.type) === FileTypeCategory.PDF) {
+ // Extract text content from PDF for preview
+ try {
+ const textContent = await convertPDFToText(file);
+ results.push({ ...base, textContent });
+ } catch (err) {
+ console.warn('Failed to extract text from PDF, adding without content:', err);
+ results.push(base);
+ }
+
+ // Show suggestion toast if vision model is available but PDF as image is disabled
+ const hasVisionSupport = activeModelId
+ ? modelsStore.modelSupportsVision(activeModelId)
+ : false;
+ const currentConfig = settingsStore.config;
+ if (hasVisionSupport && !currentConfig.pdfAsImage) {
+ toast.info(`You can enable parsing PDF as images with vision models.`, {
+ duration: 8000,
+ action: {
+ label: 'Enable PDF as Images',
+ onClick: () => {
+ settingsStore.updateConfig('pdfAsImage', true);
+ toast.success('PDF parsing as images enabled!', {
+ duration: 3000
+ });
+ }
+ }
+ });
+ }
+ } else if (getFileTypeCategory(file.type) === FileTypeCategory.AUDIO) {
+ // Generate preview URL for audio files
+ const preview = await readFileAsDataURL(file);
+ results.push({ ...base, preview });
+ } else {
+ // Fallback: treat unknown files as text
+ try {
+ const textContent = await readFileAsUTF8(file);
+ results.push({ ...base, textContent });
+ } catch (err) {
+ console.warn('Failed to read file as text, adding without content:', err);
+ results.push(base);
+ }
+ }
+ } catch (error) {
+ console.error('Error processing file', file.name, error);
+ results.push(base);
+ }
+ }
+
+ return results;
+}