1/**
2 * File validation utilities based on model modalities
3 * Ensures only compatible file types are processed based on model capabilities
4 */
5
6import { getFileTypeCategory } from '$lib/utils';
7import { FileTypeCategory } from '$lib/enums';
8
9/** Modality capabilities for file validation */
10export interface ModalityCapabilities {
11 hasVision: boolean;
12 hasAudio: boolean;
13}
14
15/**
16 * Check if a file type is supported by the given modalities
17 * @param filename - The filename to check
18 * @param mimeType - The MIME type of the file
19 * @param capabilities - The modality capabilities to check against
20 * @returns true if the file type is supported
21 */
22export function isFileTypeSupportedByModel(
23 filename: string,
24 mimeType: string | undefined,
25 capabilities: ModalityCapabilities
26): boolean {
27 const category = mimeType ? getFileTypeCategory(mimeType) : null;
28
29 // If we can't determine the category from MIME type, fall back to general support check
30 if (!category) {
31 // For unknown types, only allow if they might be text files
32 // This is a conservative approach for edge cases
33 return true; // Let the existing isFileTypeSupported handle this
34 }
35
36 switch (category) {
37 case FileTypeCategory.TEXT:
38 // Text files are always supported
39 return true;
40
41 case FileTypeCategory.PDF:
42 // PDFs are always supported (will be processed as text for non-vision models)
43 return true;
44
45 case FileTypeCategory.IMAGE:
46 // Images require vision support
47 return capabilities.hasVision;
48
49 case FileTypeCategory.AUDIO:
50 // Audio files require audio support
51 return capabilities.hasAudio;
52
53 default:
54 // Unknown categories - be conservative and allow
55 return true;
56 }
57}
58
59/**
60 * Filter files based on model modalities and return supported/unsupported lists
61 * @param files - Array of files to filter
62 * @param capabilities - The modality capabilities to check against
63 * @returns Object with supportedFiles and unsupportedFiles arrays
64 */
65export function filterFilesByModalities(
66 files: File[],
67 capabilities: ModalityCapabilities
68): {
69 supportedFiles: File[];
70 unsupportedFiles: File[];
71 modalityReasons: Record<string, string>;
72} {
73 const supportedFiles: File[] = [];
74 const unsupportedFiles: File[] = [];
75 const modalityReasons: Record<string, string> = {};
76
77 const { hasVision, hasAudio } = capabilities;
78
79 for (const file of files) {
80 const category = getFileTypeCategory(file.type);
81 let isSupported = true;
82 let reason = '';
83
84 switch (category) {
85 case FileTypeCategory.IMAGE:
86 if (!hasVision) {
87 isSupported = false;
88 reason = 'Images require a vision-capable model';
89 }
90 break;
91
92 case FileTypeCategory.AUDIO:
93 if (!hasAudio) {
94 isSupported = false;
95 reason = 'Audio files require an audio-capable model';
96 }
97 break;
98
99 case FileTypeCategory.TEXT:
100 case FileTypeCategory.PDF:
101 // Always supported
102 break;
103
104 default:
105 // For unknown types, check if it's a generally supported file type
106 // This handles edge cases and maintains backward compatibility
107 break;
108 }
109
110 if (isSupported) {
111 supportedFiles.push(file);
112 } else {
113 unsupportedFiles.push(file);
114 modalityReasons[file.name] = reason;
115 }
116 }
117
118 return { supportedFiles, unsupportedFiles, modalityReasons };
119}
120
121/**
122 * Generate a user-friendly error message for unsupported files
123 * @param unsupportedFiles - Array of unsupported files
124 * @param modalityReasons - Reasons why files are unsupported
125 * @param capabilities - The modality capabilities to check against
126 * @returns Formatted error message
127 */
128export function generateModalityErrorMessage(
129 unsupportedFiles: File[],
130 modalityReasons: Record<string, string>,
131 capabilities: ModalityCapabilities
132): string {
133 if (unsupportedFiles.length === 0) return '';
134
135 const { hasVision, hasAudio } = capabilities;
136
137 let message = '';
138
139 if (unsupportedFiles.length === 1) {
140 const file = unsupportedFiles[0];
141 const reason = modalityReasons[file.name];
142 message = `The file "${file.name}" cannot be uploaded: ${reason}.`;
143 } else {
144 const fileNames = unsupportedFiles.map((f) => f.name).join(', ');
145 message = `The following files cannot be uploaded: ${fileNames}.`;
146 }
147
148 // Add helpful information about what is supported
149 const supportedTypes: string[] = ['text files', 'PDFs'];
150 if (hasVision) supportedTypes.push('images');
151 if (hasAudio) supportedTypes.push('audio files');
152
153 message += ` This model supports: ${supportedTypes.join(', ')}.`;
154
155 return message;
156}
157
158/**
159 * Generate file input accept string based on model modalities
160 * @param capabilities - The modality capabilities to check against
161 * @returns Accept string for HTML file input element
162 */