uploadService.ts 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130
  1. import { apiClient } from './apiClient';
  2. import { IndexingConfig } from '../types';
  3. // web/services/uploadService.ts
  4. export const uploadService = {
  5. async uploadFile(file: File, authToken: string): Promise<any> {
  6. const formData = new FormData();
  7. formData.append('file', file);
  8. const response = await apiClient.request('/upload', {
  9. method: 'POST',
  10. body: formData,
  11. });
  12. if (!response.ok) {
  13. const errorData = await response.json();
  14. throw new Error(errorData.message || 'fileUploadFailed');
  15. }
  16. return response.json();
  17. },
  18. async uploadFileWithConfig(file: File, config: IndexingConfig, authToken: string): Promise<any> {
  19. const formData = new FormData();
  20. formData.append('file', file);
  21. formData.append('chunkSize', config.chunkSize.toString());
  22. formData.append('chunkOverlap', config.chunkOverlap.toString());
  23. formData.append('embeddingModelId', config.embeddingModelId);
  24. // 処理モードを追加(指定されている場合)
  25. if (config.mode) {
  26. formData.append('mode', config.mode);
  27. }
  28. const response = await apiClient.request('/upload', {
  29. method: 'POST',
  30. body: formData,
  31. });
  32. if (!response.ok) {
  33. const errorData = await response.json();
  34. throw new Error(errorData.message || 'fileUploadFailed');
  35. }
  36. return response.json();
  37. },
  38. async uploadText(content: string, title: string, config: IndexingConfig, authToken: string): Promise<any> {
  39. const { data } = await apiClient.post('/upload/text', {
  40. content,
  41. title,
  42. chunkSize: config.chunkSize.toString(),
  43. chunkOverlap: config.chunkOverlap.toString(),
  44. embeddingModelId: config.embeddingModelId,
  45. mode: config.mode
  46. });
  47. return data;
  48. },
  49. /**
  50. * ファイル処理モードの推奨を取得
  51. * ファイルの種類、サイズなどの要因に基づいて、高速モードまたは高精度モードの使用を推奨します
  52. */
  53. async recommendMode(file: File): Promise<any> {
  54. // セーフティチェック
  55. if (!file || !file.name) {
  56. return {
  57. recommendedMode: 'fast',
  58. reason: 'invalidFile',
  59. warnings: ['incompleteFileInfo'],
  60. };
  61. }
  62. // フロントエンドの簡単な判定ロジック
  63. const ext = file.name.toLowerCase().split('.').pop();
  64. const sizeMB = file.size / (1024 * 1024);
  65. const preciseFormats = ['pdf', 'doc', 'docx', 'ppt', 'pptx'];
  66. const supportedFormats = [...preciseFormats, 'xls', 'xlsx', 'txt', 'md', 'html', 'json', 'csv'];
  67. if (!supportedFormats.includes(ext || '')) {
  68. return {
  69. recommendedMode: 'fast',
  70. reason: `unsupportedFileFormat`,
  71. reasonArgs: [ext],
  72. warnings: ['willUseFastMode'],
  73. };
  74. }
  75. if (!preciseFormats.includes(ext || '')) {
  76. return {
  77. recommendedMode: 'fast',
  78. reason: `formatNoPrecise`,
  79. reasonArgs: [ext],
  80. warnings: ['willUseFastMode'],
  81. };
  82. }
  83. // 小規模なファイルには高速モードを推奨
  84. if (sizeMB < 5) {
  85. return {
  86. recommendedMode: 'fast',
  87. reason: 'smallFileFastOk',
  88. estimatedCost: 0,
  89. estimatedTime: sizeMB * 2,
  90. warnings: [],
  91. };
  92. }
  93. // 中規模なファイルには高精度モードを推奨
  94. if (sizeMB < 50) {
  95. return {
  96. recommendedMode: 'precise',
  97. reason: 'mixedContentPreciseRecommended',
  98. estimatedCost: Math.max(0.01, sizeMB * 0.01),
  99. estimatedTime: sizeMB * 8,
  100. warnings: ['willIncurApiCost'],
  101. };
  102. }
  103. // 大規模なファイルには高精度モードを推奨するが警告を表示
  104. return {
  105. recommendedMode: 'precise',
  106. reason: 'largeFilePreciseRecommended',
  107. estimatedCost: sizeMB * 0.015,
  108. estimatedTime: sizeMB * 12,
  109. warnings: ['longProcessingTime', 'highApiCost', 'considerFileSplitting'],
  110. };
  111. },
  112. };