chatService.ts 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. export interface ChatMessage {
  2. role: 'user' | 'assistant';
  3. content: string;
  4. }
  5. export interface ChatSource {
  6. fileName: string;
  7. title?: string;
  8. content: string;
  9. score: number;
  10. chunkIndex: number;
  11. fileId?: string;
  12. }
  13. export class ChatService {
  14. async *streamChat(
  15. message: string,
  16. history: ChatMessage[],
  17. authToken: string,
  18. userLanguage: string = 'ja',
  19. selectedEmbeddingId?: string,
  20. selectedLLMId?: string, // 追加: 選択された LLM ID
  21. selectedGroups?: string[], // 追加: 選択されたグループ
  22. selectedFiles?: string[], // 追加: 選択されたファイル
  23. historyId?: string, // 追加: 会話履歴 ID
  24. enableRerank?: boolean, // 追加: Rerank を有効にする
  25. selectedRerankId?: string, // 追加: Rerank モデル ID
  26. temperature?: number, // 追加: temperature パラメータ
  27. maxTokens?: number, // 追加: maxTokens パラメータ
  28. topK?: number, // 追加: topK パラメータ
  29. similarityThreshold?: number, // 追加: similarityThreshold パラメータ
  30. rerankSimilarityThreshold?: number, // 追加: rerankSimilarityThreshold パラメータ
  31. enableQueryExpansion?: boolean, // 追加
  32. enableHyDE?: boolean // 追加
  33. ): AsyncGenerator<{ type: 'content' | 'sources' | 'error' | 'historyId'; data: any }> {
  34. try {
  35. const response = await fetch('/api/chat/stream', {
  36. method: 'POST',
  37. headers: {
  38. 'Content-Type': 'application/json',
  39. 'Authorization': `Bearer ${authToken}`,
  40. 'x-api-key': localStorage.getItem('kb_api_key') || authToken,
  41. 'x-user-language': userLanguage || localStorage.getItem('userLanguage') || 'ja',
  42. },
  43. body: JSON.stringify({
  44. message,
  45. history,
  46. userLanguage,
  47. selectedEmbeddingId,
  48. selectedLLMId, // Pass LLM ID
  49. selectedGroups, // グループフィルタパラメータを渡す
  50. selectedFiles, // ファイルフィルタパラメータを渡す
  51. historyId, // 履歴 ID を渡す
  52. enableRerank,
  53. selectedRerankId,
  54. temperature, // temperature パラメータを渡す
  55. maxTokens, // maxTokens パラメータを渡す
  56. topK, // topK パラメータを渡す
  57. similarityThreshold, // similarityThreshold パラメータを渡す
  58. rerankSimilarityThreshold, // rerankSimilarityThreshold パラメータを渡す
  59. enableQueryExpansion, // enableQueryExpansion を渡す
  60. enableHyDE // enableHyDE を渡す
  61. }),
  62. });
  63. if (!response.ok) {
  64. let errorMessage = 'リクエストに失敗しました';
  65. try {
  66. const error = await response.json();
  67. errorMessage = error.error || error.message || 'リクエストに失敗しました';
  68. } catch {
  69. errorMessage = `サーバーエラー: ${response.status}`;
  70. }
  71. yield { type: 'error', data: errorMessage };
  72. return;
  73. }
  74. const reader = response.body?.getReader();
  75. if (!reader) {
  76. yield { type: 'error', data: 'レスポンスストリームを読み取れません' };
  77. return;
  78. }
  79. const decoder = new TextDecoder();
  80. let buffer = '';
  81. while (true) {
  82. const { done, value } = await reader.read();
  83. if (done) break;
  84. buffer += decoder.decode(value, { stream: true });
  85. const lines = buffer.split('\n');
  86. buffer = lines.pop() || '';
  87. for (const line of lines) {
  88. if (line.startsWith('data: ')) {
  89. const data = line.slice(6);
  90. if (data === '[DONE]') {
  91. return;
  92. }
  93. try {
  94. const parsed = JSON.parse(data);
  95. yield parsed;
  96. } catch (e) {
  97. console.warn('Failed to parse SSE data:', data);
  98. }
  99. }
  100. }
  101. }
  102. } catch (error) {
  103. yield { type: 'error', data: error.message || 'ネットワークエラー' };
  104. }
  105. }
  106. async *streamAssist(
  107. instruction: string,
  108. context: string,
  109. authToken: string
  110. ): AsyncGenerator<{ type: 'content' | 'error'; data: any }> {
  111. try {
  112. const response = await fetch('/api/chat/assist', {
  113. method: 'POST',
  114. headers: {
  115. 'Content-Type': 'application/json',
  116. 'Authorization': `Bearer ${authToken}`,
  117. 'x-api-key': localStorage.getItem('kb_api_key') || authToken,
  118. 'x-user-language': localStorage.getItem('userLanguage') || 'ja',
  119. },
  120. body: JSON.stringify({ instruction, context }),
  121. });
  122. if (!response.ok) {
  123. yield { type: 'error', data: 'リクエストに失敗しました' };
  124. return;
  125. }
  126. const reader = response.body?.getReader();
  127. if (!reader) return;
  128. const decoder = new TextDecoder();
  129. let buffer = '';
  130. while (true) {
  131. const { done, value } = await reader.read();
  132. if (done) break;
  133. buffer += decoder.decode(value, { stream: true });
  134. const lines = buffer.split('\n');
  135. buffer = lines.pop() || '';
  136. for (const line of lines) {
  137. if (line.startsWith('data: ')) {
  138. const data = line.slice(6);
  139. if (data === '[DONE]') return;
  140. try {
  141. yield JSON.parse(data);
  142. } catch (e) { console.warn(e) }
  143. }
  144. }
  145. }
  146. } catch (error: any) {
  147. yield { type: 'error', data: error.message };
  148. }
  149. }
  150. }
  151. export const chatService = new ChatService();