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.
- package/dist/ai-model.d.ts +20 -22
- package/dist/ai-model.d.ts.map +1 -1
- package/dist/ai-model.js +26 -23
- package/dist/ai-model.js.map +1 -1
- package/dist/client.d.ts +5 -5
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +17 -9
- package/dist/client.js.map +1 -1
- package/dist/http.d.ts +2 -0
- package/dist/http.d.ts.map +1 -1
- package/dist/http.js +1 -0
- package/dist/http.js.map +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -4
- package/dist/index.js.map +1 -1
- package/dist/interfaces.d.ts +49 -11
- package/dist/interfaces.d.ts.map +1 -1
- package/dist/interfaces.js +14 -0
- package/dist/interfaces.js.map +1 -1
- package/dist/providers/anthropic.d.ts +56 -0
- package/dist/providers/anthropic.d.ts.map +1 -0
- package/dist/providers/anthropic.js +524 -0
- package/dist/providers/anthropic.js.map +1 -0
- package/dist/providers/google.d.ts +5 -0
- package/dist/providers/google.d.ts.map +1 -1
- package/dist/providers/google.js +64 -8
- package/dist/providers/google.js.map +1 -1
- package/dist/providers/index.d.ts +1 -0
- package/dist/providers/index.d.ts.map +1 -1
- package/dist/providers/index.js +1 -0
- package/dist/providers/index.js.map +1 -1
- package/dist/providers/ollama.d.ts.map +1 -1
- package/dist/providers/ollama.js +38 -11
- package/dist/providers/ollama.js.map +1 -1
- package/dist/providers/openai.d.ts.map +1 -1
- package/dist/providers/openai.js +9 -7
- package/dist/providers/openai.js.map +1 -1
- package/dist/router.d.ts +13 -33
- package/dist/router.d.ts.map +1 -1
- package/dist/router.js +33 -57
- package/dist/router.js.map +1 -1
- package/dist/stream-decoder.d.ts +29 -2
- package/dist/stream-decoder.d.ts.map +1 -1
- package/dist/stream-decoder.js +39 -11
- package/dist/stream-decoder.js.map +1 -1
- package/dist/structured-output.d.ts +107 -181
- package/dist/structured-output.d.ts.map +1 -1
- package/dist/structured-output.js +137 -192
- package/dist/structured-output.js.map +1 -1
- package/dist/zod-adapter.d.ts +44 -0
- package/dist/zod-adapter.d.ts.map +1 -0
- package/dist/zod-adapter.js +61 -0
- package/dist/zod-adapter.js.map +1 -0
- package/package.json +9 -1
- package/src/ai-model.ts +350 -0
- package/src/auditor.ts +213 -0
- package/src/client.ts +402 -0
- package/src/debug/debug-google-streaming.ts +97 -0
- package/src/debug/debug-tool-execution.ts +86 -0
- package/src/debug/test-lmstudio-tools.ts +155 -0
- package/src/demos/README.md +47 -0
- package/src/demos/basic/universal-llm-examples.ts +161 -0
- package/src/demos/mcp/astrid-memory-demo.ts +295 -0
- package/src/demos/mcp/astrid-persona-memory.ts +357 -0
- package/src/demos/mcp/mcp-mongodb-demo.ts +275 -0
- package/src/demos/mcp/simple-astrid-memory.ts +148 -0
- package/src/demos/mcp/simple-mcp-demo.ts +68 -0
- package/src/demos/mcp/working-mcp-demo.ts +62 -0
- package/src/demos/model-alias-demo.ts +0 -0
- package/src/demos/tools/RAG_MEMORY_INTEGRATION.md +267 -0
- package/src/demos/tools/astrid-memory-demo.ts +270 -0
- package/src/demos/tools/astrid-production-memory-clean.ts +785 -0
- package/src/demos/tools/astrid-production-memory.ts +558 -0
- package/src/demos/tools/basic-translation-test.ts +66 -0
- package/src/demos/tools/chromadb-similarity-tuning.ts +390 -0
- package/src/demos/tools/clean-multilingual-conversation.ts +209 -0
- package/src/demos/tools/clean-translation-test.ts +119 -0
- package/src/demos/tools/clean-universal-multilingual-test.ts +131 -0
- package/src/demos/tools/complete-rag-demo.ts +369 -0
- package/src/demos/tools/complete-tool-demo.ts +132 -0
- package/src/demos/tools/demo-tool-calling.ts +124 -0
- package/src/demos/tools/dynamic-language-switching-test.ts +251 -0
- package/src/demos/tools/hybrid-thinking-test.ts +154 -0
- package/src/demos/tools/memory-integration-test.ts +420 -0
- package/src/demos/tools/multilingual-memory-system.ts +802 -0
- package/src/demos/tools/ondemand-translation-demo.ts +655 -0
- package/src/demos/tools/production-tool-demo.ts +245 -0
- package/src/demos/tools/revolutionary-multilingual-test.ts +151 -0
- package/src/demos/tools/rigorous-language-analysis.ts +218 -0
- package/src/demos/tools/test-universal-memory-system.ts +126 -0
- package/src/demos/tools/translation-integration-guide.ts +346 -0
- package/src/demos/tools/universal-memory-system.ts +560 -0
- package/src/http.ts +247 -0
- package/src/index.ts +161 -0
- package/src/interfaces.ts +657 -0
- package/src/mcp.ts +345 -0
- package/src/providers/anthropic.ts +762 -0
- package/src/providers/google.ts +620 -0
- package/src/providers/index.ts +8 -0
- package/src/providers/ollama.ts +469 -0
- package/src/providers/openai.ts +392 -0
- package/src/router.ts +780 -0
- package/src/stream-decoder.ts +361 -0
- package/src/structured-output.ts +759 -0
- package/src/test-scripts/test-advanced-tools.ts +310 -0
- package/src/test-scripts/test-google-streaming-enhanced.ts +147 -0
- package/src/test-scripts/test-google-streaming.ts +63 -0
- package/src/test-scripts/test-google-system-prompt-comprehensive.ts +189 -0
- package/src/test-scripts/test-mcp-config.ts +28 -0
- package/src/test-scripts/test-mcp-connection.ts +29 -0
- package/src/test-scripts/test-system-message-positions.ts +163 -0
- package/src/test-scripts/test-system-prompt-improvement-demo.ts +83 -0
- package/src/test-scripts/test-tool-calling.ts +231 -0
- package/src/tests/ai-model.test.ts +1614 -0
- package/src/tests/auditor.test.ts +224 -0
- package/src/tests/http.test.ts +200 -0
- package/src/tests/interfaces.test.ts +117 -0
- package/src/tests/providers/google.test.ts +660 -0
- package/src/tests/providers/ollama.test.ts +954 -0
- package/src/tests/providers/openai.test.ts +1122 -0
- package/src/tests/router.test.ts +254 -0
- package/src/tests/stream-decoder.test.ts +179 -0
- package/src/tests/structured-output.test.ts +1450 -0
- package/src/tests/tools.test.ts +175 -0
- package/src/tools.ts +246 -0
- 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
|
+
};
|