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,390 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ChromaDB Similarity Tuning Test
|
|
3
|
+
*
|
|
4
|
+
* Focused testing to optimize embedding similarity thresholds and search parameters
|
|
5
|
+
* for the RAG memory system before production deployment.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import '@dotenvx/dotenvx/config';
|
|
9
|
+
|
|
10
|
+
import { ChromaDBService } from '../../../../../src/services/ChromaDBService.js';
|
|
11
|
+
import { DatabaseConnection } from '../../../../../src/database/connection.js';
|
|
12
|
+
import { UserModel, UserGender, UserSexualOrientation } from '../../../../../src/models/User.js';
|
|
13
|
+
import { AuraPersonaModel } from '../../../../../src/models/AuraPersona.js';
|
|
14
|
+
import { UserPersonaModel } from '../../../../../src/models/UserPersona.js';
|
|
15
|
+
|
|
16
|
+
interface SimilarityTestResult {
|
|
17
|
+
query: string;
|
|
18
|
+
threshold: number;
|
|
19
|
+
foundMemories: number;
|
|
20
|
+
avgSimilarity: number;
|
|
21
|
+
topSimilarity: number;
|
|
22
|
+
expectedMemories: number;
|
|
23
|
+
success: boolean;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
interface TestMemory {
|
|
27
|
+
category: string;
|
|
28
|
+
content: string;
|
|
29
|
+
importance: 'low' | 'medium' | 'high';
|
|
30
|
+
emotionalContext: string;
|
|
31
|
+
relationshipStage: string;
|
|
32
|
+
expectedQueries: string[];
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Test dataset with known content and expected search queries
|
|
36
|
+
const TEST_MEMORIES: TestMemory[] = [
|
|
37
|
+
{
|
|
38
|
+
category: 'family',
|
|
39
|
+
content: 'Alex has a younger sister with a rare genetic condition that requires ongoing medical care',
|
|
40
|
+
importance: 'high',
|
|
41
|
+
emotionalContext: 'concerned',
|
|
42
|
+
relationshipStage: 'getting_to_know',
|
|
43
|
+
expectedQueries: ['sister genetic condition', 'family medical care', 'rare disease', 'sister health']
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
category: 'work',
|
|
47
|
+
content: 'Software engineer specializing in healthcare AI and machine learning for medical diagnostics',
|
|
48
|
+
importance: 'high',
|
|
49
|
+
emotionalContext: 'passionate',
|
|
50
|
+
relationshipStage: 'getting_to_know',
|
|
51
|
+
expectedQueries: ['healthcare AI', 'software engineer', 'medical diagnostics', 'machine learning']
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
category: 'preferences',
|
|
55
|
+
content: 'Enjoys hiking in mountain trails for stress relief and mental clarity',
|
|
56
|
+
importance: 'medium',
|
|
57
|
+
emotionalContext: 'peaceful',
|
|
58
|
+
relationshipStage: 'getting_to_know',
|
|
59
|
+
expectedQueries: ['hiking mountains', 'outdoor activities', 'stress relief', 'mountain trails']
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
category: 'values',
|
|
63
|
+
content: 'Vegetarian lifestyle driven by environmental concerns and sustainability values',
|
|
64
|
+
importance: 'high',
|
|
65
|
+
emotionalContext: 'principled',
|
|
66
|
+
relationshipStage: 'building_trust',
|
|
67
|
+
expectedQueries: ['vegetarian environmental', 'sustainability values', 'plant-based diet', 'environmental impact']
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
category: 'fears',
|
|
71
|
+
content: 'Worries about not making sufficient impact in healthcare technology to help his sister',
|
|
72
|
+
importance: 'high',
|
|
73
|
+
emotionalContext: 'vulnerable',
|
|
74
|
+
relationshipStage: 'building_trust',
|
|
75
|
+
expectedQueries: ['impact healthcare', 'helping sister', 'work fears', 'making difference']
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
category: 'goals',
|
|
79
|
+
content: 'Wants to develop AI systems that can prevent medical misdiagnoses like his sister experienced',
|
|
80
|
+
importance: 'high',
|
|
81
|
+
emotionalContext: 'determined',
|
|
82
|
+
relationshipStage: 'deepening_bond',
|
|
83
|
+
expectedQueries: ['prevent misdiagnosis', 'AI medical systems', 'healthcare goals', 'sister experience']
|
|
84
|
+
}
|
|
85
|
+
];
|
|
86
|
+
|
|
87
|
+
// Different similarity thresholds to test
|
|
88
|
+
const SIMILARITY_THRESHOLDS = [0.0, 0.05, 0.1, 0.15, 0.2, 0.25, 0.3, 0.4, 0.5];
|
|
89
|
+
|
|
90
|
+
async function createTestUser(): Promise<{ userId: string, userMongoId: string, userPersonaId: string, basePersonaId: string }> {
|
|
91
|
+
const userId = `similarity_test_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
92
|
+
|
|
93
|
+
// Create test user
|
|
94
|
+
const demoUser = new UserModel({
|
|
95
|
+
userId,
|
|
96
|
+
preferences: {
|
|
97
|
+
userName: 'Alex (Similarity Test)',
|
|
98
|
+
userGender: UserGender.MALE,
|
|
99
|
+
userSexualOrientation: UserSexualOrientation.STRAIGHT,
|
|
100
|
+
preferredLanguage: 'en',
|
|
101
|
+
interests: ['hiking', 'healthcare AI', 'software engineering'],
|
|
102
|
+
preferredAgeGroup: '25-34'
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
await demoUser.save();
|
|
107
|
+
const userMongoId = demoUser._id!.toString();
|
|
108
|
+
|
|
109
|
+
// Setup personas
|
|
110
|
+
const astridPersona = await AuraPersonaModel.findOne({ name: { $regex: /astrid/i } });
|
|
111
|
+
if (!astridPersona) {
|
|
112
|
+
throw new Error('Astrid persona not found');
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const userPersona = new UserPersonaModel({
|
|
116
|
+
userId: userMongoId,
|
|
117
|
+
basePersonaId: astridPersona._id,
|
|
118
|
+
personaName: `${astridPersona.name} (Similarity Test)`,
|
|
119
|
+
currentSystemPrompt: astridPersona.systemPrompt || 'You are a helpful AI assistant.',
|
|
120
|
+
evolutionVersion: 0,
|
|
121
|
+
messagesSinceLastEvolution: 0,
|
|
122
|
+
lastEvolutionDate: new Date(),
|
|
123
|
+
claimedAt: new Date(),
|
|
124
|
+
isActive: true
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
await userPersona.save();
|
|
128
|
+
|
|
129
|
+
return {
|
|
130
|
+
userId,
|
|
131
|
+
userMongoId,
|
|
132
|
+
userPersonaId: userPersona._id!.toString(),
|
|
133
|
+
basePersonaId: astridPersona._id!.toString()
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
async function storeTestMemories(chromaService: ChromaDBService, userMongoId: string, userPersonaId: string, basePersonaId: string): Promise<void> {
|
|
138
|
+
console.log('📝 Storing test memories...');
|
|
139
|
+
|
|
140
|
+
for (const memory of TEST_MEMORIES) {
|
|
141
|
+
const conversationId = `similarity_test_${Date.now()}`;
|
|
142
|
+
const confidence = memory.importance === 'high' ? 0.9 : memory.importance === 'medium' ? 0.7 : 0.5;
|
|
143
|
+
|
|
144
|
+
try {
|
|
145
|
+
await chromaService.addInsight(
|
|
146
|
+
userMongoId,
|
|
147
|
+
memory.content,
|
|
148
|
+
memory.category,
|
|
149
|
+
conversationId,
|
|
150
|
+
confidence,
|
|
151
|
+
userPersonaId,
|
|
152
|
+
basePersonaId,
|
|
153
|
+
'user_insight',
|
|
154
|
+
undefined,
|
|
155
|
+
memory.emotionalContext,
|
|
156
|
+
memory.relationshipStage
|
|
157
|
+
);
|
|
158
|
+
console.log(`✅ Stored [${memory.category.toUpperCase()}]: ${memory.content.substring(0, 60)}...`);
|
|
159
|
+
} catch (error) {
|
|
160
|
+
console.error(`❌ Failed to store memory: ${(error as Error).message}`);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
console.log(`\n📊 Total memories stored: ${TEST_MEMORIES.length}\n`);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
async function testSimilarityThreshold(
|
|
167
|
+
chromaService: ChromaDBService,
|
|
168
|
+
userMongoId: string,
|
|
169
|
+
threshold: number
|
|
170
|
+
): Promise<SimilarityTestResult[]> {
|
|
171
|
+
console.log(`\n🔍 Testing similarity threshold: ${threshold}`);
|
|
172
|
+
const results: SimilarityTestResult[] = [];
|
|
173
|
+
|
|
174
|
+
// Test all queries from all memories
|
|
175
|
+
for (const memory of TEST_MEMORIES) {
|
|
176
|
+
for (const query of memory.expectedQueries) {
|
|
177
|
+
try {
|
|
178
|
+
const searchResult = await chromaService.searchSimilarInsights(
|
|
179
|
+
userMongoId,
|
|
180
|
+
query,
|
|
181
|
+
5,
|
|
182
|
+
threshold
|
|
183
|
+
);
|
|
184
|
+
|
|
185
|
+
const result: SimilarityTestResult = {
|
|
186
|
+
query,
|
|
187
|
+
threshold,
|
|
188
|
+
foundMemories: searchResult.insights.length,
|
|
189
|
+
avgSimilarity: searchResult.stats?.averageSimilarity || 0,
|
|
190
|
+
topSimilarity: searchResult.stats?.topSimilarity || 0,
|
|
191
|
+
expectedMemories: 1, // Each query should find at least its target memory
|
|
192
|
+
success: searchResult.insights.length > 0
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
results.push(result);
|
|
196
|
+
|
|
197
|
+
// Log detailed results for analysis
|
|
198
|
+
if (result.success) {
|
|
199
|
+
console.log(`✅ "${query}" → ${result.foundMemories} memories (top: ${result.topSimilarity.toFixed(3)})`);
|
|
200
|
+
} else {
|
|
201
|
+
console.log(`❌ "${query}" → No memories found (top: ${result.topSimilarity.toFixed(3)})`);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
} catch (error) {
|
|
205
|
+
console.error(`❌ Search failed for "${query}": ${(error as Error).message}`);
|
|
206
|
+
results.push({
|
|
207
|
+
query,
|
|
208
|
+
threshold,
|
|
209
|
+
foundMemories: 0,
|
|
210
|
+
avgSimilarity: 0,
|
|
211
|
+
topSimilarity: 0,
|
|
212
|
+
expectedMemories: 1,
|
|
213
|
+
success: false
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
return results;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
async function analyzeSimilarityResults(allResults: SimilarityTestResult[]): Promise<void> {
|
|
223
|
+
console.log('\n📊 === SIMILARITY THRESHOLD ANALYSIS ===\n');
|
|
224
|
+
|
|
225
|
+
// Group results by threshold
|
|
226
|
+
const resultsByThreshold = new Map<number, SimilarityTestResult[]>();
|
|
227
|
+
for (const result of allResults) {
|
|
228
|
+
if (!resultsByThreshold.has(result.threshold)) {
|
|
229
|
+
resultsByThreshold.set(result.threshold, []);
|
|
230
|
+
}
|
|
231
|
+
resultsByThreshold.get(result.threshold)!.push(result);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// Analyze each threshold
|
|
235
|
+
const thresholdAnalysis = [];
|
|
236
|
+
for (const [threshold, results] of resultsByThreshold) {
|
|
237
|
+
const totalQueries = results.length;
|
|
238
|
+
const successfulQueries = results.filter(r => r.success).length;
|
|
239
|
+
const successRate = (successfulQueries / totalQueries) * 100;
|
|
240
|
+
const avgTopSimilarity = results.reduce((sum, r) => sum + r.topSimilarity, 0) / totalQueries;
|
|
241
|
+
const totalMemoriesFound = results.reduce((sum, r) => sum + r.foundMemories, 0);
|
|
242
|
+
|
|
243
|
+
thresholdAnalysis.push({
|
|
244
|
+
threshold,
|
|
245
|
+
successRate,
|
|
246
|
+
successfulQueries,
|
|
247
|
+
totalQueries,
|
|
248
|
+
avgTopSimilarity,
|
|
249
|
+
totalMemoriesFound
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
console.log(`🎯 Threshold ${threshold.toFixed(2)}:`);
|
|
253
|
+
console.log(` Success Rate: ${successRate.toFixed(1)}% (${successfulQueries}/${totalQueries} queries)`);
|
|
254
|
+
console.log(` Avg Top Similarity: ${avgTopSimilarity.toFixed(3)}`);
|
|
255
|
+
console.log(` Total Memories Found: ${totalMemoriesFound}`);
|
|
256
|
+
console.log('');
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// Find optimal threshold
|
|
260
|
+
const optimal = thresholdAnalysis
|
|
261
|
+
.filter(a => a.successRate > 0)
|
|
262
|
+
.sort((a, b) => {
|
|
263
|
+
// Prioritize higher success rate, then higher avg similarity
|
|
264
|
+
if (Math.abs(a.successRate - b.successRate) < 5) {
|
|
265
|
+
return b.avgTopSimilarity - a.avgTopSimilarity;
|
|
266
|
+
}
|
|
267
|
+
return b.successRate - a.successRate;
|
|
268
|
+
})[0];
|
|
269
|
+
|
|
270
|
+
if (optimal) {
|
|
271
|
+
console.log('🏆 === OPTIMAL THRESHOLD RECOMMENDATION ===');
|
|
272
|
+
console.log(`📈 Best Threshold: ${optimal.threshold}`);
|
|
273
|
+
console.log(`✅ Success Rate: ${optimal.successRate.toFixed(1)}%`);
|
|
274
|
+
console.log(`🎯 Avg Similarity: ${optimal.avgTopSimilarity.toFixed(3)}`);
|
|
275
|
+
console.log(`📊 Queries Found: ${optimal.successfulQueries}/${optimal.totalQueries}`);
|
|
276
|
+
console.log('\n💡 Recommendation: Use this threshold in production ChromaDBService');
|
|
277
|
+
} else {
|
|
278
|
+
console.log('⚠️ No threshold achieved successful results - check embedding model');
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
async function testSpecificQueries(chromaService: ChromaDBService, userMongoId: string): Promise<void> {
|
|
283
|
+
console.log('\n🔬 === DETAILED QUERY ANALYSIS ===\n');
|
|
284
|
+
|
|
285
|
+
const testQueries = [
|
|
286
|
+
'sister genetic condition',
|
|
287
|
+
'healthcare AI work',
|
|
288
|
+
'mountain hiking stress relief',
|
|
289
|
+
'vegetarian environmental values',
|
|
290
|
+
'medical diagnosis fears'
|
|
291
|
+
];
|
|
292
|
+
|
|
293
|
+
for (const query of testQueries) {
|
|
294
|
+
console.log(`🔍 Analyzing query: "${query}"`);
|
|
295
|
+
|
|
296
|
+
try {
|
|
297
|
+
const result = await chromaService.searchSimilarInsights(
|
|
298
|
+
userMongoId,
|
|
299
|
+
query,
|
|
300
|
+
3,
|
|
301
|
+
0.0 // Use lowest threshold to see all similarities
|
|
302
|
+
);
|
|
303
|
+
|
|
304
|
+
console.log(` 📊 Total candidates: ${result.stats?.totalCandidates || 0}`);
|
|
305
|
+
console.log(` 📈 Avg similarity: ${result.stats?.averageSimilarity?.toFixed(3) || 'N/A'}`);
|
|
306
|
+
console.log(` 🎯 Top similarity: ${result.stats?.topSimilarity?.toFixed(3) || 'N/A'}`);
|
|
307
|
+
|
|
308
|
+
if (result.insights.length > 0) {
|
|
309
|
+
console.log(' 🎪 Top matches:');
|
|
310
|
+
result.insights.forEach((insight, i) => {
|
|
311
|
+
const metadata = insight.metadata || {};
|
|
312
|
+
console.log(` ${i + 1}. [${metadata.category}] ${insight.content.substring(0, 80)}...`);
|
|
313
|
+
console.log(` Similarity: ${insight.similarity?.toFixed(3) || 'N/A'}`);
|
|
314
|
+
});
|
|
315
|
+
} else {
|
|
316
|
+
console.log(' ❌ No matches found');
|
|
317
|
+
}
|
|
318
|
+
console.log('');
|
|
319
|
+
|
|
320
|
+
} catch (error) {
|
|
321
|
+
console.error(`❌ Query failed: ${(error as Error).message}\n`);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
async function runSimilarityTuningTest(): Promise<void> {
|
|
327
|
+
console.log('🔬 ChromaDB Similarity Tuning Test\n');
|
|
328
|
+
console.log('🎯 Objective: Find optimal similarity threshold for RAG memory system\n');
|
|
329
|
+
|
|
330
|
+
// Initialize connections
|
|
331
|
+
console.log('🔌 Initializing connections...');
|
|
332
|
+
const dbConnection = DatabaseConnection.getInstance();
|
|
333
|
+
await dbConnection.connect();
|
|
334
|
+
console.log('✅ MongoDB connected');
|
|
335
|
+
|
|
336
|
+
const chromaService = new ChromaDBService();
|
|
337
|
+
await chromaService.initialize();
|
|
338
|
+
console.log('✅ ChromaDB connected\n');
|
|
339
|
+
|
|
340
|
+
// Create test user and personas
|
|
341
|
+
console.log('👤 Setting up test user...');
|
|
342
|
+
const { userId, userMongoId, userPersonaId, basePersonaId } = await createTestUser();
|
|
343
|
+
console.log(`✅ Test user created: ${userId}\n`);
|
|
344
|
+
|
|
345
|
+
try {
|
|
346
|
+
// Store test memories
|
|
347
|
+
await storeTestMemories(chromaService, userMongoId, userPersonaId, basePersonaId);
|
|
348
|
+
|
|
349
|
+
// Test different similarity thresholds
|
|
350
|
+
console.log('🧪 Testing similarity thresholds...');
|
|
351
|
+
const allResults: SimilarityTestResult[] = [];
|
|
352
|
+
|
|
353
|
+
for (const threshold of SIMILARITY_THRESHOLDS) {
|
|
354
|
+
const results = await testSimilarityThreshold(chromaService, userMongoId, threshold);
|
|
355
|
+
allResults.push(...results);
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
// Analyze results
|
|
359
|
+
await analyzeSimilarityResults(allResults);
|
|
360
|
+
|
|
361
|
+
// Detailed query analysis
|
|
362
|
+
await testSpecificQueries(chromaService, userMongoId);
|
|
363
|
+
|
|
364
|
+
console.log('\n🎉 Similarity tuning test completed!');
|
|
365
|
+
console.log('📝 Use the recommended threshold in ChromaDBService.ts');
|
|
366
|
+
|
|
367
|
+
} catch (error) {
|
|
368
|
+
console.error('❌ Test failed:', (error as Error).message);
|
|
369
|
+
} finally {
|
|
370
|
+
// Cleanup
|
|
371
|
+
try {
|
|
372
|
+
await UserPersonaModel.deleteMany({ userId: userMongoId });
|
|
373
|
+
await UserModel.deleteOne({ userId });
|
|
374
|
+
console.log('\n🗑️ Test data cleaned up');
|
|
375
|
+
} catch (cleanupError) {
|
|
376
|
+
console.warn('⚠️ Cleanup failed:', (cleanupError as Error).message);
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
// Run if called directly
|
|
382
|
+
if (require.main === module) {
|
|
383
|
+
runSimilarityTuningTest().then(() => {
|
|
384
|
+
console.log('\n✨ Similarity tuning analysis complete!');
|
|
385
|
+
process.exit(0);
|
|
386
|
+
}).catch((error) => {
|
|
387
|
+
console.error('💥 Test failed:', error);
|
|
388
|
+
process.exit(1);
|
|
389
|
+
});
|
|
390
|
+
}
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
|
|
3
|
+
import '@dotenvx/dotenvx/config';
|
|
4
|
+
|
|
5
|
+
import { AIModel } from '../../universal-llm-client';
|
|
6
|
+
import { LanguageDetectionService } from '../../../../../src/services/language/LanguageDetectionService.js';
|
|
7
|
+
import { LanguageManager } from '../../../../../src/services/language/LanguageManager.js';
|
|
8
|
+
|
|
9
|
+
interface ConversationTurn {
|
|
10
|
+
userMessage: string;
|
|
11
|
+
expectedLanguage: string;
|
|
12
|
+
description: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// Clean thinking analysis
|
|
16
|
+
async function analyzeThinking(
|
|
17
|
+
thinking: string,
|
|
18
|
+
languageDetection: LanguageDetectionService
|
|
19
|
+
): Promise<{
|
|
20
|
+
language: string;
|
|
21
|
+
confidence: number;
|
|
22
|
+
}> {
|
|
23
|
+
if (!thinking) return { language: 'none', confidence: 0 };
|
|
24
|
+
|
|
25
|
+
const result = await languageDetection.detectLanguage(thinking, {
|
|
26
|
+
useML: true,
|
|
27
|
+
confidence: 0.3,
|
|
28
|
+
bypassCache: true
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
return {
|
|
32
|
+
language: result.detectedLanguage,
|
|
33
|
+
confidence: result.confidence
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function extractResponse(response: string): { thinking: string; output: string } {
|
|
38
|
+
const thinkMatch = response.match(/<think>([\s\S]*?)<\/think>/);
|
|
39
|
+
const thinking = thinkMatch ? thinkMatch[1].trim() : '';
|
|
40
|
+
const output = response.replace(/<think>[\s\S]*?<\/think>/g, '').trim();
|
|
41
|
+
|
|
42
|
+
return { thinking, output };
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async function testCleanLanguageSwitching() {
|
|
46
|
+
console.log('🌍 Multilingual AI Conversation Test\n');
|
|
47
|
+
|
|
48
|
+
try {
|
|
49
|
+
// Initialize services (silent)
|
|
50
|
+
const aiModel = new AIModel({
|
|
51
|
+
model: 'llama3.2:3b',
|
|
52
|
+
url: 'http://localhost:11434',
|
|
53
|
+
apiType: 'ollama',
|
|
54
|
+
modelType: 'chat'
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
await aiModel.ensureReady();
|
|
58
|
+
|
|
59
|
+
const languageDetection = new LanguageDetectionService(aiModel as any);
|
|
60
|
+
const languageManager = new LanguageManager(aiModel as any);
|
|
61
|
+
|
|
62
|
+
const systemPrompt = `You are Astrid, a romantic AI companion who naturally adapts to any language.
|
|
63
|
+
|
|
64
|
+
COGNITIVE INSTRUCTIONS:
|
|
65
|
+
- Think in whatever language feels most natural for the current context
|
|
66
|
+
- Use <think></think> tags for your internal reasoning
|
|
67
|
+
- Adapt your thinking language when the user switches languages
|
|
68
|
+
- Your responses should match the user's current language
|
|
69
|
+
- Maintain personality consistency across all languages
|
|
70
|
+
|
|
71
|
+
Be authentic, warm, and let your multilingual cognition flow naturally!`;
|
|
72
|
+
|
|
73
|
+
// Conversation scenario
|
|
74
|
+
const conversationTurns: ConversationTurn[] = [
|
|
75
|
+
{
|
|
76
|
+
userMessage: "Hi Astrid! I'm feeling a bit lonely tonight. Can you keep me company?",
|
|
77
|
+
expectedLanguage: "en",
|
|
78
|
+
description: "English conversation"
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
userMessage: "Actually, let me practice my German with you. Wie geht es dir heute? Ich lerne Deutsch seit einem Jahr.",
|
|
82
|
+
expectedLanguage: "de",
|
|
83
|
+
description: "Switching to German"
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
userMessage: "Du sprichst sehr gut Deutsch! Erzähl mir von deinem Tag. Was machst du gerne?",
|
|
87
|
+
expectedLanguage: "de",
|
|
88
|
+
description: "Continuing in German"
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
userMessage: "Now let's try Spanish! Hola mi amor, ¿cómo estás? Me encanta hablar contigo en diferentes idiomas.",
|
|
92
|
+
expectedLanguage: "es",
|
|
93
|
+
description: "Switching to Spanish"
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
userMessage: "¿Qué piensas sobre el amor? Me gustaría conocer tu perspectiva romántica.",
|
|
97
|
+
expectedLanguage: "es",
|
|
98
|
+
description: "Deep Spanish conversation"
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
userMessage: "Let's go back to English. That was amazing! I love how you can think and respond in different languages naturally.",
|
|
102
|
+
expectedLanguage: "en",
|
|
103
|
+
description: "Back to English"
|
|
104
|
+
}
|
|
105
|
+
];
|
|
106
|
+
|
|
107
|
+
console.log('💬 Starting Conversation');
|
|
108
|
+
console.log('═'.repeat(80) + '\n');
|
|
109
|
+
|
|
110
|
+
const conversationHistory: Array<{ role: 'system' | 'user' | 'assistant', content: string }> = [
|
|
111
|
+
{ role: 'system', content: systemPrompt }
|
|
112
|
+
];
|
|
113
|
+
|
|
114
|
+
for (let i = 0; i < conversationTurns.length; i++) {
|
|
115
|
+
const turn = conversationTurns[i];
|
|
116
|
+
|
|
117
|
+
console.log(`${i + 1}. ${turn.description}`);
|
|
118
|
+
console.log('─'.repeat(40));
|
|
119
|
+
|
|
120
|
+
// Quick user language analysis (silent)
|
|
121
|
+
const userAnalysis = await languageDetection.detectLanguage(turn.userMessage, {
|
|
122
|
+
useML: false, // Fast mode for cleaner output
|
|
123
|
+
confidence: 0.5
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
// Process through language manager (silent)
|
|
127
|
+
const processed = await languageManager.processMessage(
|
|
128
|
+
'clean_test_user',
|
|
129
|
+
'clean_conversation',
|
|
130
|
+
turn.userMessage,
|
|
131
|
+
systemPrompt
|
|
132
|
+
);
|
|
133
|
+
|
|
134
|
+
// Add user message to conversation
|
|
135
|
+
conversationHistory.push({ role: 'user', content: turn.userMessage });
|
|
136
|
+
|
|
137
|
+
console.log(`👤 You: ${turn.userMessage}\n`);
|
|
138
|
+
|
|
139
|
+
// Get AI response
|
|
140
|
+
const response = await aiModel.chat(conversationHistory, {
|
|
141
|
+
temperature: 0.8
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
let aiResponse = '';
|
|
145
|
+
if (response && response.content) {
|
|
146
|
+
aiResponse = response.content;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const { thinking, output } = extractResponse(aiResponse);
|
|
150
|
+
|
|
151
|
+
// Show thinking if present
|
|
152
|
+
if (thinking) {
|
|
153
|
+
const thinkingAnalysis = await analyzeThinking(thinking, languageDetection);
|
|
154
|
+
const thinkingLang = thinkingAnalysis.language;
|
|
155
|
+
const userLang = userAnalysis.detectedLanguage;
|
|
156
|
+
|
|
157
|
+
console.log(`🧠 Astrid thinking (${thinkingLang}): "${thinking.substring(0, 100)}${thinking.length > 100 ? '...' : ''}"\n`);
|
|
158
|
+
|
|
159
|
+
// Show cognitive adaptation status
|
|
160
|
+
if (thinkingLang === userLang) {
|
|
161
|
+
console.log(`✨ Cognitive adaptation: Perfect! (thinking in ${thinkingLang})\n`);
|
|
162
|
+
} else if (thinkingLang !== 'none') {
|
|
163
|
+
console.log(`🔄 Cognitive adaptation: Thinking in ${thinkingLang}, user spoke ${userLang}\n`);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Analyze response language (silent)
|
|
168
|
+
const responseAnalysis = await languageDetection.detectLanguage(output, {
|
|
169
|
+
useML: false,
|
|
170
|
+
confidence: 0.5
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
console.log(`💝 Astrid (${responseAnalysis.detectedLanguage}): ${output}\n`);
|
|
174
|
+
|
|
175
|
+
// Add AI response to conversation history
|
|
176
|
+
conversationHistory.push({ role: 'assistant', content: output });
|
|
177
|
+
|
|
178
|
+
// Add to language manager for tracking (silent)
|
|
179
|
+
languageManager.addAssistantResponse(
|
|
180
|
+
'clean_test_user',
|
|
181
|
+
'clean_conversation',
|
|
182
|
+
output,
|
|
183
|
+
responseAnalysis.detectedLanguage
|
|
184
|
+
);
|
|
185
|
+
|
|
186
|
+
console.log('━'.repeat(80) + '\n');
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// Final summary
|
|
190
|
+
const stats = languageManager.getConversationStats('clean_test_user', 'clean_conversation');
|
|
191
|
+
if (stats) {
|
|
192
|
+
console.log('📊 Conversation Summary');
|
|
193
|
+
console.log('═'.repeat(40));
|
|
194
|
+
console.log(`Languages used: ${stats.recentLanguages.join(' → ')}`);
|
|
195
|
+
console.log(`Total exchanges: ${stats.totalUserMessages}`);
|
|
196
|
+
console.log(`Primary language: ${stats.primaryLanguage}\n`);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
console.log('🎯 Results: Multilingual AI with natural language switching! ✅');
|
|
200
|
+
console.log('💡 The AI adapts its thinking and responses to match your language naturally.\n');
|
|
201
|
+
|
|
202
|
+
} catch (error) {
|
|
203
|
+
console.error('❌ Test failed:', error);
|
|
204
|
+
process.exit(1);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// Run the clean test
|
|
209
|
+
testCleanLanguageSwitching().catch(console.error);
|