Browse Source

国际化

anhuiqiang 1 week ago
parent
commit
6e0a78a352
2 changed files with 45 additions and 33 deletions
  1. 36 33
      server/src/chat/chat.service.ts
  2. 9 0
      server/src/i18n/messages.ts

+ 36 - 33
server/src/chat/chat.service.ts

@@ -66,26 +66,26 @@ export class ChatService {
     tenantId?: string // New: tenant isolation
     tenantId?: string // New: tenant isolation
   ): AsyncGenerator<{ type: 'content' | 'sources' | 'historyId'; data: any }> {
   ): AsyncGenerator<{ type: 'content' | 'sources' | 'historyId'; data: any }> {
     console.log('=== ChatService.streamChat ===');
     console.log('=== ChatService.streamChat ===');
-    console.log('ユーザーID:', userId);
-    console.log('ユーザー言語:', userLanguage);
-    console.log('選択された埋め込みモデルID:', selectedEmbeddingId);
-    console.log('選択されたグループ:', selectedGroups);
-    console.log('選択されたファイル:', selectedFiles);
-    console.log('履歴ID:', historyId);
+    console.log('User ID:', userId);
+    console.log('User language:', userLanguage);
+    console.log('Selected embedding model ID:', selectedEmbeddingId);
+    console.log('Selected groups:', selectedGroups);
+    console.log('Selected files:', selectedFiles);
+    console.log('History ID:', historyId);
     console.log('Temperature:', temperature);
     console.log('Temperature:', temperature);
     console.log('Max Tokens:', maxTokens);
     console.log('Max Tokens:', maxTokens);
     console.log('Top K:', topK);
     console.log('Top K:', topK);
-    console.log('類似度しきい値:', similarityThreshold);
-    console.log('Rerankしきい値:', rerankSimilarityThreshold);
-    console.log('Query拡張:', enableQueryExpansion);
+    console.log('Similarity threshold:', similarityThreshold);
+    console.log('Rerank threshold:', rerankSimilarityThreshold);
+    console.log('Query expansion:', enableQueryExpansion);
     console.log('HyDE:', enableHyDE);
     console.log('HyDE:', enableHyDE);
-    console.log('モデル設定:', {
+    console.log('Model configuration:', {
       name: modelConfig.name,
       name: modelConfig.name,
       modelId: modelConfig.modelId,
       modelId: modelConfig.modelId,
       baseUrl: modelConfig.baseUrl,
       baseUrl: modelConfig.baseUrl,
     });
     });
-    console.log('API Key プレフィックス:', modelConfig.apiKey?.substring(0, 10) + '...');
-    console.log('API Key 長さ:', modelConfig.apiKey?.length);
+    console.log('API Key prefix:', modelConfig.apiKey?.substring(0, 10) + '...');
+    console.log('API Key length:', modelConfig.apiKey?.length);
 
 
     // Get current language setting (keeping LANGUAGE_CONFIG for backward compatibility, now uses i18n service)
     // Get current language setting (keeping LANGUAGE_CONFIG for backward compatibility, now uses i18n service)
     // Use actual language based on user settings
     // Use actual language based on user settings
@@ -284,13 +284,14 @@ export class ChatService {
     instruction: string,
     instruction: string,
     context: string,
     context: string,
     modelConfig: ModelConfig,
     modelConfig: ModelConfig,
+    userLanguage: string = DEFAULT_LANGUAGE,
   ): AsyncGenerator<{ type: 'content'; data: any }> {
   ): AsyncGenerator<{ type: 'content'; data: any }> {
     try {
     try {
       this.logger.log(this.i18nService.formatMessage('modelCall', {
       this.logger.log(this.i18nService.formatMessage('modelCall', {
         type: 'LLM (Assist)',
         type: 'LLM (Assist)',
         model: `${modelConfig.name} (${modelConfig.modelId})`,
         model: `${modelConfig.name} (${modelConfig.modelId})`,
         user: 'N/A'
         user: 'N/A'
-      }, 'ja'));
+      }, userLanguage));
       const llm = new ChatOpenAI({
       const llm = new ChatOpenAI({
         apiKey: modelConfig.apiKey || 'ollama',
         apiKey: modelConfig.apiKey || 'ollama',
         streaming: true,
         streaming: true,
@@ -301,14 +302,13 @@ export class ChatService {
         },
         },
       });
       });
 
 
-      const systemPrompt = `${this.i18nService.getMessage('intelligentAssistant', 'ja')}
-提供されたテキスト内容を、ユーザーの指示に基づいて修正または改善please。
-挨拶や結びの言葉(「わかりました、こちらが...」etc.)は含めず、修正後の内容のみを直接出力please。
+      const systemPrompt = `${this.i18nService.getMessage('intelligentAssistant', userLanguage)}
+${this.i18nService.getMessage('assistSystemPrompt', userLanguage)}
 
 
-コンテキスト(現在の内容):
+${this.i18nService.getMessage('contextLabel', userLanguage)}:
 ${context}
 ${context}
 
 
-ユーザーの指示:
+${this.i18nService.getMessage('userInstructionLabel', userLanguage)}:
 ${instruction}`;
 ${instruction}`;
 
 
       const stream = await llm.stream(systemPrompt);
       const stream = await llm.stream(systemPrompt);
@@ -319,8 +319,8 @@ ${instruction}`;
         }
         }
       }
       }
     } catch (error) {
     } catch (error) {
-      this.logger.error(this.i18nService.getMessage('assistStreamError', 'ja'), error);
-      yield { type: 'content', data: `${this.i18nService.getMessage('error', 'ja')}: ${error.message}` };
+      this.logger.error(this.i18nService.getMessage('assistStreamError', userLanguage), error);
+      yield { type: 'content', data: `${this.i18nService.getMessage('error', userLanguage)}: ${error.message}` };
     }
     }
   }
   }
 
 
@@ -331,30 +331,31 @@ ${instruction}`;
     selectedGroups?: string[], // New parameter
     selectedGroups?: string[], // New parameter
     explicitFileIds?: string[], // New parameter
     explicitFileIds?: string[], // New parameter
     tenantId?: string, // Added
     tenantId?: string, // Added
+    userLanguage: string = DEFAULT_LANGUAGE,
   ): Promise<any[]> {
   ): Promise<any[]> {
     try {
     try {
       // Join keywords into search string
       // Join keywords into search string
       const combinedQuery = keywords.join(' ');
       const combinedQuery = keywords.join(' ');
-      console.log(this.i18nService.getMessage('searchString', 'ja') + combinedQuery);
+      console.log(this.i18nService.getMessage('searchString', userLanguage) + combinedQuery);
 
 
       // Check if embedding model ID is provided
       // Check if embedding model ID is provided
       if (!embeddingModelId) {
       if (!embeddingModelId) {
-        console.log(this.i18nService.getMessage('embeddingModelIdNotProvided', 'ja'));
+        console.log(this.i18nService.getMessage('embeddingModelIdNotProvided', userLanguage));
         return [];
         return [];
       }
       }
 
 
       // Use actual embedding vector
       // Use actual embedding vector
-      console.log(this.i18nService.getMessage('generatingEmbeddings', 'ja'));
+      console.log(this.i18nService.getMessage('generatingEmbeddings', userLanguage));
       const queryEmbedding = await this.embeddingService.getEmbeddings(
       const queryEmbedding = await this.embeddingService.getEmbeddings(
         [combinedQuery],
         [combinedQuery],
         userId,
         userId,
         embeddingModelId,
         embeddingModelId,
       );
       );
       const queryVector = queryEmbedding[0];
       const queryVector = queryEmbedding[0];
-      console.log(this.i18nService.getMessage('embeddingsGenerated', 'ja') + this.i18nService.getMessage('dimensions', 'ja') + ':', queryVector.length);
+      console.log(this.i18nService.getMessage('embeddingsGenerated', userLanguage) + this.i18nService.getMessage('dimensions', userLanguage) + ':', queryVector.length);
 
 
       // Hybrid search
       // Hybrid search
-      console.log(this.i18nService.getMessage('performingHybridSearch', 'ja'));
+      console.log(this.i18nService.getMessage('performingHybridSearch', userLanguage));
       const results = await this.elasticsearchService.hybridSearch(
       const results = await this.elasticsearchService.hybridSearch(
         queryVector,
         queryVector,
         combinedQuery,
         combinedQuery,
@@ -363,13 +364,13 @@ ${instruction}`;
         0.6,
         0.6,
         selectedGroups, // Pass selected groups
         selectedGroups, // Pass selected groups
         explicitFileIds, // Pass explicit file IDs
         explicitFileIds, // Pass explicit file IDs
-        tenantId, // Added: tenantId
+        tenantId, // Pass tenant ID
       );
       );
-      console.log(this.i18nService.getMessage('esSearchCompleted', 'ja') + this.i18nService.getMessage('resultsCount', 'ja') + ':', results.length);
+      console.log(this.i18nService.getMessage('esSearchCompleted', userLanguage) + this.i18nService.getMessage('resultsCount', userLanguage) + ':', results.length);
 
 
       return results.slice(0, 10);
       return results.slice(0, 10);
     } catch (error) {
     } catch (error) {
-      console.error(this.i18nService.getMessage('hybridSearchFailed', 'ja') + ':', error);
+      console.error(this.i18nService.getMessage('hybridSearchFailed', userLanguage) + ':', error);
       return [];
       return [];
     }
     }
   }
   }
@@ -398,7 +399,7 @@ ${instruction}`;
       )
       )
       .join('\n');
       .join('\n');
   }
   }
-  async getContextForTopic(topic: string, userId: string, tenantId?: string, groupId?: string, fileIds?: string[]): Promise<string> {
+  async getContextForTopic(topic: string, userId: string, tenantId?: string, groupId?: string, fileIds?: string[], userLanguage: string = DEFAULT_LANGUAGE): Promise<string> {
     try {
     try {
       // Use organization's default embedding from Index Chat Config (strict)
       // Use organization's default embedding from Index Chat Config (strict)
       const embeddingModel = await this.modelConfigService.findDefaultByType(tenantId || 'default', ModelType.EMBEDDING);
       const embeddingModel = await this.modelConfigService.findDefaultByType(tenantId || 'default', ModelType.EMBEDDING);
@@ -409,12 +410,13 @@ ${instruction}`;
         embeddingModel.id,
         embeddingModel.id,
         groupId ? [groupId] : undefined,
         groupId ? [groupId] : undefined,
         fileIds,
         fileIds,
-        tenantId
+        tenantId,
+        userLanguage,
       );
       );
 
 
       return this.buildContext(results);
       return this.buildContext(results);
     } catch (err) {
     } catch (err) {
-      this.logger.error(`${this.i18nService.getMessage('getContextForTopicFailed', 'ja')}: ${err.message}`);
+      this.logger.error(`${this.i18nService.getMessage('getContextForTopicFailed', userLanguage)}: ${err.message}`);
       return '';
       return '';
     }
     }
   }
   }
@@ -424,6 +426,7 @@ ${instruction}`;
     userId: string,
     userId: string,
     tenantId?: string,
     tenantId?: string,
     modelConfig?: ModelConfig, // Optional, looks up if not provided
     modelConfig?: ModelConfig, // Optional, looks up if not provided
+    userLanguage: string = DEFAULT_LANGUAGE,
   ): Promise<string> {
   ): Promise<string> {
     try {
     try {
       let config = modelConfig;
       let config = modelConfig;
@@ -454,13 +457,13 @@ ${instruction}`;
 
 
       return String(response.content);
       return String(response.content);
     } catch (error) {
     } catch (error) {
-      this.logger.error(this.i18nService.getMessage('simpleChatGenerationError', 'ja'), error);
+      this.logger.error(this.i18nService.getMessage('simpleChatGenerationError', userLanguage), error);
       throw error;
       throw error;
     }
     }
   }
   }
 
 
   /**
   /**
-   * 対話内容に基づいてチャットのタイトルを自動生成する
+   * Automatically generate chat title based on conversation content
    */
    */
   async generateChatTitle(historyId: string, userId: string, tenantId?: string): Promise<string | null> {
   async generateChatTitle(historyId: string, userId: string, tenantId?: string): Promise<string | null> {
     this.logger.log(`Generating automatic title for chat session ${historyId}`);
     this.logger.log(`Generating automatic title for chat session ${historyId}`);

+ 9 - 0
server/src/i18n/messages.ts

@@ -357,6 +357,9 @@ export const statusMessages = {
     userLabel: '用户',
     userLabel: '用户',
     assistantLabel: '助手',
     assistantLabel: '助手',
     intelligentAssistant: '您是智能写作助手。',
     intelligentAssistant: '您是智能写作助手。',
+    assistSystemPrompt: '请根据用户的指示修正或改进提供的文本内容。不要包含问候语或结束语(如"明白了,这是..."等),直接输出修正后的内容。',
+    contextLabel: '上下文(当前内容)',
+    userInstructionLabel: '用户指示',
     searchString: '搜索字符串: ',
     searchString: '搜索字符串: ',
     embeddingModelIdNotProvided: '未提供嵌入模型ID',
     embeddingModelIdNotProvided: '未提供嵌入模型ID',
     generatingEmbeddings: '生成嵌入向量...',
     generatingEmbeddings: '生成嵌入向量...',
@@ -449,6 +452,9 @@ export const statusMessages = {
     userLabel: 'ユーザー',
     userLabel: 'ユーザー',
     assistantLabel: 'アシスタント',
     assistantLabel: 'アシスタント',
     intelligentAssistant: 'あなたはインテリジェントな執筆アシスタントです。',
     intelligentAssistant: 'あなたはインテリジェントな執筆アシスタントです。',
+    assistSystemPrompt: '提供されたテキスト内容を、ユーザーの指示に基づいて修正または改善してください。挨拶や結びの言葉(「わかりました、こちらが...」など)は含めず、修正後の内容のみを直接出力してください。',
+    contextLabel: 'コンテキスト(現在の内容)',
+    userInstructionLabel: 'ユーザーの指示',
     searchString: '検索文字列: ',
     searchString: '検索文字列: ',
     embeddingModelIdNotProvided: '埋め込みモデルIDが提供されていません',
     embeddingModelIdNotProvided: '埋め込みモデルIDが提供されていません',
     generatingEmbeddings: '埋め込みベクトルを生成中...',
     generatingEmbeddings: '埋め込みベクトルを生成中...',
@@ -541,6 +547,9 @@ export const statusMessages = {
     userLabel: 'User',
     userLabel: 'User',
     assistantLabel: 'Assistant',
     assistantLabel: 'Assistant',
     intelligentAssistant: 'You are an intelligent writing assistant.',
     intelligentAssistant: 'You are an intelligent writing assistant.',
+    assistSystemPrompt: 'Please revise or improve the provided text content based on the user\'s instructions. Do not include greetings or closing phrases (such as "Understood, here is..." etc.), output only the revised content directly.',
+    contextLabel: 'Context (current content)',
+    userInstructionLabel: 'User instructions',
     searchString: 'Search string: ',
     searchString: 'Search string: ',
     embeddingModelIdNotProvided: 'Embedding model ID not provided',
     embeddingModelIdNotProvided: 'Embedding model ID not provided',
     generatingEmbeddings: 'Generating embeddings...',
     generatingEmbeddings: 'Generating embeddings...',