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 */