import { IndexingConfig } from '../types'; // web/services/uploadService.ts export const uploadService = { async uploadFile(file: File, authToken: string): Promise { const formData = new FormData(); formData.append('file', file); const response = await fetch('/api/upload', { method: 'POST', headers: { 'Authorization': `Bearer ${authToken}`, }, body: formData, }); if (!response.ok) { const errorData = await response.json(); throw new Error(errorData.message || 'fileUploadFailed'); } return response.json(); }, async uploadFileWithConfig(file: File, config: IndexingConfig, authToken: string): Promise { const formData = new FormData(); formData.append('file', file); formData.append('chunkSize', config.chunkSize.toString()); formData.append('chunkOverlap', config.chunkOverlap.toString()); formData.append('embeddingModelId', config.embeddingModelId); // 処理モードを追加(指定されている場合) if (config.mode) { formData.append('mode', config.mode); } const response = await fetch('/api/upload', { method: 'POST', headers: { 'Authorization': `Bearer ${authToken}`, }, body: formData, }); if (!response.ok) { const errorData = await response.json(); throw new Error(errorData.message || 'fileUploadFailed'); } return response.json(); }, async uploadText(content: string, title: string, config: IndexingConfig, authToken: string): Promise { const response = await fetch('/api/upload/text', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${authToken}` }, body: JSON.stringify({ content, title, chunkSize: config.chunkSize.toString(), chunkOverlap: config.chunkOverlap.toString(), embeddingModelId: config.embeddingModelId, mode: config.mode }) }); if (!response.ok) { const errorData = await response.json().catch(() => ({})); throw new Error(errorData.message || 'noteIndexFailed'); } return response.json(); }, /** * ファイル処理モードの推奨を取得 * ファイルの種類、サイズなどの要因に基づいて、高速モードまたは高精度モードの使用を推奨します */ async recommendMode(file: File): Promise { // セーフティチェック if (!file || !file.name) { return { recommendedMode: 'fast', reason: 'invalidFile', warnings: ['incompleteFileInfo'], }; } // フロントエンドの簡単な判定ロジック const ext = file.name.toLowerCase().split('.').pop(); const sizeMB = file.size / (1024 * 1024); const preciseFormats = ['pdf', 'doc', 'docx', 'ppt', 'pptx']; const supportedFormats = [...preciseFormats, 'xls', 'xlsx', 'txt', 'md', 'html', 'json', 'csv']; if (!supportedFormats.includes(ext || '')) { return { recommendedMode: 'fast', reason: `unsupportedFileFormat`, reasonArgs: [ext], warnings: ['willUseFastMode'], }; } if (!preciseFormats.includes(ext || '')) { return { recommendedMode: 'fast', reason: `formatNoPrecise`, reasonArgs: [ext], warnings: ['willUseFastMode'], }; } // 小規模なファイルには高速モードを推奨 if (sizeMB < 5) { return { recommendedMode: 'fast', reason: 'smallFileFastOk', estimatedCost: 0, estimatedTime: sizeMB * 2, warnings: [], }; } // 中規模なファイルには高精度モードを推奨 if (sizeMB < 50) { return { recommendedMode: 'precise', reason: 'mixedContentPreciseRecommended', estimatedCost: Math.max(0.01, sizeMB * 0.01), estimatedTime: sizeMB * 8, warnings: ['willIncurApiCost'], }; } // 大規模なファイルには高精度モードを推奨するが警告を表示 return { recommendedMode: 'precise', reason: 'largeFilePreciseRecommended', estimatedCost: sizeMB * 0.015, estimatedTime: sizeMB * 12, warnings: ['longProcessingTime', 'highApiCost', 'considerFileSplitting'], }; }, };