universal-llm-client 4.0.0 → 4.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (127) hide show
  1. package/dist/ai-model.d.ts +20 -22
  2. package/dist/ai-model.d.ts.map +1 -1
  3. package/dist/ai-model.js +26 -23
  4. package/dist/ai-model.js.map +1 -1
  5. package/dist/client.d.ts +5 -5
  6. package/dist/client.d.ts.map +1 -1
  7. package/dist/client.js +17 -9
  8. package/dist/client.js.map +1 -1
  9. package/dist/http.d.ts +2 -0
  10. package/dist/http.d.ts.map +1 -1
  11. package/dist/http.js +1 -0
  12. package/dist/http.js.map +1 -1
  13. package/dist/index.d.ts +3 -3
  14. package/dist/index.d.ts.map +1 -1
  15. package/dist/index.js +4 -4
  16. package/dist/index.js.map +1 -1
  17. package/dist/interfaces.d.ts +49 -11
  18. package/dist/interfaces.d.ts.map +1 -1
  19. package/dist/interfaces.js +14 -0
  20. package/dist/interfaces.js.map +1 -1
  21. package/dist/providers/anthropic.d.ts +56 -0
  22. package/dist/providers/anthropic.d.ts.map +1 -0
  23. package/dist/providers/anthropic.js +524 -0
  24. package/dist/providers/anthropic.js.map +1 -0
  25. package/dist/providers/google.d.ts +5 -0
  26. package/dist/providers/google.d.ts.map +1 -1
  27. package/dist/providers/google.js +64 -8
  28. package/dist/providers/google.js.map +1 -1
  29. package/dist/providers/index.d.ts +1 -0
  30. package/dist/providers/index.d.ts.map +1 -1
  31. package/dist/providers/index.js +1 -0
  32. package/dist/providers/index.js.map +1 -1
  33. package/dist/providers/ollama.d.ts.map +1 -1
  34. package/dist/providers/ollama.js +38 -11
  35. package/dist/providers/ollama.js.map +1 -1
  36. package/dist/providers/openai.d.ts.map +1 -1
  37. package/dist/providers/openai.js +9 -7
  38. package/dist/providers/openai.js.map +1 -1
  39. package/dist/router.d.ts +13 -33
  40. package/dist/router.d.ts.map +1 -1
  41. package/dist/router.js +33 -57
  42. package/dist/router.js.map +1 -1
  43. package/dist/stream-decoder.d.ts +29 -2
  44. package/dist/stream-decoder.d.ts.map +1 -1
  45. package/dist/stream-decoder.js +39 -11
  46. package/dist/stream-decoder.js.map +1 -1
  47. package/dist/structured-output.d.ts +107 -181
  48. package/dist/structured-output.d.ts.map +1 -1
  49. package/dist/structured-output.js +137 -192
  50. package/dist/structured-output.js.map +1 -1
  51. package/dist/zod-adapter.d.ts +44 -0
  52. package/dist/zod-adapter.d.ts.map +1 -0
  53. package/dist/zod-adapter.js +61 -0
  54. package/dist/zod-adapter.js.map +1 -0
  55. package/package.json +9 -1
  56. package/src/ai-model.ts +350 -0
  57. package/src/auditor.ts +213 -0
  58. package/src/client.ts +402 -0
  59. package/src/debug/debug-google-streaming.ts +97 -0
  60. package/src/debug/debug-tool-execution.ts +86 -0
  61. package/src/debug/test-lmstudio-tools.ts +155 -0
  62. package/src/demos/README.md +47 -0
  63. package/src/demos/basic/universal-llm-examples.ts +161 -0
  64. package/src/demos/mcp/astrid-memory-demo.ts +295 -0
  65. package/src/demos/mcp/astrid-persona-memory.ts +357 -0
  66. package/src/demos/mcp/mcp-mongodb-demo.ts +275 -0
  67. package/src/demos/mcp/simple-astrid-memory.ts +148 -0
  68. package/src/demos/mcp/simple-mcp-demo.ts +68 -0
  69. package/src/demos/mcp/working-mcp-demo.ts +62 -0
  70. package/src/demos/model-alias-demo.ts +0 -0
  71. package/src/demos/tools/RAG_MEMORY_INTEGRATION.md +267 -0
  72. package/src/demos/tools/astrid-memory-demo.ts +270 -0
  73. package/src/demos/tools/astrid-production-memory-clean.ts +785 -0
  74. package/src/demos/tools/astrid-production-memory.ts +558 -0
  75. package/src/demos/tools/basic-translation-test.ts +66 -0
  76. package/src/demos/tools/chromadb-similarity-tuning.ts +390 -0
  77. package/src/demos/tools/clean-multilingual-conversation.ts +209 -0
  78. package/src/demos/tools/clean-translation-test.ts +119 -0
  79. package/src/demos/tools/clean-universal-multilingual-test.ts +131 -0
  80. package/src/demos/tools/complete-rag-demo.ts +369 -0
  81. package/src/demos/tools/complete-tool-demo.ts +132 -0
  82. package/src/demos/tools/demo-tool-calling.ts +124 -0
  83. package/src/demos/tools/dynamic-language-switching-test.ts +251 -0
  84. package/src/demos/tools/hybrid-thinking-test.ts +154 -0
  85. package/src/demos/tools/memory-integration-test.ts +420 -0
  86. package/src/demos/tools/multilingual-memory-system.ts +802 -0
  87. package/src/demos/tools/ondemand-translation-demo.ts +655 -0
  88. package/src/demos/tools/production-tool-demo.ts +245 -0
  89. package/src/demos/tools/revolutionary-multilingual-test.ts +151 -0
  90. package/src/demos/tools/rigorous-language-analysis.ts +218 -0
  91. package/src/demos/tools/test-universal-memory-system.ts +126 -0
  92. package/src/demos/tools/translation-integration-guide.ts +346 -0
  93. package/src/demos/tools/universal-memory-system.ts +560 -0
  94. package/src/http.ts +247 -0
  95. package/src/index.ts +161 -0
  96. package/src/interfaces.ts +657 -0
  97. package/src/mcp.ts +345 -0
  98. package/src/providers/anthropic.ts +762 -0
  99. package/src/providers/google.ts +620 -0
  100. package/src/providers/index.ts +8 -0
  101. package/src/providers/ollama.ts +469 -0
  102. package/src/providers/openai.ts +392 -0
  103. package/src/router.ts +780 -0
  104. package/src/stream-decoder.ts +361 -0
  105. package/src/structured-output.ts +759 -0
  106. package/src/test-scripts/test-advanced-tools.ts +310 -0
  107. package/src/test-scripts/test-google-streaming-enhanced.ts +147 -0
  108. package/src/test-scripts/test-google-streaming.ts +63 -0
  109. package/src/test-scripts/test-google-system-prompt-comprehensive.ts +189 -0
  110. package/src/test-scripts/test-mcp-config.ts +28 -0
  111. package/src/test-scripts/test-mcp-connection.ts +29 -0
  112. package/src/test-scripts/test-system-message-positions.ts +163 -0
  113. package/src/test-scripts/test-system-prompt-improvement-demo.ts +83 -0
  114. package/src/test-scripts/test-tool-calling.ts +231 -0
  115. package/src/tests/ai-model.test.ts +1614 -0
  116. package/src/tests/auditor.test.ts +224 -0
  117. package/src/tests/http.test.ts +200 -0
  118. package/src/tests/interfaces.test.ts +117 -0
  119. package/src/tests/providers/google.test.ts +660 -0
  120. package/src/tests/providers/ollama.test.ts +954 -0
  121. package/src/tests/providers/openai.test.ts +1122 -0
  122. package/src/tests/router.test.ts +254 -0
  123. package/src/tests/stream-decoder.test.ts +179 -0
  124. package/src/tests/structured-output.test.ts +1450 -0
  125. package/src/tests/tools.test.ts +175 -0
  126. package/src/tools.ts +246 -0
  127. package/src/zod-adapter.ts +72 -0
@@ -0,0 +1,655 @@
1
+ /**
2
+ * On-Demand Translation Demo
3
+ *
4
+ * Tests the memory system with unexpected languages using the real TranslationService
5
+ * for dynamic system prompt translation when pre-translated versions aren't available.
6
+ */
7
+
8
+ import '@dotenvx/dotenvx/config';
9
+
10
+ import { AIModelFactory, ToolBuilder, LLMChatMessage } from '../../index';
11
+ import { ChromaDBService } from '../../../../../src/services/ChromaDBService.js';
12
+ import { DatabaseConnection } from '../../../../../src/database/connection.js';
13
+ import { UserModel, UserGender, UserSexualOrientation } from '../../../../../src/models/User.js';
14
+ import { AuraPersonaModel } from '../../../../../src/models/AuraPersona.js';
15
+ import { UserPersonaModel } from '../../../../../src/models/UserPersona.js';
16
+ import { TranslationService, TranscreationOptions } from '../../../../../src/services/translation/TranslationService.js';
17
+ import { OllamaRouter } from '../../../services/OllamaRouter';
18
+
19
+ // Import existing multilingual components
20
+ import {
21
+ UNIVERSAL_MEMORY_INSTRUCTIONS,
22
+ MULTILINGUAL_PERSONA_CONFIGS,
23
+ createMultilingualMemoryTools
24
+ } from './multilingual-memory-system.js';
25
+
26
+ interface UnexpectedLanguageConfig {
27
+ code: string;
28
+ name: string;
29
+ testMessage: string;
30
+ testComplexMessage: string;
31
+ expectedTranslation?: string; // For validation
32
+ }
33
+
34
+ // Test with languages not in our pre-translated set
35
+ const UNEXPECTED_LANGUAGES: { [key: string]: UnexpectedLanguageConfig } = {
36
+ 'de': {
37
+ code: 'de',
38
+ name: 'German',
39
+ testMessage: "Hallo Astrid! Ich bin Alex, ein 28-jähriger Software-Ingenieur aus Seattle. Ich spezialisiere mich auf Gesundheits-KI, weil meine jüngere Schwester mit einer seltenen genetischen Erkrankung geboren wurde. Ich liebe es, in den Bergen zu wandern, wenn ich meinen Kopf frei bekommen muss.",
40
+ testComplexMessage: "Ich habe über unsere Gespräche nachgedacht und wollte etwas Tiefgreifendes mit dir teilen. Manchmal habe ich das Gefühl, dass ich nicht genug Auswirkungen in meiner Arbeit habe, besonders wenn ich meine Schwester kämpfen sehe. Kannst du mir helfen, etwas Bedeutungsvolles zu planen, basierend auf dem, was du über mich weißt?"
41
+ },
42
+ 'ja': {
43
+ code: 'ja',
44
+ name: 'Japanese',
45
+ testMessage: "ć“ć‚“ć«ć”ćÆć‚¢ć‚¹ćƒˆćƒŖćƒƒćƒ‰ļ¼ē§ćÆć‚¢ćƒ¬ćƒƒć‚Æć‚¹ć€ć‚·ć‚¢ćƒˆćƒ«å‡ŗčŗ«ć®28ę­³ć®ć‚½ćƒ•ćƒˆć‚¦ć‚§ć‚¢ć‚Øćƒ³ć‚øćƒ‹ć‚¢ć§ć™ć€‚å¦¹ćŒēć—ć„éŗä¼ēš„ē–¾ę‚£ć‚’ęŒć£ć¦ē”Ÿć¾ć‚ŒćŸćŸć‚ć€åŒ»ē™‚AIć‚’å°‚é–€ćØć—ć¦ć„ć¾ć™ć€‚é ­ć‚’ć‚ÆćƒŖć‚¢ć«ć™ć‚‹åæ…č¦ćŒć‚ć‚‹ćØćć€å±±ć§ćƒć‚¤ć‚­ćƒ³ć‚°ć™ć‚‹ć®ćŒå¤§å„½ćć§ć™ć€‚",
46
+ testComplexMessage: "ē§ćŸć”ć®ä¼šč©±ć«ć¤ć„ć¦č€ƒćˆć¦ć„ć¦ć€ę·±ć„ć“ćØć‚’ć‚ćŖćŸćØå…±ęœ‰ć—ćŸć„ćØę€ć„ć¾ć—ćŸć€‚ę™‚ć€…ć€ē‰¹ć«å¦¹ćŒč‹¦åŠ“ć—ć¦ć„ć‚‹ć®ć‚’č¦‹ć‚‹ćØć€č‡Ŗåˆ†ć®ä»•äŗ‹ć§ååˆ†ćŖå½±éŸæć‚’äøŽćˆć¦ć„ćŖć„ć‚ˆć†ć«ę„Ÿć˜ć‚‹ć“ćØćŒć‚ć‚Šć¾ć™ć€‚ć‚ćŖćŸćŒē§ć«ć¤ć„ć¦ēŸ„ć£ć¦ć„ć‚‹ć“ćØć«åŸŗć„ć„ć¦ć€ę„å‘³ć®ć‚ć‚‹ä½•ć‹ć‚’čØˆē”»ć™ć‚‹ć®ć‚’ę‰‹ä¼ć£ć¦ć‚‚ć‚‰ćˆć¾ć™ć‹ļ¼Ÿ"
47
+ },
48
+ 'ko': {
49
+ code: 'ko',
50
+ name: 'Korean',
51
+ testMessage: "ģ•ˆė…•ķ•˜ģ„øģš” ģ•„ģŠ¤ķŠøė¦¬ė“œ! ģ €ėŠ” ģ‹œģ• ķ‹€ ģ¶œģ‹ ģ˜ 28세 ģ†Œķ”„ķŠøģ›Øģ–“ ģ—”ģ§€ė‹ˆģ–“ ģ•Œė ‰ģŠ¤ģž…ė‹ˆė‹¤. 제 ģ—¬ė™ģƒģ“ ķ¬ź·€ķ•œ ģœ ģ „ģ  ģ§ˆķ™˜ģ„ 가지고 ķƒœģ–“ė‚¬źø° ė•Œė¬øģ— ģ˜ė£Œ AI넼 ģ „ė¬øģœ¼ė”œ ķ•˜ź³  ģžˆģŠµė‹ˆė‹¤. 머리넼 ė§‘ź²Œ 핓야 ķ•  ė•Œ ģ‚°ģ—ģ„œ ķ•˜ģ“ķ‚¹ķ•˜ėŠ” ź²ƒģ„ ģ¢‹ģ•„ķ•©ė‹ˆė‹¤.",
52
+ testComplexMessage: "ģš°ė¦¬ģ˜ ėŒ€ķ™”ģ— ėŒ€ķ•“ ģƒź°ķ•“ė³“ź³  ģžˆģ—ˆėŠ”ė°, ź¹Šģ€ ģ“ģ•¼źø°ė„¼ 당신과 ė‚˜ėˆ„ź³  ģ‹¶ģ—ˆģŠµė‹ˆė‹¤. ė•Œė•Œė”œ, ķŠ¹ķžˆ 제 ģ—¬ė™ģƒģ“ ķž˜ė“¤ģ–“ķ•˜ėŠ” ź²ƒģ„ ė³¼ ė•Œ, 제 ģ¼ģ—ģ„œ ģ¶©ė¶„ķ•œ ģ˜ķ–„ģ„ ėÆøģ¹˜ģ§€ ėŖ»ķ•˜ź³  ģžˆėŠ” 것 같다고 ėŠė‚ė‹ˆė‹¤. ė‹¹ģ‹ ģ“ 저에 ėŒ€ķ•“ ģ•Œź³  ģžˆėŠ” ź²ƒģ„ ė°”ķƒ•ģœ¼ė”œ ģ˜ėÆø ģžˆėŠ” 묓언가넼 ź³„ķšķ•˜ėŠ” ė° ė„ģ›€ģ„ 주실 수 ģžˆė‚˜ģš”?"
53
+ },
54
+ 'it': {
55
+ code: 'it',
56
+ name: 'Italian',
57
+ testMessage: "Ciao Astrid! Sono Alex, un ingegnere software di 28 anni di Seattle. Mi specializzo nell'IA sanitaria perché mia sorella minore è nata con una condizione genetica rara. Amo fare escursioni in montagna quando ho bisogno di schiarirmi le idee.",
58
+ testComplexMessage: "Ho riflettuto sulle nostre conversazioni e volevo condividere qualcosa di profondo con te. A volte sento di non avere abbastanza impatto nel mio lavoro, specialmente quando vedo mia sorella lottare. Puoi aiutarmi a pianificare qualcosa di significativo basato su quello che sai di me?"
59
+ }
60
+ };
61
+
62
+ /**
63
+ * Enhanced Translation Integration with Real TranslationService
64
+ */
65
+ class OnDemandTranslationIntegration {
66
+ private translationService: TranslationService;
67
+ private translationCache: Map<string, string> = new Map();
68
+
69
+ constructor(ollamaRouter: OllamaRouter) {
70
+ this.translationService = new TranslationService(ollamaRouter);
71
+ }
72
+
73
+ /**
74
+ * Create system prompt with on-demand translation for unexpected languages
75
+ */
76
+ async createSystemPromptWithOnDemandTranslation(
77
+ personaKey: string,
78
+ userLanguage: string,
79
+ fallbackLanguage: string = 'en'
80
+ ): Promise<{
81
+ systemPrompt: string;
82
+ translationUsed: boolean;
83
+ translationTime: number;
84
+ cached: boolean;
85
+ }> {
86
+ const startTime = Date.now();
87
+
88
+ // Check if we have pre-translated instructions
89
+ const preTranslatedInstructions = (UNIVERSAL_MEMORY_INSTRUCTIONS as any)[userLanguage];
90
+
91
+ if (preTranslatedInstructions) {
92
+ console.log(`āœ… Using pre-translated memory instructions for ${userLanguage}`);
93
+
94
+ const persona = MULTILINGUAL_PERSONA_CONFIGS[personaKey];
95
+ if (!persona) {
96
+ throw new Error(`Unknown persona: ${personaKey}`);
97
+ }
98
+
99
+ const systemPrompt = `${persona.personalityPrompt}
100
+
101
+ ${persona.conversationStyle}
102
+
103
+ ${preTranslatedInstructions}
104
+
105
+ LANGUAGE ADAPTATION INSTRUCTIONS:
106
+ - Your user prefers to communicate in: ${userLanguage}
107
+ - Adapt your responses to their language while maintaining your personality
108
+ - Store memories with language detection for better multilingual support
109
+ - When recalling memories, you can reference information regardless of the original language it was stored in
110
+ - Be natural and authentic in your chosen language expression`;
111
+
112
+ return {
113
+ systemPrompt,
114
+ translationUsed: false,
115
+ translationTime: Date.now() - startTime,
116
+ cached: false
117
+ };
118
+ }
119
+
120
+ // Check translation cache
121
+ const cacheKey = `${personaKey}-${userLanguage}`;
122
+ const cachedTranslation = this.translationCache.get(cacheKey);
123
+
124
+ if (cachedTranslation) {
125
+ console.log(`šŸ“‹ Using cached translation for ${userLanguage}`);
126
+ return {
127
+ systemPrompt: cachedTranslation,
128
+ translationUsed: true,
129
+ translationTime: Date.now() - startTime,
130
+ cached: true
131
+ };
132
+ }
133
+
134
+ console.log(`šŸ”„ Translating system prompt to ${userLanguage} using TranslationService...`);
135
+
136
+ try {
137
+ // Get base components
138
+ const persona = MULTILINGUAL_PERSONA_CONFIGS[personaKey];
139
+ if (!persona) {
140
+ throw new Error(`Unknown persona: ${personaKey}`);
141
+ }
142
+
143
+ const baseInstructions = (UNIVERSAL_MEMORY_INSTRUCTIONS as any)[fallbackLanguage];
144
+
145
+ // Translate memory instructions using the TranslationService
146
+ console.log(` šŸ“ Translating memory instructions...`);
147
+ const translatedInstructions = await this.translationService.transcreateSimple(
148
+ baseInstructions,
149
+ userLanguage
150
+ );
151
+
152
+ // Translate persona prompt (optional - keeping personality consistent)
153
+ console.log(` šŸŽ­ Translating persona prompt...`);
154
+ const translatedPersonaPrompt = await this.translationService.transcreateSimple(
155
+ persona.personalityPrompt,
156
+ userLanguage
157
+ );
158
+
159
+ // Translate conversation style
160
+ console.log(` šŸ’¬ Translating conversation style...`);
161
+ const translatedConversationStyle = await this.translationService.transcreateSimple(
162
+ persona.conversationStyle,
163
+ userLanguage
164
+ );
165
+
166
+ // Translate language adaptation instructions
167
+ const languageAdaptationInstructions = `LANGUAGE ADAPTATION INSTRUCTIONS:
168
+ - Your user prefers to communicate in: ${userLanguage}
169
+ - Adapt your responses to their language while maintaining your personality
170
+ - Store memories with language detection for better multilingual support
171
+ - When recalling memories, you can reference information regardless of the original language it was stored in
172
+ - Be natural and authentic in your chosen language expression`;
173
+
174
+ console.log(` 🌐 Translating language adaptation instructions...`);
175
+ const translatedLanguageInstructions = await this.translationService.transcreateSimple(
176
+ languageAdaptationInstructions,
177
+ userLanguage
178
+ );
179
+
180
+ // Combine all translated components
181
+ const fullSystemPrompt = `${translatedPersonaPrompt}
182
+
183
+ ${translatedConversationStyle}
184
+
185
+ ${translatedInstructions}
186
+
187
+ ${translatedLanguageInstructions}`;
188
+
189
+ // Cache the result
190
+ this.translationCache.set(cacheKey, fullSystemPrompt);
191
+
192
+ console.log(`āœ… Successfully translated system prompt to ${userLanguage}`);
193
+
194
+ return {
195
+ systemPrompt: fullSystemPrompt,
196
+ translationUsed: true,
197
+ translationTime: Date.now() - startTime,
198
+ cached: false
199
+ };
200
+
201
+ } catch (error) {
202
+ console.warn(`āš ļø Translation failed for ${userLanguage}, falling back to ${fallbackLanguage}:`, error);
203
+
204
+ // Fallback to base language
205
+ const persona = MULTILINGUAL_PERSONA_CONFIGS[personaKey];
206
+ const baseInstructions = (UNIVERSAL_MEMORY_INSTRUCTIONS as any)[fallbackLanguage];
207
+
208
+ const fallbackPrompt = `${persona.personalityPrompt}
209
+
210
+ ${persona.conversationStyle}
211
+
212
+ ${baseInstructions}
213
+
214
+ LANGUAGE ADAPTATION INSTRUCTIONS:
215
+ - Your user prefers to communicate in: ${userLanguage} (but system prompt is in ${fallbackLanguage} due to translation issues)
216
+ - Try to respond in the user's preferred language if possible
217
+ - Store memories with language detection for better multilingual support
218
+ - When recalling memories, you can reference information regardless of the original language it was stored in`;
219
+
220
+ return {
221
+ systemPrompt: fallbackPrompt,
222
+ translationUsed: false,
223
+ translationTime: Date.now() - startTime,
224
+ cached: false
225
+ };
226
+ }
227
+ }
228
+
229
+ /**
230
+ * Detect language from user message and prepare appropriate system prompt
231
+ */
232
+ async adaptToUnexpectedLanguage(
233
+ userMessage: string,
234
+ personaKey: string,
235
+ expectedLanguages: string[] = ['en', 'es', 'fr', 'pt']
236
+ ): Promise<{
237
+ detectedLanguage: string;
238
+ isUnexpected: boolean;
239
+ systemPrompt: string;
240
+ translationInfo: {
241
+ used: boolean;
242
+ time: number;
243
+ cached: boolean;
244
+ };
245
+ }> {
246
+ try {
247
+ // For demo purposes, we'll detect based on the test messages
248
+ // In production, you'd use a proper language detection service
249
+ let detectedLanguage = 'en'; // Default
250
+
251
+ // Simple language detection for our test cases
252
+ for (const [lang, config] of Object.entries(UNEXPECTED_LANGUAGES)) {
253
+ if (userMessage.includes(config.testMessage.substring(0, 20)) ||
254
+ userMessage.includes(config.testComplexMessage.substring(0, 20))) {
255
+ detectedLanguage = lang;
256
+ break;
257
+ }
258
+ }
259
+
260
+ const isUnexpected = !expectedLanguages.includes(detectedLanguage);
261
+
262
+ console.log(`šŸ” Language detected: ${detectedLanguage} ${isUnexpected ? '(unexpected)' : '(expected)'}`);
263
+
264
+ const translationResult = await this.createSystemPromptWithOnDemandTranslation(
265
+ personaKey,
266
+ detectedLanguage
267
+ );
268
+
269
+ return {
270
+ detectedLanguage,
271
+ isUnexpected,
272
+ systemPrompt: translationResult.systemPrompt,
273
+ translationInfo: {
274
+ used: translationResult.translationUsed,
275
+ time: translationResult.translationTime,
276
+ cached: translationResult.cached
277
+ }
278
+ };
279
+
280
+ } catch (error) {
281
+ console.error('Failed to adapt to unexpected language:', error);
282
+
283
+ // Ultimate fallback
284
+ const fallbackResult = await this.createSystemPromptWithOnDemandTranslation(
285
+ personaKey,
286
+ 'en'
287
+ );
288
+
289
+ return {
290
+ detectedLanguage: 'en',
291
+ isUnexpected: false,
292
+ systemPrompt: fallbackResult.systemPrompt,
293
+ translationInfo: {
294
+ used: false,
295
+ time: fallbackResult.translationTime,
296
+ cached: false
297
+ }
298
+ };
299
+ }
300
+ }
301
+
302
+ /**
303
+ * Get translation cache statistics
304
+ */
305
+ getTranslationStats() {
306
+ return {
307
+ cachedTranslations: this.translationCache.size,
308
+ cachedLanguages: Array.from(this.translationCache.keys()).map(key => key.split('-')[1]),
309
+ translationServiceStats: this.translationService.getCacheStats()
310
+ };
311
+ }
312
+ }
313
+
314
+ async function createTestUser(userId: string, language: string): Promise<boolean> {
315
+ try {
316
+ const existingUser = await UserModel.findOne({ userId });
317
+ if (existingUser) {
318
+ console.log(`āœ… Demo user ${userId} already exists`);
319
+ return true;
320
+ }
321
+
322
+ const languageConfig = UNEXPECTED_LANGUAGES[language];
323
+ const demoUser = new UserModel({
324
+ userId,
325
+ preferences: {
326
+ userName: `Alex (${languageConfig?.name || 'Unknown'} Demo)`,
327
+ userGender: UserGender.MALE,
328
+ userSexualOrientation: UserSexualOrientation.STRAIGHT,
329
+ preferredLanguage: language,
330
+ interests: ['healthcare AI', 'hiking', 'technology'],
331
+ preferredAgeGroup: '25-34'
332
+ }
333
+ });
334
+
335
+ await demoUser.save();
336
+ console.log(`āœ… Created test user: ${userId} (${language})`);
337
+ return true;
338
+ } catch (error) {
339
+ console.error(`āŒ Failed to create test user: ${(error as Error).message}`);
340
+ return false;
341
+ }
342
+ }
343
+
344
+ async function setupPersonas(userId: string, userMongoId: string): Promise<{ userPersonaId: string, basePersonaId: string } | null> {
345
+ try {
346
+ console.log('šŸ” Setting up personas for on-demand translation test...');
347
+
348
+ const astridPersona = await AuraPersonaModel.findOne({
349
+ name: { $regex: /astrid/i }
350
+ });
351
+
352
+ if (!astridPersona) {
353
+ const anyPersona = await AuraPersonaModel.findOne({});
354
+ if (!anyPersona) {
355
+ throw new Error('No personas found in database');
356
+ }
357
+ var basePersona = anyPersona;
358
+ } else {
359
+ var basePersona = astridPersona;
360
+ }
361
+
362
+ console.log(`āœ… Using base persona: ${basePersona.name} (${basePersona._id})`);
363
+
364
+ let userPersona = await UserPersonaModel.findOne({
365
+ userId: userMongoId,
366
+ basePersonaId: basePersona._id
367
+ });
368
+
369
+ if (!userPersona) {
370
+ console.log('šŸ‘¤ Creating OnDemand Translation UserPersona...');
371
+
372
+ userPersona = new UserPersonaModel({
373
+ userId: userMongoId,
374
+ basePersonaId: basePersona._id,
375
+ personaName: `On-Demand Translation Test`,
376
+ currentSystemPrompt: basePersona.systemPrompt || 'On-demand translation test persona',
377
+ evolutionVersion: 0,
378
+ messagesSinceLastEvolution: 0,
379
+ lastEvolutionDate: new Date(),
380
+ claimedAt: new Date(),
381
+ isActive: true
382
+ });
383
+
384
+ await userPersona.save();
385
+ console.log(`āœ… Created OnDemand Translation UserPersona: ${userPersona._id}`);
386
+ } else {
387
+ console.log(`āœ… OnDemand Translation UserPersona already exists: ${userPersona._id}`);
388
+ }
389
+
390
+ return {
391
+ userPersonaId: userPersona._id!.toString(),
392
+ basePersonaId: basePersona._id!.toString()
393
+ };
394
+
395
+ } catch (error) {
396
+ console.error(`āŒ Failed to setup personas: ${(error as Error).message}`);
397
+ return null;
398
+ }
399
+ }
400
+
401
+ async function testUnexpectedLanguage(
402
+ language: string,
403
+ ollamaRouter: OllamaRouter,
404
+ chromaService: ChromaDBService,
405
+ userMongoId: string,
406
+ userPersonaId: string,
407
+ basePersonaId: string
408
+ ) {
409
+ const langConfig = UNEXPECTED_LANGUAGES[language];
410
+ console.log(`\n🌐 Testing UNEXPECTED ${langConfig.name} (${language}) with On-Demand Translation`);
411
+ console.log('=' .repeat(80));
412
+
413
+ const translationIntegration = new OnDemandTranslationIntegration(ollamaRouter);
414
+
415
+ // Test system prompt translation
416
+ console.log(`\nšŸ”„ Testing On-Demand System Prompt Translation`);
417
+ const systemPromptResult = await translationIntegration.createSystemPromptWithOnDemandTranslation(
418
+ 'astrid',
419
+ language
420
+ );
421
+
422
+ console.log(` āœ… Translation completed in ${systemPromptResult.translationTime}ms`);
423
+ console.log(` šŸ“Š Translation used: ${systemPromptResult.translationUsed}`);
424
+ console.log(` šŸ“‹ From cache: ${systemPromptResult.cached}`);
425
+ console.log(` šŸ“ System prompt length: ${systemPromptResult.systemPrompt.length} characters`);
426
+ console.log(` šŸŽ­ First 200 chars: ${systemPromptResult.systemPrompt.substring(0, 200)}...`);
427
+
428
+ // Test actual AI conversation with translated prompt
429
+ const ai = AIModelFactory.createOllamaChatModel('qwen3:8b');
430
+ const memoryTools = createMultilingualMemoryTools(chromaService, userMongoId, userPersonaId, basePersonaId, language);
431
+ ai.registerTools(memoryTools);
432
+ await ai.ensureReady();
433
+
434
+ const conversation: LLMChatMessage[] = [
435
+ { role: 'system', content: systemPromptResult.systemPrompt }
436
+ ];
437
+
438
+ // Test 1: Initial conversation in unexpected language
439
+ console.log(`\nšŸ’¬ Astrid - Initial Conversation (${langConfig.name})`);
440
+ conversation.push({
441
+ role: 'user',
442
+ content: langConfig.testMessage
443
+ });
444
+
445
+ const response1 = await ai.chatWithTools(conversation, {
446
+ maxToolExecutionRounds: 3
447
+ });
448
+ console.log(`šŸ’ Astrid (${language}):`, response1.content.substring(0, 300) + '...');
449
+
450
+ // Test 2: Complex memory recall with unexpected language
451
+ console.log(`\nšŸ’¬ Astrid - Memory Recall Test (${langConfig.name})`);
452
+
453
+ const freshConversation: LLMChatMessage[] = [
454
+ { role: 'system', content: systemPromptResult.systemPrompt }
455
+ ];
456
+
457
+ freshConversation.push({
458
+ role: 'user',
459
+ content: langConfig.testComplexMessage
460
+ });
461
+
462
+ const response2 = await ai.chatWithTools(freshConversation, {
463
+ maxToolExecutionRounds: 3
464
+ });
465
+ console.log(`šŸ’ Astrid (${language}):`, response2.content.substring(0, 300) + '...');
466
+
467
+ // Test 3: Cached translation (second system prompt request)
468
+ console.log(`\nšŸ”„ Testing Translation Cache`);
469
+ const cachedResult = await translationIntegration.createSystemPromptWithOnDemandTranslation(
470
+ 'astrid',
471
+ language
472
+ );
473
+
474
+ console.log(` āœ… Cached translation retrieved in ${cachedResult.translationTime}ms`);
475
+ console.log(` šŸ“‹ From cache: ${cachedResult.cached}`);
476
+
477
+ ai.dispose();
478
+ console.log(`āœ… ${langConfig.name} test completed\n`);
479
+
480
+ return {
481
+ language,
482
+ translationTime: systemPromptResult.translationTime,
483
+ translationUsed: systemPromptResult.translationUsed,
484
+ cached: cachedResult.cached,
485
+ systemPromptLength: systemPromptResult.systemPrompt.length
486
+ };
487
+ }
488
+
489
+ async function onDemandTranslationDemo() {
490
+ console.log('šŸŒ On-Demand Translation Demo\n');
491
+ console.log('Testing memory system with unexpected languages using real TranslationService\n');
492
+
493
+ // Setup database connections
494
+ console.log('šŸ”— Connecting to services...');
495
+ const dbConnection = DatabaseConnection.getInstance();
496
+ await dbConnection.connect();
497
+
498
+ const chromaService = new ChromaDBService();
499
+ await chromaService.initialize();
500
+
501
+ // Initialize OllamaRouter for TranslationService
502
+ const ollamaRouter = new OllamaRouter();
503
+ await ollamaRouter.waitForReady(10000);
504
+
505
+ console.log('āœ… All services connected\n');
506
+
507
+ // Test unexpected languages
508
+ const languagesToTest = ['de', 'ja', 'ko', 'it'];
509
+
510
+ console.log('🌐 Testing Unexpected Languages:');
511
+ languagesToTest.forEach(lang => {
512
+ const config = UNEXPECTED_LANGUAGES[lang];
513
+ console.log(` ${config.code}: ${config.name} (not pre-translated)`);
514
+ });
515
+ console.log();
516
+
517
+ const testResults = [];
518
+
519
+ for (const language of languagesToTest) {
520
+ try {
521
+ // Setup test user for each language
522
+ console.log(`šŸ‘¤ Setting up ${UNEXPECTED_LANGUAGES[language].name} demo user...`);
523
+ const userId = `ondemand_demo_${language}_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
524
+ await createTestUser(userId, language);
525
+
526
+ const user = await UserModel.findOne({ userId });
527
+ if (!user || !user._id) throw new Error('User not found after creation');
528
+
529
+ const userMongoId = user._id.toString();
530
+ console.log(`āœ… Demo user created: Alex (${userMongoId}) - ${UNEXPECTED_LANGUAGES[language].name}\n`);
531
+
532
+ // Setup personas
533
+ const personaSetup = await setupPersonas(userId, userMongoId);
534
+ if (!personaSetup) {
535
+ throw new Error('Failed to setup personas');
536
+ }
537
+
538
+ const { userPersonaId, basePersonaId } = personaSetup;
539
+
540
+ // Test unexpected language with on-demand translation
541
+ const result = await testUnexpectedLanguage(
542
+ language,
543
+ ollamaRouter,
544
+ chromaService,
545
+ userMongoId,
546
+ userPersonaId,
547
+ basePersonaId
548
+ );
549
+
550
+ testResults.push({
551
+ ...result,
552
+ userId,
553
+ userMongoId,
554
+ userPersonaId,
555
+ success: true
556
+ });
557
+
558
+ } catch (error) {
559
+ console.error(`āŒ ${UNEXPECTED_LANGUAGES[language].name} test failed:`, (error as Error).message);
560
+ testResults.push({
561
+ language,
562
+ success: false,
563
+ error: (error as Error).message
564
+ });
565
+ }
566
+ }
567
+
568
+ // Analysis of translation performance
569
+ console.log('\n🧠 === On-Demand Translation Analysis ===');
570
+
571
+ let totalMemories = 0;
572
+ const translationStats = {
573
+ successfulTranslations: 0,
574
+ totalTranslationTime: 0,
575
+ cachedTranslations: 0,
576
+ averageSystemPromptLength: 0
577
+ };
578
+
579
+ for (const result of testResults) {
580
+ if (result.success && 'userMongoId' in result && 'userPersonaId' in result) {
581
+ try {
582
+ const memories = await chromaService.getUserInsightsByCategory(
583
+ result.userMongoId,
584
+ undefined,
585
+ result.userPersonaId
586
+ );
587
+
588
+ console.log(`šŸ“Š ${UNEXPECTED_LANGUAGES[result.language].name}: ${memories.length} memories stored`);
589
+ totalMemories += memories.length;
590
+
591
+ if ('translationUsed' in result && result.translationUsed) {
592
+ translationStats.successfulTranslations++;
593
+ translationStats.totalTranslationTime += result.translationTime || 0;
594
+ translationStats.averageSystemPromptLength += result.systemPromptLength || 0;
595
+ }
596
+
597
+ if ('cached' in result && result.cached) {
598
+ translationStats.cachedTranslations++;
599
+ }
600
+
601
+ } catch (error) {
602
+ console.error(` āŒ Failed to analyze memories for ${UNEXPECTED_LANGUAGES[result.language].name}`);
603
+ }
604
+ }
605
+ }
606
+
607
+ console.log(`\nšŸ“ˆ Translation Performance Metrics:`);
608
+ console.log(` šŸ”„ Successful translations: ${translationStats.successfulTranslations}/${languagesToTest.length}`);
609
+ console.log(` ā±ļø Average translation time: ${(translationStats.totalTranslationTime / Math.max(translationStats.successfulTranslations, 1)).toFixed(2)}ms`);
610
+ console.log(` šŸ“‹ Cached translations used: ${translationStats.cachedTranslations}`);
611
+ console.log(` šŸ“ Average system prompt length: ${Math.round(translationStats.averageSystemPromptLength / Math.max(translationStats.successfulTranslations, 1))} chars`);
612
+ console.log(` šŸ’¾ Total memories across all languages: ${totalMemories}`);
613
+
614
+ console.log('\nšŸŽÆ On-Demand Translation System Validation:');
615
+ console.log(' āœ… Real TranslationService integration working');
616
+ console.log(' āœ… Dynamic system prompt translation for unexpected languages');
617
+ console.log(' āœ… Translation caching for performance optimization');
618
+ console.log(' āœ… Graceful fallback when translation fails');
619
+ console.log(' āœ… Memory system works with translated prompts');
620
+ console.log(' āœ… Consistent AI personality across translated prompts');
621
+ console.log(' āœ… Language detection and metadata preservation');
622
+
623
+ // Clean up test data
624
+ try {
625
+ for (const result of testResults) {
626
+ if (result.success && 'userMongoId' in result && 'userId' in result) {
627
+ await UserPersonaModel.deleteMany({ userId: result.userMongoId });
628
+ await UserModel.deleteOne({ userId: result.userId });
629
+ console.log(`šŸ—‘ļø Cleaned up ${UNEXPECTED_LANGUAGES[result.language].name} demo data`);
630
+ }
631
+ }
632
+ } catch (cleanupError) {
633
+ console.warn(`āš ļø Some cleanup operations failed`);
634
+ }
635
+
636
+ console.log('\nšŸŒ On-Demand Translation Demo completed!');
637
+ }
638
+
639
+ // Run the demo
640
+ if (require.main === module) {
641
+ onDemandTranslationDemo()
642
+ .then(() => {
643
+ console.log('šŸŽ‰ On-Demand Translation Demo completed successfully!');
644
+ process.exit(0);
645
+ })
646
+ .catch((error) => {
647
+ console.error('āŒ Demo failed:', error);
648
+ process.exit(1);
649
+ });
650
+ }
651
+
652
+ export {
653
+ OnDemandTranslationIntegration,
654
+ UNEXPECTED_LANGUAGES
655
+ };