backend_cjk.txt 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404
  1. openAIApiKey: config.apiKey || 'ollama', // ローカルモデルの場合は key が不要な場合がある
  2. modelName: config.modelId, // modelId に修正
  3. ); // modelId に修正
  4. selectedLLMId?: string; // 新增:选中的 LLM 模型 ID
  5. selectedGroups?: string[]; // 新增
  6. selectedFiles?: string[]; // 新增:选中的文件
  7. historyId?: string; // 新增
  8. enableRerank?: boolean; // 新增
  9. selectedRerankId?: string; // 新增
  10. temperature?: number; // 新增:temperature 参数
  11. maxTokens?: number; // 新增:maxTokens 参数
  12. topK?: number; // 新增:topK 参数
  13. similarityThreshold?: number; // 新増:similarityThreshold 参数
  14. rerankSimilarityThreshold?: number; // 新増:rerankSimilarityThreshold 参数
  15. enableQueryExpansion?: boolean; // 新增
  16. enableHyDE?: boolean; // 新增
  17. console.log('Final LLM model used (default):', llmModel ? llmModel.name : '无');
  18. `data: ${JSON.stringify({ type: 'error', data: '请在模型管理中添加LLM模型并配置API密钥' })}\n\n`,
  19. selectedGroups, // 新增
  20. selectedFiles, // 新增
  21. historyId, // 新增
  22. temperature, // 传递 temperature 参数
  23. maxTokens, // 传递 maxTokens 参数
  24. topK, // 传递 topK 参数
  25. similarityThreshold, // 传递 similarityThreshold 参数
  26. rerankSimilarityThreshold, // 传递 rerankSimilarityThreshold 参数
  27. enableQueryExpansion, // 传递 enableQueryExpansion
  28. enableHyDE, // 传递 enableHyDE
  29. `data: ${JSON.stringify({ type: 'error', data: error.message || '服务器错误' })}\n\n`,
  30. `data: ${JSON.stringify({ type: 'error', data: '未找到LLM模型配置' })}\n\n`,
  31. selectedGroups?: string[], // 新規:選択されたグループ
  32. selectedFiles?: string[], // 新規:選択されたファイル
  33. historyId?: string, // 新規:対話履歴ID
  34. temperature?: number, // 新規: temperature パラメータ
  35. maxTokens?: number, // 新規: maxTokens パラメータ
  36. topK?: number, // 新規: topK パラメータ
  37. similarityThreshold?: number, // 新規: similarityThreshold パラメータ
  38. rerankSimilarityThreshold?: number, // 新規: rerankSimilarityThreshold パラメータ
  39. enableQueryExpansion?: boolean, // 新規
  40. enableHyDE?: boolean, // 新規
  41. tenantId?: string // 新規: tenant isolation
  42. console.log('ユーザーID:', userId);
  43. console.log('API Key プレフィックス:', modelConfig.apiKey?.substring(0, 10) + '...');
  44. tenantId || 'default', // 新規
  45. let effectiveFileIds = selectedFiles; // 明示的に指定されたファイルを優先
  46. 提供されたテキスト内容を、ユーザーの指示に基づいて修正または改善してください。
  47. 挨拶や結びの言葉(「わかりました、こちらが...」など)は含めず、修正後の内容のみを直接出力してください。
  48. コンテキスト(現在の内容):
  49. ユーザーの指示:
  50. selectedGroups?: string[], // 新規パラメータ
  51. explicitFileIds?: string[], // 新規パラメータ
  52. selectedGroups, // 選択されたグループを渡す
  53. explicitFileIds, // 明示的なファイルIDを渡す
  54. temperature: settings.temperature ?? 0.7, // ユーザー設定またはデフォルトを使用
  55. * 対話内容に基づいてチャットのタイトルを自動生成する
  56. * アプリケーション全体で使用される定数定義
  57. refresh: true, // 即座に検索に反映させる
  58. score: this.normalizeScore(hit._score), // スコアの正規化
  59. selectedGroups?: string[], // 後方互換性のために残す(未使用)
  60. explicitFileIds?: string[], // 明示的に指定されたファイルIDリスト
  61. const maxScore = Math.max(...allScores, 1); // ゼロ除算を避けるため最小1
  62. * Elasticsearch スコアを 0-1 の範囲に正規化する
  63. * Elasticsearch のスコアは 1.0 を超える可能性があるため、正規化が必要
  64. * ただし、kNN検索の類似度スコアは既に0-1の範囲にある(cosine similarity)ので、
  65. * 特別な正規化は不要。必要に応じて最小値保護のみ行う。
  66. if (!rawScore || rawScore <= 0) return 0; // 最小値は0
  67. * 指定されたファイルのすべてのチャンクを取得
  68. size: 10000, // 単一ファイルが 10000 チャンクを超えないと想定
  69. excludes: ['vector'], // 転送量を減らすため、ベクトルデータは返さない
  70. private readonly defaultLanguage = 'ja'; // プロジェクト要件に従い、Japaneseをデフォルトとして使用
  71. 基于以下知识库内容回答用户问题。
  72. **重要提示**: 用户已选择特定知识组,请严格基于以下知识库内容回答。如果知识库中没有相关信息,请明确告知用户:"${noMatchMsg}",然后再提供答案。
  73. 知识库内容:
  74. 历史对话:
  75. 用户问题:{question}
  76. 请用Chinese回答,并严格遵循以下 Markdown 格式要求:
  77. 1. **段落与结构**:
  78. - 使用清晰的段落分隔,每个要点之间空一行
  79. - 使用标题(## 或 ###)组织长回答
  80. 2. **文本格式**:
  81. - 使用 **粗体** 强调重要概念和关键词
  82. - 使用列表(- 或 1.)组织多个要点
  83. - 使用 \`代码\` 标记技术术语、命令、文件名
  84. 3. **代码展示**:
  85. - 使用代码块展示代码,并指定语言:
  86. return "示例"
  87. - 支持语言:python, javascript, typescript, java, bash, sql 等
  88. 4. **图表与可视化**:
  89. - 使用 Mermaid 语法绘制流程图、序列图等:
  90. A[开始] --> B[处理]
  91. B --> C[结束]
  92. - 适用场景:流程、架构、状态机、时序图
  93. 5. **其他要求**:
  94. - 回答精炼准确
  95. - 多步骤操作使用有序列表
  96. - 对比类信息建议用表格展示(如果适用)
  97. 作为智能助手,请回答用户的问题。
  98. 请用Chinese回答。
  99. } else { // 默认为日语,符合项目要求
  100. 以下のナレッジベースの内容に基づいてユーザーの質問に答えてください。
  101. **重要**: ユーザーが特定の知識グループを選択しました。以下のナレッジベースの内容に厳密に基づいて回答してください。ナレッジベースに関連情報がない場合は、「${noMatchMsg}」とユーザーに明示的に伝えてから、回答を提供してください。
  102. ナレッジベースの内容:
  103. 会話履歴:
  104. ユーザーの質問:{question}
  105. Japaneseで回答してください。以下の Markdown 書式要件に厳密に従ってください:
  106. 1. **段落と構造**:
  107. - 明確な段落分けを使用し、要点間に空行を入れる
  108. - 長い回答には見出し(## または ###)を使用
  109. 2. **テキスト書式**:
  110. - 重要な概念やキーワードを強調するために **太字** を使用
  111. - 複数のポイントを整理するためにリスト(- または 1.)を使用
  112. - 技術用語、コマンド、ファイル名をマークするために \`コード\` を使用
  113. 3. **コード表示**:
  114. - 言語を指定してコードブロックを使用:
  115. return "例"
  116. - 対応言語:python, javascript, typescript, java, bash, sql など
  117. 4. **図表とチャート**:
  118. - フローチャート、シーケンス図などに Mermaid 構文を使用:
  119. A[開始] --> B[処理]
  120. B --> C[終了]
  121. - 使用例:プロセスフロー、アーキテクチャ図、状態図、シーケンス図
  122. 5. **その他の要件**:
  123. - 簡潔で明確な回答を心がける
  124. - 複数のステップがある場合は番号付きリストを使用
  125. - 比較情報には表を使用(該当する場合)
  126. インテリジェントアシスタントとして、ユーザーの質問に答えてください。
  127. Japaneseで回答してください。
  128. return `你是一个文档分析师。请阅读以下文本(文档开Header分),并生成一个简炼、专业的标题(不超过50个字符)。
  129. 只返回标题文本。不要包含任何解释性文字或前导词(如“标题是:”)。
  130. 语言:Chinese
  131. 文本内容:
  132. return `あなたはドキュメントアナライザーです。以下のテキスト(ドキュメントの冒頭部分)を読み、簡潔でプロフェッショナルなタイトル(最大50文字)を生成してください。
  133. タイトルテキストのみを返してください。説明文や前置き(例:「タイトルは:」)は含めないでください。
  134. 言語:Japanese
  135. テキスト:
  136. return `根据以下对话片段,生成一个简短、描述性的标题(不超过50个字符),总结讨论的主题。
  137. 只返回标题文本。不要包含任何前导词。
  138. 片段:
  139. 用户: ${userMessage}
  140. 助手: ${aiResponse}`;
  141. return `以下の会話スニペットに基づいて、トピックを要約する短く説明的なタイトル(最大50文字)を生成してください。
  142. タイトルのみを返してください。前置きは不要です。
  143. スニペット:
  144. ユーザー: ${userMessage}
  145. アシスタント: ${aiResponse}`;
  146. * Chunk configurationサービス
  147. * チャンクパラメータの検証と管理を担当し、モデルの制限や環境変数の設定に適合していることを確認します
  148. * 制限の優先順位:
  149. * 1. 環境変数 (MAX_CHUNK_SIZE, MAX_OVERLAP_SIZE)
  150. * 2. データベース内のモデル設定 (maxInputTokens, maxBatchSize)
  151. * 3. デフォルト値
  152. maxOverlapRatio: DEFAULT_MAX_OVERLAP_RATIO, // 重なりはChunk sizeの50%まで
  153. maxBatchSize: DEFAULT_MAX_BATCH_SIZE, // デフォルトのバッチ制限
  154. expectedDimensions: DEFAULT_VECTOR_DIMENSIONS, // デフォルトのベクトル次元
  155. * モデルの制限設定を取得(データベースから読み込み)
  156. const providerName = modelConfig.providerName || '不明';
  157. ` - プロバイダー: ${providerName}\n` +
  158. ` - Token制限: ${maxInputTokens}\n` +
  159. ` - ベクトルモデルか: ${isVectorModel}`,
  160. * Chunk configurationを検証および修正
  161. * 優先順位: 環境変数の上限 > モデルの制限 > ユーザー設定
  162. const safetyMargin = 0.8; // 80% 安全マージン、バッチ処理のためにスペースを確保
  163. 1000000, // 1MB のテキストを想定
  164. * 推奨されるバッチサイズを取得
  165. 200, // 安全のための上限
  166. return Math.max(10, recommended); // 最低10個
  167. * チャンク数を推定
  168. * ベクトル次元の検証
  169. * 設定概要を取得(ログ用)
  170. `Chunk size: ${chunkSize} tokens (制限: ${limits.maxInputTokens})`,
  171. `重なりサイズ: ${chunkOverlap} tokens`,
  172. `バッチサイズ: ${limits.maxBatchSize}`,
  173. * フロントエンド用のConfig limitsを取得
  174. * フロントエンドのスライダーの上限設定に使用
  175. throw new Error(`埋め込みモデル設定 ${embeddingModelConfigId} が見つかりません`);
  176. throw new Error(`モデル ${modelConfig.name} は無効化されているため、埋め込みベクトルを生成できません`);
  177. throw new Error(`モデル ${modelConfig.name} に baseUrl が設定されていません`);
  178. await new Promise(resolve => setTimeout(resolve, 100)); // 100ms待機
  179. * モデルIDに基づいて最大バッチサイズを決定
  180. return Math.min(10, configuredMaxBatchSize || 100); // Googleの場合は10を上限
  181. return Math.min(2048, configuredMaxBatchSize || 2048); // OpenAI v3は2048 exceeds limit
  182. * 単一バッチの埋め込み処理
  183. `総計 ${totalLength} 文字、平均 ${Math.round(avgLength)} 文字、` +
  184. `モデル制限: ${modelConfig.maxInputTokens || 8192} tokens`
  185. `テキスト長がモデルの制限。` +
  186. `現在: ${texts.length} 個のテキストで計 ${totalLength} 文字、` +
  187. `モデル制限: ${modelConfig.maxInputTokens || 8192} tokens。` +
  188. `アドバイス: Chunk sizeまたはバッチサイズを小さくしてください`
  189. this.logger.error(`リクエストパラメータ: model=${modelConfig.modelId}, inputLength=${texts[0]?.length}`);
  190. throw new Error(`埋め込み API の呼び出しに失敗しました: ${response.statusText} - ${errorText}`);
  191. * Fetch chunk configuration limits(フロントエンドのスライダー設定用)
  192. * クエリパラメータ: embeddingModelId - Embedding model ID
  193. fs.unlinkSync(pdfPath); // 空のファイルを削除
  194. EXTRACTED = 'extracted', // テキスト抽出が完了し、データベースに保存されました
  195. VECTORIZED = 'vectorized', // ベクトル化が完了し、ES にインデックスされました
  196. FAST = 'fast', // Fast Mode - Tika を使用
  197. PRECISE = 'precise', // Precise Mode - Vision Pipeline を使用
  198. @Column({ name: 'user_id', nullable: true }) // 暫定的に空を許可(デバッグ用)、将来的には必須にすべき
  199. content: string; // Tika で抽出されたテキスト内容を保存
  200. metadata: any; // Addedのメタデータを保存(画像の説明、信頼度など)
  201. pdfPath: string; // PDF ファイルパス(プレビュー用)
  202. ragPrompt: query, // オリジナルのクエリを使用
  203. * Fast Mode処理(既存フロー)
  204. * Precise Mode処理(新規フロー)
  205. * Precise Modeの結果をインデックス
  206. * PDF の特定ページの画像を取得
  207. if (error.message && (error.message.includes('context length') || error.message.includes('コンテキスト長 exceeds limit ') || error.message.includes('コンテキスト長 exceeds limit '))) {
  208. [chunk.content], // 単一テキスト
  209. * バッチ処理、メモリ制御付き
  210. * 失敗したファイルのベクトル化を再試行
  211. throw new NotFoundException('ファイルが存在しません');
  212. * ファイルのすべてのチャンク情報を取得
  213. * モデルの実際の次元数を取得(キャッシュ確認とプローブロジック付き)
  214. * AIを使用して文書のタイトルを自動生成する
  215. heapUsed: number; // 使用済みヒープメモリ (MB)
  216. heapTotal: number; // 総ヒープメモリ (MB)
  217. external: number; // 外部メモリ (MB)
  218. rss: number; // RSS (常駐セットサイズ) (MB)
  219. this.MAX_MEMORY_MB = parseInt(process.env.MAX_MEMORY_USAGE_MB || '1024'); // 1GB上限
  220. this.BATCH_SIZE = parseInt(process.env.CHUNK_BATCH_SIZE || '100'); // 1バッチあたり100チャンク
  221. this.GC_THRESHOLD_MB = parseInt(process.env.GC_THRESHOLD_MB || '800'); // 800MBでGCをトリガー
  222. * 現在のメモリ使用状況を取得
  223. * メモリ exceeds limit に近づいているかチェック
  224. return usage.heapUsed > this.MAX_MEMORY_MB * 0.85; // 85%閾値
  225. * メモリが利用可能になるまで待機(タイムアウトあり)
  226. throw new Error(`メモリ待機がタイムアウトしました: 現在 ${this.getMemoryUsage().heapUsed}MB > ${this.MAX_MEMORY_MB * 0.85}MB`);
  227. * ガベージコレクションを強制実行(可能な場合)
  228. * バッチサイズを動的に調整
  229. * 大規模データの処理:自動バッチングとメモリ制御
  230. * 処理に必要なメモリを見積もる
  231. * バッチ処理を使用すべきかチェック
  232. const threshold = this.MAX_MEMORY_MB * 0.7; // 70%閾値
  233. * LibreOffice サービスインターフェース定義
  234. pdf_data?: string; // base64 エンコードされた PDF データ
  235. * LibreOffice サービスの状態をチェック
  236. * ドキュメントを PDF に変換
  237. * @param filePath 変換するファイルのパス
  238. * @returns PDF ファイルのパス
  239. throw new Error(`ファイルが存在しません: ${filePath}`);
  240. timeout: 300000, // 5分タイムアウト
  241. responseType: 'stream', // ファイルストリームを受信
  242. maxRedirects: 5, // リダイレクトの最大数
  243. const delay = 2000 * attempt; // だんだん増える遅延
  244. throw new Error('変換がタイムアウトしました。ファイルが大きすぎる可能性があります');
  245. throw new Error(`変換に失敗しました: ${detail}`);
  246. throw new Error(`変換に失敗しました: ${lastError.message}`);
  247. throw new Error('LibreOffice サービスが実行されていません。サービスの状態を確認してください');
  248. throw new Error('LibreOffice サービスとの接続が切断されました。サービスが不安定である可能性があります');
  249. * ファイルの一括変換
  250. * サービスのバージョン情報を取得
  251. @Min(1, { message: 'ベクトル次元の最小値は 1 です' })
  252. @Max(4096, { message: 'ベクトル次元の最大値は 4096 です(Elasticsearch の制限)' })
  253. * モデルの入力トークン制限(embedding/rerank にのみ有効)
  254. * バッチ処理の制限(embedding/rerank にのみ有効)
  255. * ベトルモデルかどうか
  256. * モデルプロバイダー名
  257. * このモデルを有効にするかどうか
  258. * このモデルをデフォルトとして使用するかどうか
  259. dimensions?: number; // 埋め込みモデルの次元、システムによって自動的に検出され保存されます
  260. * モデルの入力トークン制限
  261. * 例: OpenAI=8191, Gemini=2048
  262. * 一括処理制限(1回のリクエストあたりの最大入力数)
  263. * 例: OpenAI=2048, Gemini=100
  264. * ベトルモデルかどうか(システム設定での識別用)
  265. * ユーザーは使用しないモデルを無効にして、誤選択を防ぐことができます
  266. * 各タイプ(llm, embedding, rerank)ごとに1つのみデフォルトにできます
  267. * モデルプロバイダー名(表示および識別用)
  268. * 例: "OpenAI", "Google Gemini", "Custom"
  269. * 指定されたモデルをデフォルトに設定
  270. * 指定されたタイプのデフォルトモデルを取得
  271. * 厳密なルール:Index Chat Configで指定されたモデルのみを返し、なければエラーを投げる
  272. * PDF 转图片接口定义
  273. density?: number; // DPI 分辨率,默认 300
  274. quality?: number; // JPEG 质量 (1-100),默认 85
  275. format?: 'jpeg' | 'png'; // 输出格式,默认 jpeg
  276. outDir?: string; // 输出目录,默认 ./temp
  277. path: string; // 图片文件路径
  278. pageIndex: number; // 页码(从 1 开始)
  279. size: number; // 文件大小(字节)
  280. width?: number; // 图片宽度
  281. height?: number; // 图片高度
  282. * PDF を画像リストに変換します
  283. * ImageMagick の convert コマンドを使用します
  284. throw new Error(`PDF ファイルが存在しません: ${pdfPath}`);
  285. throw new Error('PDF のページ数を取得できません');
  286. throw new Error(`Python での変換に失敗しました: ${result.error}`);
  287. throw new Error(`PDF から画像への変換に失敗しました: ${error.message}`);
  288. * 複数の PDF を一括変換
  289. * 画像ファイルのクリーンアップ
  290. * ディレクトリのクリーンアップ
  291. * 画像品質が妥当か確認
  292. originalScore?: number; // Rerank前のスコア(デバッグ用)
  293. vectorSimilarityThreshold: number = 0.3, // ベクトル検索のしきい値
  294. rerankSimilarityThreshold: number = 0.5, // Rerankのしきい値(デフォルト0.5)
  295. queriesToSearch = [hydeDoc]; // HyDE の場合は仮想ドキュメントをクエリとして使用
  296. throw new Error('Embedding model IDが提供されていません');
  297. effectiveTopK * 2 // 少し多めに残す
  298. score: r.score, // Rerank スコア
  299. originalScore: originalItem.score // 元のスコア
  300. * Search resultsの重複排除
  301. * クエリを拡張してバリエーションを生成
  302. .slice(0, 3); // 最大3つに制限
  303. * 仮想的なドキュメント(HyDE)を生成
  304. * 内部タスク用の LLM インスタンスを取得
  305. * リランクの実行
  306. * @param query ユーザーのクエリ
  307. * @param documents 候補ドキュメントリスト
  308. * @param userId ユーザーID
  309. * @param rerankModelId 選択された Rerank モデル設定ID
  310. * @param topN 返す結果の数 (上位 N 個)
  311. return { message: '对话历史删除成功' };
  312. mode?: 'fast' | 'precise'; // 処理モード
  313. `ユーザー ${req.user.id} がファイルをアップロードしました: ${file.originalname} (${this.formatBytes(file.size)})`,
  314. estimatedChunks: Math.ceil(file.size / (indexingConfig.chunkSize * 4)), // 推定チャンク数
  315. ); // 環境変数からアップロードパスを取得し、ない場合はデフォルトとして './uploads' を使用します
  316. fileSize: maxFileSize, // ファイルサイズの制限
  317. console.log('パスワード:', randomPassword);
  318. import { User } from '../user/user.entity'; // Userエンティティのパス
  319. console.log('=== updateLanguage デバッグ ===');
  320. console.log('=== getLanguage デバッグ ===');
  321. * システム全体のグローバル設定を取得する
  322. * システム全体のグローバル設定を更新する
  323. * Vision 服务接口定义
  324. text: string; // 抽出されたテキスト内容
  325. images: ImageDescription[]; // 画像の説明
  326. layout: string; // レイアウトの種類
  327. confidence: number; // 信頼度 (0-1)
  328. pageIndex?: number; // 页码
  329. type: string; // 图片类型 (图表/架构图/流程图等)
  330. description: string; // 详细描述
  331. position?: number; // ページ内での位置
  332. estimatedCost: number; // 预估成本(美元)
  333. * 単一画像の分析(ドキュメントページ)
  334. const baseDelay = 3000; // 3秒の基礎遅延
  335. const delay = baseDelay + Math.random() * 2000; // 3-5秒のランダムな遅延
  336. * 実際の画像分析を実行
  337. temperature: 0.1, // ランダム性を抑え、一貫性を高める
  338. page: pageIndex ? ` (第 ${pageIndex} ページ)` : '',
  339. throw error; // 重新抛出错误供重试机制处理
  340. * 再試行可能なエラーかどうかを判断
  341. if (errorCode === 429 || errorMessage.includes('rate limit') || errorMessage.includes('リクエストが多すぎます')) {
  342. * 遅延関数
  343. * 複数画像の一括分析
  344. * 画像品質のチェック
  345. return { isGood: false, reason: `ファイルが小さすぎます (${sizeKB.toFixed(2)}KB)`, score: 0 };
  346. return { isGood: false, reason: `ファイルが大きすぎます (${sizeKB.toFixed(2)}KB)`, score: 0 };
  347. * サポートされている画像ファイルかどうかを確認
  348. * MIME タイプを取得
  349. * 旧インターフェース互換:単一画像の内容を抽出
  350. * コスト制御およびクォータ管理サービス
  351. * Vision Pipeline の API 呼び出しコストを管理するために使用されます
  352. monthlyCost: number; // 今月の使用済みコスト
  353. maxCost: number; // 月間最大コスト
  354. remaining: number; // 残りコスト
  355. lastReset: Date; // 最終リセット時間
  356. estimatedCost: number; // 推定コスト
  357. estimatedTime: number; // 推定時間(秒)
  358. pageBreakdown: { // ページごとの明細
  359. private readonly COST_PER_PAGE = 0.01; // 1ページあたりのコスト(USD)
  360. private readonly DEFAULT_MONTHLY_LIMIT = 100; // デフォルトの月間制限(USD)
  361. * 処理コストの推定
  362. const estimatedTime = pageCount * 3; // 1ページあたり約 3 秒
  363. * ユーザーのクォータをチェック
  364. reason: `クォータ不足: 残り $${quota.remaining.toFixed(2)}, 必要 $${estimatedCost.toFixed(2)}`,
  365. * クォータの差し引き
  366. * ユーザーのクォータを取得
  367. throw new Error(`ユーザー ${userId} は存在しません`);
  368. * 月間クォータのチェックとリセット
  369. * ユーザーのクォータ制限を設定
  370. * コストレポートの取得
  371. quotaUsage: number; // パーセンテージ
  372. * コスト警告閾値のチェック
  373. message: `⚠️ クォータ使用率が ${usagePercent.toFixed(1)}% に達しました。残り $${quota.remaining.toFixed(2)}`,
  374. message: `💡 クォータ使用率 ${usagePercent.toFixed(1)}%。コストの管理に注意してください`,
  375. * コスト表示のフォーマット
  376. * 時間表示のフォーマット
  377. return `${seconds.toFixed(0)}秒`;
  378. return `${minutes}分${remainingSeconds.toFixed(0)}秒`;
  379. * Vision Pipeline サービス(コスト制御付き)
  380. * これは vision-pipeline.service.ts の拡張版であり、コスト制御が統合されています
  381. private costControl: CostControlService, // 新增成本控制服务
  382. * メイン処理フロー:Precise Mode(コスト制御付き)
  383. this.updateStatus('converting', 10, 'ドキュメント形式を変換中...');
  384. this.updateStatus('splitting', 30, 'PDF を画像に変換中...');
  385. throw new Error('PDF から画像への変換に失敗しました。画像が生成されませんでした');
  386. this.updateStatus('checking', 40, 'クォータを確認し、コストを見積もり中...');
  387. this.updateStatus('analyzing', 50, 'ビジョンモデルを使用してページをAnalyzing...');
  388. this.updateStatus('completed', 100, '処理が完了しました。一時ファイルをクリーンアップ中...');
  389. * Vision モデル設定の取得
  390. throw new Error(`モデル設定が見つかりません: ${modelId}`);
  391. * PDF への変換
  392. * 形式検出とモードの推奨(コスト見積もり付き)
  393. reason: `サポートされていないファイル形式です: ${ext}`,
  394. warnings: ['Fast Mode(テキスト抽出のみ)を使用します'],
  395. reason: `形式 ${ext} はPrecise Modeをサポートしていません`,
  396. reason: 'ファイルが大きいため、完全な情報を保持するためにPrecise Modeを推奨します',
  397. warnings: ['処理時間が長くなる可能性があります', 'API 費用が発生します'],
  398. reason: 'Precise Modeが利用可能です。テキストと画像の混合コンテンツを保持できます',
  399. warnings: ['API 費用が発生します'],
  400. * ユーザーのクォータ情報を取得
  401. * 処理状態の更新(リアルタイムフィードバック用)
  402. * Vision Pipeline 接口定义
  403. duration: number; // 秒
  404. estimatedTime?: number; // 秒