refactor: Architecture improvements

This commit is contained in:
Aleksander Grygier 2025-11-20 16:34:25 +01:00
parent 55d33a8b8c
commit 8a88576849
13 changed files with 180 additions and 113 deletions

View File

@ -23,8 +23,8 @@ import type {
ApiRouterModelsUnloadResponse ApiRouterModelsUnloadResponse
} from '$lib/types/api'; } from '$lib/types/api';
import { ServerMode } from '$lib/enums/server'; import { ServerMode, ServerModelStatus } from '$lib/enums/server';
import { ServerModelStatus } from '$lib/enums/model'; import { ModelModality } from '$lib/enums/model';
import type { import type {
ChatMessageType, ChatMessageType,
@ -79,8 +79,6 @@ declare global {
ApiRouterModelsListResponse, ApiRouterModelsListResponse,
ApiRouterModelsUnloadRequest, ApiRouterModelsUnloadRequest,
ApiRouterModelsUnloadResponse, ApiRouterModelsUnloadResponse,
ServerMode,
ServerModelStatus,
ChatMessageData, ChatMessageData,
ChatMessagePromptProgress, ChatMessagePromptProgress,
ChatMessageSiblingInfo, ChatMessageSiblingInfo,
@ -96,6 +94,9 @@ declare global {
DatabaseMessageExtraTextFile, DatabaseMessageExtraTextFile,
DatabaseMessageExtraPdfFile, DatabaseMessageExtraPdfFile,
DatabaseMessageExtraLegacyContext, DatabaseMessageExtraLegacyContext,
ModelModality,
ServerMode,
ServerModelStatus,
SettingsConfigValue, SettingsConfigValue,
SettingsFieldConfig, SettingsFieldConfig,
SettingsConfigType, SettingsConfigType,

View File

@ -1,8 +1,11 @@
<script lang="ts"> <script lang="ts">
import { Button } from '$lib/components/ui/button';
import { FileText, Image, Music, FileIcon, Eye } from '@lucide/svelte'; import { FileText, Image, Music, FileIcon, Eye } from '@lucide/svelte';
import { FileTypeCategory, MimeTypeApplication } from '$lib/enums/files'; import { FileTypeCategory, MimeTypeApplication } from '$lib/enums/files';
import { ModelModality } from '$lib/enums/model';
import { AttachmentType } from '$lib/enums/attachment';
import type { DatabaseMessageExtra } from '$lib/types/database';
import { convertPDFToImage } from '$lib/utils/pdf-processing'; import { convertPDFToImage } from '$lib/utils/pdf-processing';
import { Button } from '$lib/components/ui/button';
import { getFileTypeCategory } from '$lib/utils/file-type'; import { getFileTypeCategory } from '$lib/utils/file-type';
interface Props { interface Props {
@ -21,33 +24,36 @@
let displayName = $derived(uploadedFile?.name || attachment?.name || name || 'Unknown File'); let displayName = $derived(uploadedFile?.name || attachment?.name || name || 'Unknown File');
let displayPreview = $derived( let displayPreview = $derived(
uploadedFile?.preview || (attachment?.type === 'imageFile' ? attachment.base64Url : preview) uploadedFile?.preview ||
(attachment?.type === AttachmentType.IMAGE ? attachment.base64Url : preview)
); );
let displayType = $derived( let displayType = $derived(
uploadedFile?.type || uploadedFile
(attachment?.type === 'imageFile' ? uploadedFile.type
: attachment?.type === AttachmentType.IMAGE
? 'image' ? 'image'
: attachment?.type === 'textFile' : attachment?.type === AttachmentType.TEXT
? 'text' ? 'text'
: attachment?.type === 'audioFile' : attachment?.type === AttachmentType.AUDIO
? attachment.mimeType || 'audio' ? attachment.mimeType || ModelModality.AUDIO
: attachment?.type === 'pdfFile' : attachment?.type === AttachmentType.PDF
? MimeTypeApplication.PDF ? MimeTypeApplication.PDF
: type || 'unknown') : type || 'unknown'
); );
let displayTextContent = $derived( let displayTextContent = $derived(
uploadedFile?.textContent || uploadedFile?.textContent ||
(attachment?.type === 'textFile' (attachment?.type === AttachmentType.TEXT
? attachment.content ? attachment.content
: attachment?.type === 'pdfFile' : attachment?.type === AttachmentType.PDF
? attachment.content ? attachment.content
: textContent) : textContent)
); );
let isAudio = $derived( let isAudio = $derived(
getFileTypeCategory(displayType) === FileTypeCategory.AUDIO || displayType === 'audio' getFileTypeCategory(displayType) === FileTypeCategory.AUDIO ||
displayType === ModelModality.AUDIO
); );
let isImage = $derived( let isImage = $derived(
@ -87,9 +93,9 @@
if (uploadedFile?.file) { if (uploadedFile?.file) {
file = uploadedFile.file; file = uploadedFile.file;
} else if (attachment?.type === 'pdfFile') { } else if (attachment?.type === AttachmentType.PDF) {
// Check if we have pre-processed images // Check if we have pre-processed images
if (attachment.images && Array.isArray(attachment.images)) { if (attachment.images && Array.isArray(attachment.images) && attachment.images.length > 0) {
pdfImages = attachment.images; pdfImages = attachment.images;
return; return;
} }
@ -237,7 +243,7 @@
<div class="w-full max-w-md text-center"> <div class="w-full max-w-md text-center">
<Music class="mx-auto mb-4 h-16 w-16 text-muted-foreground" /> <Music class="mx-auto mb-4 h-16 w-16 text-muted-foreground" />
{#if attachment?.type === 'audioFile'} {#if attachment?.type === AttachmentType.AUDIO}
<audio <audio
controls controls
class="mb-4 w-full" class="mb-4 w-full"

View File

@ -2,10 +2,13 @@
import { ChatAttachmentThumbnailImage, ChatAttachmentThumbnailFile } from '$lib/components/app'; import { ChatAttachmentThumbnailImage, ChatAttachmentThumbnailFile } from '$lib/components/app';
import { Button } from '$lib/components/ui/button'; import { Button } from '$lib/components/ui/button';
import { ChevronLeft, ChevronRight } from '@lucide/svelte'; import { ChevronLeft, ChevronRight } from '@lucide/svelte';
import { FileTypeCategory } from '$lib/enums/files';
import { getFileTypeCategory } from '$lib/utils/file-type'; import { getFileTypeCategory } from '$lib/utils/file-type';
import { FileTypeCategory } from '$lib/enums/files';
import { ModelModality } from '$lib/enums/model';
import { AttachmentType } from '$lib/enums/attachment';
import { DialogChatAttachmentPreview, DialogChatAttachmentsViewAll } from '$lib/components/app'; import { DialogChatAttachmentPreview, DialogChatAttachmentsViewAll } from '$lib/components/app';
import type { ChatAttachmentDisplayItem, ChatAttachmentPreviewItem } from '$lib/types/chat'; import type { ChatAttachmentDisplayItem, ChatAttachmentPreviewItem } from '$lib/types/chat';
import type { DatabaseMessageExtra } from '$lib/types/database';
interface Props { interface Props {
class?: string; class?: string;
@ -68,7 +71,7 @@
// Add stored attachments (ChatMessage) // Add stored attachments (ChatMessage)
for (const [index, attachment] of attachments.entries()) { for (const [index, attachment] of attachments.entries()) {
if (attachment.type === 'imageFile') { if (attachment.type === AttachmentType.IMAGE) {
items.push({ items.push({
id: `attachment-${index}`, id: `attachment-${index}`,
name: attachment.name, name: attachment.name,
@ -78,7 +81,7 @@
attachment, attachment,
attachmentIndex: index attachmentIndex: index
}); });
} else if (attachment.type === 'textFile') { } else if (attachment.type === AttachmentType.TEXT) {
items.push({ items.push({
id: `attachment-${index}`, id: `attachment-${index}`,
name: attachment.name, name: attachment.name,
@ -88,7 +91,25 @@
attachmentIndex: index, attachmentIndex: index,
textContent: attachment.content textContent: attachment.content
}); });
} else if (attachment.type === 'context') { } else if (attachment.type === AttachmentType.AUDIO) {
items.push({
id: `attachment-${index}`,
name: attachment.name,
type: attachment.mimeType || ModelModality.AUDIO,
isImage: false,
attachment,
attachmentIndex: index
});
} else if (attachment.type === AttachmentType.PDF) {
items.push({
id: `attachment-${index}`,
name: attachment.name,
type: 'application/pdf',
isImage: false,
attachment,
attachmentIndex: index
});
} else if (attachment.type === AttachmentType.LEGACY_CONTEXT) {
// Legacy format from old webui - treat as text file // Legacy format from old webui - treat as text file
items.push({ items.push({
id: `attachment-${index}`, id: `attachment-${index}`,
@ -99,25 +120,6 @@
attachmentIndex: index, attachmentIndex: index,
textContent: attachment.content textContent: attachment.content
}); });
} else if (attachment.type === 'audioFile') {
items.push({
id: `attachment-${index}`,
name: attachment.name,
type: attachment.mimeType || 'audio',
isImage: false,
attachment,
attachmentIndex: index
});
} else if (attachment.type === 'pdfFile') {
items.push({
id: `attachment-${index}`,
name: attachment.name,
type: 'application/pdf',
isImage: false,
attachment,
attachmentIndex: index,
textContent: attachment.content
});
} }
} }

View File

@ -5,8 +5,11 @@
DialogChatAttachmentPreview DialogChatAttachmentPreview
} from '$lib/components/app'; } from '$lib/components/app';
import { FileTypeCategory } from '$lib/enums/files'; import { FileTypeCategory } from '$lib/enums/files';
import { ModelModality } from '$lib/enums/model';
import { AttachmentType } from '$lib/enums/attachment';
import { getFileTypeCategory } from '$lib/utils/file-type'; import { getFileTypeCategory } from '$lib/utils/file-type';
import type { ChatAttachmentDisplayItem, ChatAttachmentPreviewItem } from '$lib/types/chat'; import type { ChatAttachmentDisplayItem, ChatAttachmentPreviewItem } from '$lib/types/chat';
import type { DatabaseMessageExtra } from '$lib/types/database';
interface Props { interface Props {
uploadedFiles?: ChatUploadedFile[]; uploadedFiles?: ChatUploadedFile[];
@ -52,7 +55,7 @@
} }
for (const [index, attachment] of attachments.entries()) { for (const [index, attachment] of attachments.entries()) {
if (attachment.type === 'imageFile') { if (attachment.type === AttachmentType.IMAGE) {
items.push({ items.push({
id: `attachment-${index}`, id: `attachment-${index}`,
name: attachment.name, name: attachment.name,
@ -62,7 +65,7 @@
attachment, attachment,
attachmentIndex: index attachmentIndex: index
}); });
} else if (attachment.type === 'textFile') { } else if (attachment.type === AttachmentType.TEXT) {
items.push({ items.push({
id: `attachment-${index}`, id: `attachment-${index}`,
name: attachment.name, name: attachment.name,
@ -72,7 +75,25 @@
attachmentIndex: index, attachmentIndex: index,
textContent: attachment.content textContent: attachment.content
}); });
} else if (attachment.type === 'context') { } else if (attachment.type === AttachmentType.AUDIO) {
items.push({
id: `attachment-${index}`,
name: attachment.name,
type: attachment.mimeType || ModelModality.AUDIO,
isImage: false,
attachment,
attachmentIndex: index
});
} else if (attachment.type === AttachmentType.PDF) {
items.push({
id: `attachment-${index}`,
name: attachment.name,
type: 'application/pdf',
isImage: false,
attachment,
attachmentIndex: index
});
} else if (attachment.type === AttachmentType.LEGACY_CONTEXT) {
// Legacy format from old webui - treat as text file // Legacy format from old webui - treat as text file
items.push({ items.push({
id: `attachment-${index}`, id: `attachment-${index}`,
@ -83,25 +104,6 @@
attachmentIndex: index, attachmentIndex: index,
textContent: attachment.content textContent: attachment.content
}); });
} else if (attachment.type === 'audioFile') {
items.push({
id: `attachment-${index}`,
name: attachment.name,
type: attachment.mimeType || 'audio',
isImage: false,
attachment,
attachmentIndex: index
});
} else if (attachment.type === 'pdfFile') {
items.push({
id: `attachment-${index}`,
name: attachment.name,
type: 'application/pdf',
isImage: false,
attachment,
attachmentIndex: index,
textContent: attachment.content
});
} }
} }

View File

@ -1,10 +1,14 @@
<script lang="ts"> <script lang="ts">
import * as Dialog from '$lib/components/ui/dialog'; import * as Dialog from '$lib/components/ui/dialog';
import { ModelModality } from '$lib/enums/model';
import { AttachmentType } from '$lib/enums/attachment';
import type { DatabaseMessageExtra } from '$lib/types/database';
import { ChatAttachmentPreview } from '$lib/components/app'; import { ChatAttachmentPreview } from '$lib/components/app';
import { formatFileSize } from '$lib/utils/file-preview'; import { formatFileSize } from '$lib/utils/file-preview';
interface Props { interface Props {
open: boolean; open: boolean;
onOpenChange?: (open: boolean) => void;
// Either an uploaded file or a stored attachment // Either an uploaded file or a stored attachment
uploadedFile?: ChatUploadedFile; uploadedFile?: ChatUploadedFile;
attachment?: DatabaseMessageExtra; attachment?: DatabaseMessageExtra;
@ -18,6 +22,7 @@
let { let {
open = $bindable(), open = $bindable(),
onOpenChange,
uploadedFile, uploadedFile,
attachment, attachment,
preview, preview,
@ -32,16 +37,17 @@
let displayName = $derived(uploadedFile?.name || attachment?.name || name || 'Unknown File'); let displayName = $derived(uploadedFile?.name || attachment?.name || name || 'Unknown File');
let displayType = $derived( let displayType = $derived(
uploadedFile?.type || uploadedFile
(attachment?.type === 'imageFile' ? uploadedFile.type
: attachment?.type === AttachmentType.IMAGE
? 'image' ? 'image'
: attachment?.type === 'textFile' : attachment?.type === AttachmentType.TEXT
? 'text' ? 'text'
: attachment?.type === 'audioFile' : attachment?.type === AttachmentType.AUDIO
? attachment.mimeType || 'audio' ? attachment.mimeType || ModelModality.AUDIO
: attachment?.type === 'pdfFile' : attachment?.type === AttachmentType.PDF
? 'application/pdf' ? 'application/pdf'
: type || 'unknown') : type || 'unknown'
); );
let displaySize = $derived(uploadedFile?.size || size); let displaySize = $derived(uploadedFile?.size || size);
@ -53,7 +59,7 @@
}); });
</script> </script>
<Dialog.Root bind:open> <Dialog.Root bind:open {onOpenChange}>
<Dialog.Content class="grid max-h-[90vh] max-w-5xl overflow-hidden sm:w-auto sm:max-w-6xl"> <Dialog.Content class="grid max-h-[90vh] max-w-5xl overflow-hidden sm:w-auto sm:max-w-6xl">
<Dialog.Header> <Dialog.Header>
<Dialog.Title>{displayName}</Dialog.Title> <Dialog.Title>{displayName}</Dialog.Title>

View File

@ -0,0 +1,10 @@
/**
* Attachment type enum for database message extras
*/
export enum AttachmentType {
AUDIO = 'AUDIO',
IMAGE = 'IMAGE',
PDF = 'PDF',
TEXT = 'TEXT',
LEGACY_CONTEXT = 'context' // Legacy attachment type for backward compatibility
}

View File

@ -1,9 +1,5 @@
/** export enum ModelModality {
* Model status enum - matches tools/server/server-models.h from C++ server TEXT = 'TEXT',
*/ AUDIO = 'AUDIO',
export enum ServerModelStatus { VISION = 'VISION'
UNLOADED = 'UNLOADED',
LOADING = 'LOADING',
LOADED = 'LOADED',
FAILED = 'FAILED'
} }

View File

@ -7,3 +7,13 @@ export enum ServerMode {
/** Router mode - server managing multiple model instances */ /** Router mode - server managing multiple model instances */
ROUTER = 'ROUTER' ROUTER = 'ROUTER'
} }
/**
* Model status enum - matches tools/server/server-models.h from C++ server
*/
export enum ServerModelStatus {
UNLOADED = 'UNLOADED',
LOADING = 'LOADING',
LOADED = 'LOADED',
FAILED = 'FAILED'
}

View File

@ -7,8 +7,10 @@ import type {
ApiChatCompletionStreamChunk, ApiChatCompletionStreamChunk,
ApiChatCompletionToolCall, ApiChatCompletionToolCall,
ApiChatCompletionToolCallDelta, ApiChatCompletionToolCallDelta,
ApiChatMessageData ApiChatMessageData,
ApiModelListResponse
} from '$lib/types/api'; } from '$lib/types/api';
import { AttachmentType } from '$lib/enums/attachment';
import type { import type {
DatabaseMessage, DatabaseMessage,
DatabaseMessageExtra, DatabaseMessageExtra,
@ -618,7 +620,7 @@ export class ChatService {
const imageFiles = message.extra.filter( const imageFiles = message.extra.filter(
(extra: DatabaseMessageExtra): extra is DatabaseMessageExtraImageFile => (extra: DatabaseMessageExtra): extra is DatabaseMessageExtraImageFile =>
extra.type === 'imageFile' extra.type === AttachmentType.IMAGE
); );
for (const image of imageFiles) { for (const image of imageFiles) {
@ -630,7 +632,7 @@ export class ChatService {
const textFiles = message.extra.filter( const textFiles = message.extra.filter(
(extra: DatabaseMessageExtra): extra is DatabaseMessageExtraTextFile => (extra: DatabaseMessageExtra): extra is DatabaseMessageExtraTextFile =>
extra.type === 'textFile' extra.type === AttachmentType.TEXT
); );
for (const textFile of textFiles) { for (const textFile of textFiles) {
@ -643,7 +645,7 @@ export class ChatService {
// Handle legacy 'context' type from old webui (pasted content) // Handle legacy 'context' type from old webui (pasted content)
const legacyContextFiles = message.extra.filter( const legacyContextFiles = message.extra.filter(
(extra: DatabaseMessageExtra): extra is DatabaseMessageExtraLegacyContext => (extra: DatabaseMessageExtra): extra is DatabaseMessageExtraLegacyContext =>
extra.type === 'context' extra.type === AttachmentType.LEGACY_CONTEXT
); );
for (const legacyContextFile of legacyContextFiles) { for (const legacyContextFile of legacyContextFiles) {
@ -655,7 +657,7 @@ export class ChatService {
const audioFiles = message.extra.filter( const audioFiles = message.extra.filter(
(extra: DatabaseMessageExtra): extra is DatabaseMessageExtraAudioFile => (extra: DatabaseMessageExtra): extra is DatabaseMessageExtraAudioFile =>
extra.type === 'audioFile' extra.type === AttachmentType.AUDIO
); );
for (const audio of audioFiles) { for (const audio of audioFiles) {
@ -670,7 +672,7 @@ export class ChatService {
const pdfFiles = message.extra.filter( const pdfFiles = message.extra.filter(
(extra: DatabaseMessageExtra): extra is DatabaseMessageExtraPdfFile => (extra: DatabaseMessageExtra): extra is DatabaseMessageExtraPdfFile =>
extra.type === 'pdfFile' extra.type === AttachmentType.PDF
); );
for (const pdfFile of pdfFiles) { for (const pdfFile of pdfFiles) {
@ -722,6 +724,33 @@ export class ChatService {
} }
} }
/**
* Get model information from /models endpoint
*/
static async getModels(): Promise<ApiModelListResponse> {
try {
const currentConfig = config();
const apiKey = currentConfig.apiKey?.toString().trim();
const response = await fetch(`./models`, {
headers: {
'Content-Type': 'application/json',
...(apiKey ? { Authorization: `Bearer ${apiKey}` } : {})
}
});
if (!response.ok) {
throw new Error(`Failed to fetch models: ${response.status} ${response.statusText}`);
}
const data = await response.json();
return data;
} catch (error) {
console.error('Error fetching models:', error);
throw error;
}
}
/** /**
* Aborts any ongoing chat completion request. * Aborts any ongoing chat completion request.
* Cancels the current request and cleans up the abort controller. * Cancels the current request and cleans up the abort controller.

View File

@ -3,6 +3,7 @@ import { SERVER_PROPS_LOCALSTORAGE_KEY } from '$lib/constants/localstorage-keys'
import { ChatService } from '$lib/services/chat'; import { ChatService } from '$lib/services/chat';
import { config } from '$lib/stores/settings.svelte'; import { config } from '$lib/stores/settings.svelte';
import { ServerMode } from '$lib/enums/server'; import { ServerMode } from '$lib/enums/server';
import { ModelModality } from '$lib/enums/model';
import { updateConfig } from '$lib/stores/settings.svelte'; import { updateConfig } from '$lib/stores/settings.svelte';
/** /**
@ -115,10 +116,10 @@ class ServerStore {
get supportedModalities(): string[] { get supportedModalities(): string[] {
const modalities: string[] = []; const modalities: string[] = [];
if (this._serverProps?.modalities?.audio) { if (this._serverProps?.modalities?.audio) {
modalities.push('audio'); modalities.push(ModelModality.AUDIO);
} }
if (this._serverProps?.modalities?.vision) { if (this._serverProps?.modalities?.vision) {
modalities.push('vision'); modalities.push(ModelModality.VISION);
} }
return modalities; return modalities;
} }

View File

@ -1,4 +1,5 @@
import type { ChatMessageTimings } from './chat'; import type { ChatMessageTimings, ChatRole, ChatMessageType } from '$lib/types/chat';
import { AttachmentType } from '$lib/enums/attachment';
export interface DatabaseConversation { export interface DatabaseConversation {
currNode: string | null; currNode: string | null;
@ -8,38 +9,39 @@ export interface DatabaseConversation {
} }
export interface DatabaseMessageExtraAudioFile { export interface DatabaseMessageExtraAudioFile {
type: 'audioFile'; type: AttachmentType.AUDIO;
name: string; name: string;
base64Data: string; base64Data: string;
mimeType: string; mimeType: string;
} }
export interface DatabaseMessageExtraImageFile { export interface DatabaseMessageExtraImageFile {
type: 'imageFile'; type: AttachmentType.IMAGE;
name: string; name: string;
base64Url: string; base64Url: string;
} }
export interface DatabaseMessageExtraTextFile {
type: 'textFile';
name: string;
content: string;
}
export interface DatabaseMessageExtraPdfFile {
type: 'pdfFile';
name: string;
content: string; // Text content extracted from PDF
images?: string[]; // Optional: PDF pages as base64 images
processedAsImages: boolean; // Whether PDF was processed as images
}
/** /**
* Legacy format from old webui - pasted content was stored as "context" type * Legacy format from old webui - pasted content was stored as "context" type
* @deprecated Use DatabaseMessageExtraTextFile instead * @deprecated Use DatabaseMessageExtraTextFile instead
*/ */
export interface DatabaseMessageExtraLegacyContext { export interface DatabaseMessageExtraLegacyContext {
type: 'context'; type: AttachmentType.LEGACY_CONTEXT;
name: string;
content: string;
}
export interface DatabaseMessageExtraPdfFile {
type: AttachmentType.PDF;
base64Data: string;
name: string;
content: string; // Text content extracted from PDF
images?: string[]; // Optional: PDF pages as base64 images
processedAsImages: boolean; // Whether PDF was processed as images
}
export interface DatabaseMessageExtraTextFile {
type: AttachmentType.TEXT;
name: string; name: string;
content: string; content: string;
} }

View File

@ -2,6 +2,7 @@ import { convertPDFToImage, convertPDFToText } from './pdf-processing';
import { isSvgMimeType, svgBase64UrlToPngDataURL } from './svg-to-png'; import { isSvgMimeType, svgBase64UrlToPngDataURL } from './svg-to-png';
import { isWebpMimeType, webpBase64UrlToPngDataURL } from './webp-to-png'; import { isWebpMimeType, webpBase64UrlToPngDataURL } from './webp-to-png';
import { FileTypeCategory } from '$lib/enums/files'; import { FileTypeCategory } from '$lib/enums/files';
import { AttachmentType } from '$lib/enums/attachment';
import { config, settingsStore } from '$lib/stores/settings.svelte'; import { config, settingsStore } from '$lib/stores/settings.svelte';
import { supportsVision } from '$lib/stores/server.svelte'; import { supportsVision } from '$lib/stores/server.svelte';
import { getFileTypeCategory } from '$lib/utils/file-type'; import { getFileTypeCategory } from '$lib/utils/file-type';
@ -56,7 +57,7 @@ export async function parseFilesToMessageExtras(
} }
extras.push({ extras.push({
type: 'imageFile', type: AttachmentType.IMAGE,
name: file.name, name: file.name,
base64Url base64Url
}); });
@ -67,7 +68,7 @@ export async function parseFilesToMessageExtras(
const base64Data = await readFileAsBase64(file.file); const base64Data = await readFileAsBase64(file.file);
extras.push({ extras.push({
type: 'audioFile', type: AttachmentType.AUDIO,
name: file.name, name: file.name,
base64Data: base64Data, base64Data: base64Data,
mimeType: file.type mimeType: file.type
@ -117,7 +118,7 @@ export async function parseFilesToMessageExtras(
); );
extras.push({ extras.push({
type: 'pdfFile', type: AttachmentType.PDF,
name: file.name, name: file.name,
content: `PDF file with ${images.length} pages`, content: `PDF file with ${images.length} pages`,
images: images, images: images,
@ -134,7 +135,7 @@ export async function parseFilesToMessageExtras(
const content = await convertPDFToText(file.file); const content = await convertPDFToText(file.file);
extras.push({ extras.push({
type: 'pdfFile', type: AttachmentType.PDF,
name: file.name, name: file.name,
content: content, content: content,
processedAsImages: false, processedAsImages: false,
@ -151,7 +152,7 @@ export async function parseFilesToMessageExtras(
}); });
extras.push({ extras.push({
type: 'pdfFile', type: AttachmentType.PDF,
name: file.name, name: file.name,
content: content, content: content,
processedAsImages: false, processedAsImages: false,
@ -171,7 +172,7 @@ export async function parseFilesToMessageExtras(
emptyFiles.push(file.name); emptyFiles.push(file.name);
} else if (isLikelyTextFile(content)) { } else if (isLikelyTextFile(content)) {
extras.push({ extras.push({
type: 'textFile', type: AttachmentType.TEXT,
name: file.name, name: file.name,
content: content content: content
}); });

View File

@ -158,7 +158,8 @@ export default defineConfig({
proxy: { proxy: {
'/v1': 'http://localhost:8080', '/v1': 'http://localhost:8080',
'/props': 'http://localhost:8080', '/props': 'http://localhost:8080',
'/slots': 'http://localhost:8080' '/slots': 'http://localhost:8080',
'/models': 'http://localhost:8080'
}, },
headers: { headers: {
'Cross-Origin-Embedder-Policy': 'require-corp', 'Cross-Origin-Embedder-Policy': 'require-corp',