refactor: Enhance model info and attachment handling
This commit is contained in:
parent
491fe2d3f7
commit
eed1bd9b97
|
|
@ -3,7 +3,7 @@
|
|||
import { getFileTypeLabel, getPreviewText } from '$lib/utils/file-preview';
|
||||
import { formatFileSize } from '$lib/utils/formatters';
|
||||
import { isTextFile } from '$lib/utils/attachment-type';
|
||||
import type { DatabaseMessageExtra } from '$lib/types/database';
|
||||
import type { DatabaseMessageExtra, DatabaseMessageExtraPdfFile } from '$lib/types/database';
|
||||
import { AttachmentType } from '$lib/enums';
|
||||
|
||||
interface Props {
|
||||
|
|
@ -35,14 +35,31 @@
|
|||
|
||||
let isText = $derived(isTextFile(attachment, uploadedFile));
|
||||
|
||||
// Get file type for display - check uploadedFile first, then attachment mimeType
|
||||
let fileType = $derived.by(() => {
|
||||
if (uploadedFile?.type) return uploadedFile.type;
|
||||
// For audio attachments stored in DB, get mimeType from the attachment
|
||||
if (attachment?.type === AttachmentType.AUDIO && 'mimeType' in attachment) {
|
||||
return attachment.mimeType;
|
||||
let fileTypeLabel = $derived.by(() => {
|
||||
if (uploadedFile?.type) {
|
||||
return getFileTypeLabel(uploadedFile.type);
|
||||
}
|
||||
return 'unknown';
|
||||
|
||||
if (attachment) {
|
||||
if ('mimeType' in attachment && attachment.mimeType) {
|
||||
return getFileTypeLabel(attachment.mimeType);
|
||||
}
|
||||
|
||||
if (attachment.type) {
|
||||
return getFileTypeLabel(attachment.type);
|
||||
}
|
||||
}
|
||||
|
||||
return getFileTypeLabel(name);
|
||||
});
|
||||
|
||||
let pdfProcessingMode = $derived.by(() => {
|
||||
if (attachment?.type === AttachmentType.PDF) {
|
||||
const pdfAttachment = attachment as DatabaseMessageExtraPdfFile;
|
||||
|
||||
return pdfAttachment.processedAsImages ? 'Sent as Image' : 'Sent as Text';
|
||||
}
|
||||
return null;
|
||||
});
|
||||
</script>
|
||||
|
||||
|
|
@ -123,17 +140,19 @@
|
|||
<div
|
||||
class="flex h-8 w-8 items-center justify-center rounded bg-primary/10 text-xs font-medium text-primary"
|
||||
>
|
||||
{getFileTypeLabel(fileType)}
|
||||
{fileTypeLabel}
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col gap-1">
|
||||
<div class="flex flex-col gap-0.5">
|
||||
<span
|
||||
class="max-w-24 truncate text-sm font-medium text-foreground group-hover:pr-6 md:max-w-32"
|
||||
>
|
||||
{name}
|
||||
</span>
|
||||
|
||||
{#if size}
|
||||
{#if pdfProcessingMode}
|
||||
<span class="text-left text-xs text-muted-foreground">{pdfProcessingMode}</span>
|
||||
{:else if size}
|
||||
<span class="text-left text-xs text-muted-foreground">{formatFileSize(size)}</span>
|
||||
{/if}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -229,7 +229,9 @@
|
|||
}
|
||||
|
||||
async function handleSendMessage(message: string, files?: ChatUploadedFile[]): Promise<boolean> {
|
||||
const result = files ? await parseFilesToMessageExtras(files) : undefined;
|
||||
const result = files
|
||||
? await parseFilesToMessageExtras(files, activeModelId ?? undefined)
|
||||
: undefined;
|
||||
|
||||
if (result?.emptyFiles && result.emptyFiles.length > 0) {
|
||||
emptyFileNames = result.emptyFiles;
|
||||
|
|
@ -292,7 +294,10 @@
|
|||
}
|
||||
|
||||
if (supportedFiles.length > 0) {
|
||||
const processed = await processFilesToChatUploaded(supportedFiles);
|
||||
const processed = await processFilesToChatUploaded(
|
||||
supportedFiles,
|
||||
activeModelId ?? undefined
|
||||
);
|
||||
uploadedFiles = [...uploadedFiles, ...processed];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
import * as Table from '$lib/components/ui/table';
|
||||
import { BadgeModality, CopyToClipboardIcon } from '$lib/components/app';
|
||||
import { serverStore } from '$lib/stores/server.svelte';
|
||||
import { modelsStore } from '$lib/stores/models.svelte';
|
||||
import { ChatService } from '$lib/services/chat';
|
||||
import type { ApiModelListResponse } from '$lib/types/api';
|
||||
import { formatFileSize, formatParameters, formatNumber } from '$lib/utils/formatters';
|
||||
|
|
@ -15,7 +16,14 @@
|
|||
let { open = $bindable(), onOpenChange }: Props = $props();
|
||||
|
||||
let serverProps = $derived(serverStore.props);
|
||||
let modalities = $derived(serverStore.supportedModalities);
|
||||
let modelName = $derived(modelsStore.singleModelName);
|
||||
|
||||
// Get modalities from modelStore using the model ID from the first model
|
||||
let modalities = $derived.by(() => {
|
||||
if (!modelsData?.data?.[0]?.id) return [];
|
||||
|
||||
return modelsStore.getModelModalitiesArray(modelsData.data[0].id);
|
||||
});
|
||||
|
||||
let modelsData = $state<ApiModelListResponse | null>(null);
|
||||
let isLoadingModels = $state(false);
|
||||
|
|
@ -77,12 +85,12 @@
|
|||
class="resizable-text-container min-w-0 flex-1 truncate"
|
||||
style:--threshold="12rem"
|
||||
>
|
||||
{serverStore.modelName}
|
||||
{modelName}
|
||||
</span>
|
||||
|
||||
<CopyToClipboardIcon
|
||||
text={serverStore.modelName || ''}
|
||||
canCopy={!!serverStore.modelName}
|
||||
text={modelName || ''}
|
||||
canCopy={!!modelName}
|
||||
ariaLabel="Copy model name to clipboard"
|
||||
/>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
<script lang="ts">
|
||||
import { Package } from '@lucide/svelte';
|
||||
import { BadgeInfo, CopyToClipboardIcon } from '$lib/components/app';
|
||||
import { modelsStore } from '$lib/stores/models.svelte';
|
||||
import { serverStore } from '$lib/stores/server.svelte';
|
||||
import * as Tooltip from '$lib/components/ui/tooltip';
|
||||
import { TOOLTIP_DELAY_DURATION } from '$lib/constants/tooltip-config';
|
||||
|
|
@ -21,7 +22,7 @@
|
|||
showTooltip = false
|
||||
}: Props = $props();
|
||||
|
||||
let model = $derived(modelProp || serverStore.modelName);
|
||||
let model = $derived(modelProp || modelsStore.singleModelName);
|
||||
let isModelMode = $derived(serverStore.isModelMode);
|
||||
</script>
|
||||
|
||||
|
|
|
|||
|
|
@ -10,11 +10,13 @@
|
|||
modelsLoading,
|
||||
modelsUpdating,
|
||||
selectedModelId,
|
||||
routerModels
|
||||
routerModels,
|
||||
propsCacheVersion,
|
||||
singleModelName
|
||||
} from '$lib/stores/models.svelte';
|
||||
import { usedModalities, conversationsStore } from '$lib/stores/conversations.svelte';
|
||||
import { ServerModelStatus } from '$lib/enums';
|
||||
import { isRouterMode, serverStore } from '$lib/stores/server.svelte';
|
||||
import { isRouterMode } from '$lib/stores/server.svelte';
|
||||
import { DialogModelInformation } from '$lib/components/app';
|
||||
import type { ModelOption } from '$lib/types/models';
|
||||
|
||||
|
|
@ -50,7 +52,7 @@
|
|||
let updating = $derived(modelsUpdating());
|
||||
let activeId = $derived(selectedModelId());
|
||||
let isRouter = $derived(isRouterMode());
|
||||
let serverModel = $derived(serverStore.modelName);
|
||||
let serverModel = $derived(singleModelName());
|
||||
|
||||
// Reactive router models state - needed for proper reactivity of status checks
|
||||
let currentRouterModels = $derived(routerModels());
|
||||
|
|
@ -69,9 +71,19 @@
|
|||
* Returns true if the model can be selected, false if it should be disabled.
|
||||
*/
|
||||
function isModelCompatible(option: ModelOption): boolean {
|
||||
const modelModalities = option.modalities;
|
||||
void propsCacheVersion();
|
||||
|
||||
if (!modelModalities) return true;
|
||||
const modelModalities = modelsStore.getModelModalities(option.model);
|
||||
|
||||
if (!modelModalities) {
|
||||
const status = getModelStatus(option.model);
|
||||
|
||||
if (status === ServerModelStatus.LOADED) {
|
||||
if (requiredModalities.vision || requiredModalities.audio) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (requiredModalities.vision && !modelModalities.vision) return false;
|
||||
if (requiredModalities.audio && !modelModalities.audio) return false;
|
||||
|
|
@ -84,8 +96,24 @@
|
|||
* Returns object with vision/audio booleans indicating what's missing.
|
||||
*/
|
||||
function getMissingModalities(option: ModelOption): { vision: boolean; audio: boolean } | null {
|
||||
const modelModalities = option.modalities;
|
||||
if (!modelModalities) return null;
|
||||
void propsCacheVersion();
|
||||
|
||||
const modelModalities = modelsStore.getModelModalities(option.model);
|
||||
|
||||
if (!modelModalities) {
|
||||
const status = getModelStatus(option.model);
|
||||
|
||||
if (status === ServerModelStatus.LOADED) {
|
||||
const missing = {
|
||||
vision: requiredModalities.vision,
|
||||
audio: requiredModalities.audio
|
||||
};
|
||||
|
||||
if (missing.vision || missing.audio) return missing;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
const missing = {
|
||||
vision: requiredModalities.vision && !modelModalities.vision,
|
||||
|
|
@ -93,6 +121,7 @@
|
|||
};
|
||||
|
||||
if (!missing.vision && !missing.audio) return null;
|
||||
|
||||
return missing;
|
||||
}
|
||||
|
||||
|
|
@ -160,9 +189,10 @@
|
|||
await tick();
|
||||
updateMenuPosition();
|
||||
requestAnimationFrame(() => updateMenuPosition());
|
||||
|
||||
modelsStore.fetchModalitiesForLoadedModels();
|
||||
}
|
||||
|
||||
// Export open function for programmatic access
|
||||
export function open() {
|
||||
if (isRouter) {
|
||||
openMenu();
|
||||
|
|
|
|||
|
|
@ -2,7 +2,8 @@
|
|||
import { AlertTriangle, Server } from '@lucide/svelte';
|
||||
import { Badge } from '$lib/components/ui/badge';
|
||||
import { Button } from '$lib/components/ui/button';
|
||||
import { serverProps, serverLoading, serverError, modelName } from '$lib/stores/server.svelte';
|
||||
import { serverProps, serverLoading, serverError } from '$lib/stores/server.svelte';
|
||||
import { singleModelName } from '$lib/stores/models.svelte';
|
||||
|
||||
interface Props {
|
||||
class?: string;
|
||||
|
|
@ -13,7 +14,7 @@
|
|||
|
||||
let error = $derived(serverError());
|
||||
let loading = $derived(serverLoading());
|
||||
let model = $derived(modelName());
|
||||
let model = $derived(singleModelName());
|
||||
let serverData = $derived(serverProps());
|
||||
|
||||
function getStatusColor() {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { SvelteSet } from 'svelte/reactivity';
|
||||
import { ModelsService } from '$lib/services/models';
|
||||
import { PropsService } from '$lib/services/props';
|
||||
import { ServerModelStatus } from '$lib/enums';
|
||||
import { ServerModelStatus, ModelModality } from '$lib/enums';
|
||||
import { serverStore } from '$lib/stores/server.svelte';
|
||||
import type { ModelOption, ModelModalities } from '$lib/types/models';
|
||||
import type { ApiModelDataEntry } from '$lib/types/api';
|
||||
|
|
@ -56,6 +56,11 @@ class ModelsStore {
|
|||
private modelPropsCache = $state<Map<string, ApiLlamaCppServerProps>>(new Map());
|
||||
private modelPropsFetching = $state<Set<string>>(new Set());
|
||||
|
||||
/**
|
||||
* Version counter for props cache - used to trigger reactivity when props are updated
|
||||
*/
|
||||
propsCacheVersion = $state(0);
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// Computed Getters
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
|
@ -77,6 +82,21 @@ class ModelsStore {
|
|||
.map(([id]) => id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get model name in MODEL mode (single model).
|
||||
* Extracts from model_path or model_alias from server props.
|
||||
* In ROUTER mode, returns null (model is per-conversation).
|
||||
*/
|
||||
get singleModelName(): string | null {
|
||||
if (serverStore.isRouterMode) return null;
|
||||
|
||||
const props = serverStore.props;
|
||||
if (props?.model_alias) return props.model_alias;
|
||||
if (!props?.model_path) return null;
|
||||
|
||||
return props.model_path.split(/(\\|\/)/).pop() || null;
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// Modalities
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
|
@ -118,6 +138,21 @@ class ModelsStore {
|
|||
return this.getModelModalities(modelId)?.audio ?? false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get model modalities as an array of ModelModality enum values
|
||||
*/
|
||||
getModelModalitiesArray(modelId: string): ModelModality[] {
|
||||
const modalities = this.getModelModalities(modelId);
|
||||
if (!modalities) return [];
|
||||
|
||||
const result: ModelModality[] = [];
|
||||
|
||||
if (modalities.vision) result.push(ModelModality.VISION);
|
||||
if (modalities.audio) result.push(ModelModality.AUDIO);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get props for a specific model (from cache)
|
||||
*/
|
||||
|
|
@ -300,6 +335,9 @@ class ModelsStore {
|
|||
|
||||
return { ...model, modalities };
|
||||
});
|
||||
|
||||
// Increment version to trigger reactivity
|
||||
this.propsCacheVersion++;
|
||||
} catch (error) {
|
||||
console.warn('Failed to fetch modalities for loaded models:', error);
|
||||
}
|
||||
|
|
@ -322,6 +360,9 @@ class ModelsStore {
|
|||
this.models = this.models.map((model) =>
|
||||
model.model === modelId ? { ...model, modalities } : model
|
||||
);
|
||||
|
||||
// Increment version to trigger reactivity
|
||||
this.propsCacheVersion++;
|
||||
} catch (error) {
|
||||
console.warn(`Failed to update modalities for model ${modelId}:`, error);
|
||||
}
|
||||
|
|
@ -583,3 +624,5 @@ export const selectedModelName = () => modelsStore.selectedModelName;
|
|||
export const selectedModelOption = () => modelsStore.selectedModel;
|
||||
export const loadedModelIds = () => modelsStore.loadedModelIds;
|
||||
export const loadingModelIds = () => modelsStore.loadingModelIds;
|
||||
export const propsCacheVersion = () => modelsStore.propsCacheVersion;
|
||||
export const singleModelName = () => modelsStore.singleModelName;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { PropsService } from '$lib/services/props';
|
||||
import { ServerRole, ModelModality } from '$lib/enums';
|
||||
import { ServerRole } from '$lib/enums';
|
||||
|
||||
/**
|
||||
* serverStore - Server connection state, configuration, and role detection
|
||||
|
|
@ -16,12 +16,6 @@ import { ServerRole, ModelModality } from '$lib/enums';
|
|||
* - **Server State**: Connection status, loading, error handling
|
||||
* - **Role Detection**: MODEL (single model) vs ROUTER (multi-model)
|
||||
* - **Default Params**: Server-wide generation defaults
|
||||
*
|
||||
* **Note on Modalities:**
|
||||
* Model-specific modalities (vision, audio) are now managed by modelsStore.
|
||||
* Use `modelsStore.getModelModalities(modelId)` for per-model modality info.
|
||||
* The `supportsVision`/`supportsAudio` getters here are deprecated and only
|
||||
* apply to MODEL mode (single model).
|
||||
*/
|
||||
class ServerStore {
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
|
@ -38,45 +32,6 @@ class ServerStore {
|
|||
// Getters
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Get model name from server props.
|
||||
* In MODEL mode: extracts from model_path or model_alias
|
||||
* In ROUTER mode: returns null (model is per-conversation)
|
||||
*/
|
||||
get modelName(): string | null {
|
||||
if (this.role === ServerRole.ROUTER) return null;
|
||||
if (this.props?.model_alias) return this.props.model_alias;
|
||||
if (!this.props?.model_path) return null;
|
||||
return this.props.model_path.split(/(\\|\/)/).pop() || null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use modelsStore.getModelModalities(modelId) for per-model modalities.
|
||||
* This only works in MODEL mode (single model).
|
||||
*/
|
||||
get supportedModalities(): ModelModality[] {
|
||||
const modalities: ModelModality[] = [];
|
||||
if (this.props?.modalities?.audio) modalities.push(ModelModality.AUDIO);
|
||||
if (this.props?.modalities?.vision) modalities.push(ModelModality.VISION);
|
||||
return modalities;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use modelsStore.modelSupportsVision(modelId) for per-model check.
|
||||
* This only works in MODEL mode (single model).
|
||||
*/
|
||||
get supportsVision(): boolean {
|
||||
return this.props?.modalities?.vision ?? false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use modelsStore.modelSupportsAudio(modelId) for per-model check.
|
||||
* This only works in MODEL mode (single model).
|
||||
*/
|
||||
get supportsAudio(): boolean {
|
||||
return this.props?.modalities?.audio ?? false;
|
||||
}
|
||||
|
||||
get defaultParams(): ApiLlamaCppServerProps['default_generation_settings']['params'] | null {
|
||||
return this.props?.default_generation_settings?.params || null;
|
||||
}
|
||||
|
|
@ -179,10 +134,6 @@ export const serverProps = () => serverStore.props;
|
|||
export const serverLoading = () => serverStore.loading;
|
||||
export const serverError = () => serverStore.error;
|
||||
export const serverRole = () => serverStore.role;
|
||||
export const modelName = () => serverStore.modelName;
|
||||
export const supportedModalities = () => serverStore.supportedModalities;
|
||||
export const supportsVision = () => serverStore.supportsVision;
|
||||
export const supportsAudio = () => serverStore.supportsAudio;
|
||||
export const slotsEndpointAvailable = () => serverStore.slotsEndpointAvailable;
|
||||
export const defaultParams = () => serverStore.defaultParams;
|
||||
export const contextSize = () => serverStore.contextSize;
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import { isSvgMimeType, svgBase64UrlToPngDataURL } from './svg-to-png';
|
|||
import { isWebpMimeType, webpBase64UrlToPngDataURL } from './webp-to-png';
|
||||
import { FileTypeCategory, AttachmentType } from '$lib/enums';
|
||||
import { config, settingsStore } from '$lib/stores/settings.svelte';
|
||||
import { supportsVision } from '$lib/stores/server.svelte';
|
||||
import { modelsStore } from '$lib/stores/models.svelte';
|
||||
import { getFileTypeCategory } from '$lib/utils/file-type';
|
||||
import { readFileAsText, isLikelyTextFile } from './text-files';
|
||||
import { toast } from 'svelte-sonner';
|
||||
|
|
@ -31,7 +31,8 @@ export interface FileProcessingResult {
|
|||
}
|
||||
|
||||
export async function parseFilesToMessageExtras(
|
||||
files: ChatUploadedFile[]
|
||||
files: ChatUploadedFile[],
|
||||
activeModelId?: string
|
||||
): Promise<FileProcessingResult> {
|
||||
const extras: DatabaseMessageExtra[] = [];
|
||||
const emptyFiles: string[] = [];
|
||||
|
|
@ -80,7 +81,10 @@ export async function parseFilesToMessageExtras(
|
|||
// Always get base64 data for preview functionality
|
||||
const base64Data = await readFileAsBase64(file.file);
|
||||
const currentConfig = config();
|
||||
const hasVisionSupport = supportsVision();
|
||||
// Use per-model vision check for router mode
|
||||
const hasVisionSupport = activeModelId
|
||||
? modelsStore.modelSupportsVision(activeModelId)
|
||||
: false;
|
||||
|
||||
// Force PDF-to-text for non-vision models
|
||||
let shouldProcessAsImages = Boolean(currentConfig.pdfAsImage) && hasVisionSupport;
|
||||
|
|
|
|||
|
|
@ -1,10 +1,38 @@
|
|||
/**
|
||||
* Gets a display label for a file type
|
||||
* @param fileType - The file type/mime type
|
||||
* @returns Formatted file type label
|
||||
* Gets a display label for a file type from various input formats
|
||||
*
|
||||
* Handles:
|
||||
* - MIME types: 'application/pdf' → 'PDF'
|
||||
* - AttachmentType values: 'PDF', 'AUDIO' → 'PDF', 'AUDIO'
|
||||
* - File names: 'document.pdf' → 'PDF'
|
||||
* - Unknown: returns 'FILE'
|
||||
*
|
||||
* @param input - MIME type, AttachmentType value, or file name
|
||||
* @returns Formatted file type label (uppercase)
|
||||
*/
|
||||
export function getFileTypeLabel(fileType: string): string {
|
||||
return fileType.split('/').pop()?.toUpperCase() || 'FILE';
|
||||
export function getFileTypeLabel(input: string | undefined): string {
|
||||
if (!input) return 'FILE';
|
||||
|
||||
// Handle MIME types (contains '/')
|
||||
if (input.includes('/')) {
|
||||
const subtype = input.split('/').pop();
|
||||
if (subtype) {
|
||||
// Handle special cases like 'vnd.ms-excel' → 'EXCEL'
|
||||
if (subtype.includes('.')) {
|
||||
return subtype.split('.').pop()?.toUpperCase() || 'FILE';
|
||||
}
|
||||
return subtype.toUpperCase();
|
||||
}
|
||||
}
|
||||
|
||||
// Handle file names (contains '.')
|
||||
if (input.includes('.')) {
|
||||
const ext = input.split('.').pop();
|
||||
if (ext) return ext.toUpperCase();
|
||||
}
|
||||
|
||||
// Handle AttachmentType or other plain strings
|
||||
return input.toUpperCase();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import { isTextFileByName } from './text-files';
|
|||
import { isWebpMimeType, webpBase64UrlToPngDataURL } from './webp-to-png';
|
||||
import { FileTypeCategory } from '$lib/enums';
|
||||
import { getFileTypeCategory } from '$lib/utils/file-type';
|
||||
import { supportsVision } from '$lib/stores/server.svelte';
|
||||
import { modelsStore } from '$lib/stores/models.svelte';
|
||||
import { settingsStore } from '$lib/stores/settings.svelte';
|
||||
import { toast } from 'svelte-sonner';
|
||||
|
||||
|
|
@ -47,7 +47,10 @@ function readFileAsUTF8(file: File): Promise<string> {
|
|||
* @param files - Array of File objects to process
|
||||
* @returns Promise resolving to array of ChatUploadedFile objects
|
||||
*/
|
||||
export async function processFilesToChatUploaded(files: File[]): Promise<ChatUploadedFile[]> {
|
||||
export async function processFilesToChatUploaded(
|
||||
files: File[],
|
||||
activeModelId?: string
|
||||
): Promise<ChatUploadedFile[]> {
|
||||
const results: ChatUploadedFile[] = [];
|
||||
|
||||
for (const file of files) {
|
||||
|
|
@ -96,7 +99,9 @@ export async function processFilesToChatUploaded(files: File[]): Promise<ChatUpl
|
|||
results.push(base);
|
||||
|
||||
// Show suggestion toast if vision model is available but PDF as image is disabled
|
||||
const hasVisionSupport = supportsVision();
|
||||
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.`, {
|
||||
|
|
|
|||
Loading…
Reference in New Issue