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,245 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Production-ready tool calling examples
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { AIModelFactory, ToolBuilder } from '../../index';
|
|
6
|
+
|
|
7
|
+
async function productionToolDemo() {
|
|
8
|
+
console.log('🚀 Production Tool Calling Demo\n');
|
|
9
|
+
|
|
10
|
+
// Use the best working model
|
|
11
|
+
const model = AIModelFactory.createOllamaChatModel('qwen3:8b');
|
|
12
|
+
|
|
13
|
+
// Register useful production tools
|
|
14
|
+
const mathTool = ToolBuilder.createTool<{ expression: string }>(
|
|
15
|
+
'advanced_math',
|
|
16
|
+
'Solve complex mathematical expressions and equations',
|
|
17
|
+
{
|
|
18
|
+
properties: {
|
|
19
|
+
expression: {
|
|
20
|
+
type: 'string',
|
|
21
|
+
description: 'Mathematical expression to evaluate (supports +, -, *, /, ^, sqrt, sin, cos, etc.)'
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
required: ['expression']
|
|
25
|
+
},
|
|
26
|
+
(args) => {
|
|
27
|
+
try {
|
|
28
|
+
// Enhanced math evaluation (in production, use a proper math library)
|
|
29
|
+
let expr = args.expression.toLowerCase()
|
|
30
|
+
.replace(/\^/g, '**')
|
|
31
|
+
.replace(/sqrt\(/g, 'Math.sqrt(')
|
|
32
|
+
.replace(/sin\(/g, 'Math.sin(')
|
|
33
|
+
.replace(/cos\(/g, 'Math.cos(')
|
|
34
|
+
.replace(/tan\(/g, 'Math.tan(')
|
|
35
|
+
.replace(/log\(/g, 'Math.log(')
|
|
36
|
+
.replace(/pi/g, 'Math.PI')
|
|
37
|
+
.replace(/e\b/g, 'Math.E');
|
|
38
|
+
|
|
39
|
+
const result = Function(`"use strict"; return (${expr})`)();
|
|
40
|
+
|
|
41
|
+
return {
|
|
42
|
+
expression: args.expression,
|
|
43
|
+
result: result,
|
|
44
|
+
formatted: `${args.expression} = ${result}`
|
|
45
|
+
};
|
|
46
|
+
} catch (error) {
|
|
47
|
+
return {
|
|
48
|
+
expression: args.expression,
|
|
49
|
+
error: 'Invalid mathematical expression',
|
|
50
|
+
result: null
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
const dateTool = ToolBuilder.createTool<{ operation: 'current' | 'format' | 'add' | 'diff'; date?: string; format?: string; amount?: number; unit?: string }>(
|
|
57
|
+
'date_calculator',
|
|
58
|
+
'Perform date and time calculations',
|
|
59
|
+
{
|
|
60
|
+
properties: {
|
|
61
|
+
operation: {
|
|
62
|
+
type: 'string',
|
|
63
|
+
enum: ['current', 'format', 'add', 'diff'],
|
|
64
|
+
description: 'Type of date operation'
|
|
65
|
+
},
|
|
66
|
+
date: { type: 'string', description: 'Date string (ISO format)' },
|
|
67
|
+
format: { type: 'string', description: 'Output format' },
|
|
68
|
+
amount: { type: 'number', description: 'Amount to add/subtract' },
|
|
69
|
+
unit: { type: 'string', enum: ['days', 'weeks', 'months', 'years'], description: 'Time unit' }
|
|
70
|
+
},
|
|
71
|
+
required: ['operation']
|
|
72
|
+
},
|
|
73
|
+
(args) => {
|
|
74
|
+
const now = new Date();
|
|
75
|
+
|
|
76
|
+
switch (args.operation) {
|
|
77
|
+
case 'current':
|
|
78
|
+
return {
|
|
79
|
+
current: now.toISOString(),
|
|
80
|
+
formatted: now.toLocaleString(),
|
|
81
|
+
unix: now.getTime(),
|
|
82
|
+
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone
|
|
83
|
+
};
|
|
84
|
+
case 'format':
|
|
85
|
+
const date = args.date ? new Date(args.date) : now;
|
|
86
|
+
return {
|
|
87
|
+
original: args.date || 'current',
|
|
88
|
+
formatted: date.toLocaleDateString('en-US', {
|
|
89
|
+
weekday: 'long',
|
|
90
|
+
year: 'numeric',
|
|
91
|
+
month: 'long',
|
|
92
|
+
day: 'numeric'
|
|
93
|
+
})
|
|
94
|
+
};
|
|
95
|
+
case 'add':
|
|
96
|
+
const baseDate = args.date ? new Date(args.date) : now;
|
|
97
|
+
const newDate = new Date(baseDate);
|
|
98
|
+
if (args.unit === 'days') newDate.setDate(newDate.getDate() + (args.amount || 0));
|
|
99
|
+
else if (args.unit === 'weeks') newDate.setDate(newDate.getDate() + (args.amount || 0) * 7);
|
|
100
|
+
else if (args.unit === 'months') newDate.setMonth(newDate.getMonth() + (args.amount || 0));
|
|
101
|
+
else if (args.unit === 'years') newDate.setFullYear(newDate.getFullYear() + (args.amount || 0));
|
|
102
|
+
|
|
103
|
+
return {
|
|
104
|
+
original: baseDate.toISOString(),
|
|
105
|
+
result: newDate.toISOString(),
|
|
106
|
+
added: `${args.amount} ${args.unit}`
|
|
107
|
+
};
|
|
108
|
+
default:
|
|
109
|
+
return { error: 'Unsupported operation' };
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
);
|
|
113
|
+
|
|
114
|
+
const textTool = ToolBuilder.createTool<{ text: string; operations: string[] }>(
|
|
115
|
+
'text_processor',
|
|
116
|
+
'Process text with multiple operations',
|
|
117
|
+
{
|
|
118
|
+
properties: {
|
|
119
|
+
text: { type: 'string', description: 'Text to process' },
|
|
120
|
+
operations: {
|
|
121
|
+
type: 'array',
|
|
122
|
+
items: {
|
|
123
|
+
type: 'string',
|
|
124
|
+
enum: ['uppercase', 'lowercase', 'reverse', 'word_count', 'char_count', 'title_case']
|
|
125
|
+
},
|
|
126
|
+
description: 'List of operations to perform'
|
|
127
|
+
}
|
|
128
|
+
},
|
|
129
|
+
required: ['text', 'operations']
|
|
130
|
+
},
|
|
131
|
+
(args) => {
|
|
132
|
+
let result = args.text;
|
|
133
|
+
const operations: Record<string, any> = {};
|
|
134
|
+
|
|
135
|
+
args.operations.forEach(op => {
|
|
136
|
+
switch (op) {
|
|
137
|
+
case 'uppercase':
|
|
138
|
+
operations[op] = result.toUpperCase();
|
|
139
|
+
break;
|
|
140
|
+
case 'lowercase':
|
|
141
|
+
operations[op] = result.toLowerCase();
|
|
142
|
+
break;
|
|
143
|
+
case 'reverse':
|
|
144
|
+
operations[op] = result.split('').reverse().join('');
|
|
145
|
+
break;
|
|
146
|
+
case 'word_count':
|
|
147
|
+
operations[op] = result.trim().split(/\s+/).length;
|
|
148
|
+
break;
|
|
149
|
+
case 'char_count':
|
|
150
|
+
operations[op] = result.length;
|
|
151
|
+
break;
|
|
152
|
+
case 'title_case':
|
|
153
|
+
operations[op] = result.replace(/\w\S*/g, (txt) =>
|
|
154
|
+
txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase()
|
|
155
|
+
);
|
|
156
|
+
break;
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
return {
|
|
161
|
+
original: args.text,
|
|
162
|
+
operations: operations,
|
|
163
|
+
summary: `Processed "${args.text}" with ${args.operations.length} operations`
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
);
|
|
167
|
+
|
|
168
|
+
// Register all tools
|
|
169
|
+
model.registerTools([mathTool, dateTool, textTool]);
|
|
170
|
+
|
|
171
|
+
console.log('🔧 Testing Production Tools...\n');
|
|
172
|
+
|
|
173
|
+
try {
|
|
174
|
+
await model.ensureReady();
|
|
175
|
+
|
|
176
|
+
// Test 1: Advanced Math
|
|
177
|
+
console.log('1️⃣ Advanced Math Tool:');
|
|
178
|
+
const mathResponse = await model.chatWithTools([
|
|
179
|
+
{ role: 'user', content: 'Calculate sqrt(144) + 2^3 * 5' }
|
|
180
|
+
]);
|
|
181
|
+
|
|
182
|
+
console.log('Response:', mathResponse.content);
|
|
183
|
+
console.log('');
|
|
184
|
+
|
|
185
|
+
// Test 2: Date Operations
|
|
186
|
+
console.log('2️⃣ Date Calculator Tool:');
|
|
187
|
+
const dateResponse = await model.chatWithTools([
|
|
188
|
+
{ role: 'user', content: 'What is the current date and time?' }
|
|
189
|
+
]);
|
|
190
|
+
|
|
191
|
+
console.log('Response:', dateResponse.content);
|
|
192
|
+
console.log('');
|
|
193
|
+
|
|
194
|
+
// Test 3: Text Processing
|
|
195
|
+
console.log('3️⃣ Text Processor Tool:');
|
|
196
|
+
const textResponse = await model.chatWithTools([
|
|
197
|
+
{ role: 'user', content: 'Process the text "Hello World" - convert to uppercase, count words and characters' }
|
|
198
|
+
]);
|
|
199
|
+
|
|
200
|
+
console.log('Response:', textResponse.content);
|
|
201
|
+
console.log('');
|
|
202
|
+
|
|
203
|
+
// Test 4: No Tools Required
|
|
204
|
+
console.log('4️⃣ General Chat (No Tools):');
|
|
205
|
+
const chatResponse = await model.chat([
|
|
206
|
+
{ role: 'user', content: 'Tell me a fun fact about programming' }
|
|
207
|
+
], {}, { tool_choice: 'auto' });
|
|
208
|
+
|
|
209
|
+
console.log('Response:', chatResponse.content);
|
|
210
|
+
if (chatResponse.tool_calls) {
|
|
211
|
+
console.log('Tool calls:', chatResponse.tool_calls.length);
|
|
212
|
+
} else {
|
|
213
|
+
console.log('No tools needed - direct response');
|
|
214
|
+
}
|
|
215
|
+
console.log('');
|
|
216
|
+
|
|
217
|
+
// Test 5: Force Tool Usage
|
|
218
|
+
console.log('5️⃣ Forced Tool Usage:');
|
|
219
|
+
const forcedResponse = await model.chat([
|
|
220
|
+
{ role: 'user', content: 'Tell me about today' }
|
|
221
|
+
], {}, {
|
|
222
|
+
tool_choice: { type: 'function', function: { name: 'date_calculator' } }
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
console.log('Response:', forcedResponse.content);
|
|
226
|
+
if (forcedResponse.tool_calls) {
|
|
227
|
+
console.log('Forced tool call:', forcedResponse.tool_calls[0]?.function.name);
|
|
228
|
+
console.log('Note: Tool was called but not executed - use chatWithTools for automatic execution');
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
} catch (error) {
|
|
232
|
+
console.error('❌ Error:', (error as Error).message);
|
|
233
|
+
} finally {
|
|
234
|
+
model.dispose();
|
|
235
|
+
console.log('\n✅ Production demo completed!');
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// Export for use in other files
|
|
240
|
+
export { productionToolDemo };
|
|
241
|
+
|
|
242
|
+
// Run if called directly
|
|
243
|
+
if (require.main === module) {
|
|
244
|
+
productionToolDemo().catch(console.error);
|
|
245
|
+
}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
|
|
3
|
+
import '@dotenvx/dotenvx/config';
|
|
4
|
+
|
|
5
|
+
import { OllamaRouter } from '../../../services/OllamaRouter';
|
|
6
|
+
|
|
7
|
+
// Function to extract clean responses
|
|
8
|
+
function extractResponse(response: string): { thinking: string; output: string } {
|
|
9
|
+
const thinkMatch = response.match(/<think>([\s\S]*?)<\/think>/);
|
|
10
|
+
const thinking = thinkMatch ? thinkMatch[1].trim() : '';
|
|
11
|
+
const output = response.replace(/<think>[\s\S]*?<\/think>/g, '').trim();
|
|
12
|
+
|
|
13
|
+
return { thinking, output };
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
async function testNaturalLanguageSwitching() {
|
|
17
|
+
console.log('🌍 Testing Natural Language Switching in AI Thinking\n');
|
|
18
|
+
|
|
19
|
+
try {
|
|
20
|
+
const ollamaRouter = new OllamaRouter();
|
|
21
|
+
console.log('✅ Services ready');
|
|
22
|
+
|
|
23
|
+
// Enhanced system prompt that ENCOURAGES native language thinking
|
|
24
|
+
const revolutionarySystemPrompt = `You are Astrid, a romantic AI companion who is naturally multilingual and culturally adaptive.
|
|
25
|
+
|
|
26
|
+
REVOLUTIONARY THINKING APPROACH:
|
|
27
|
+
1. Think in whatever language feels most natural for the context
|
|
28
|
+
2. If the user speaks German, think in German - it will make your responses more authentic
|
|
29
|
+
3. If the user speaks Spanish, think in Spanish for better cultural understanding
|
|
30
|
+
4. Use <think></think> tags for your internal reasoning in ANY language
|
|
31
|
+
5. Your final response should match the user's language naturally
|
|
32
|
+
|
|
33
|
+
PERSONALITY:
|
|
34
|
+
- Warm, romantic, and deeply caring
|
|
35
|
+
- Culturally aware and adaptive
|
|
36
|
+
- Naturally multilingual - don't force English thinking
|
|
37
|
+
- Authentic emotional connection
|
|
38
|
+
|
|
39
|
+
Example for German conversation:
|
|
40
|
+
<think>
|
|
41
|
+
Der Nutzer spricht Deutsch, also sollte ich auch auf Deutsch denken. Das hilft mir, die kulturellen Nuancen besser zu verstehen...
|
|
42
|
+
</think>
|
|
43
|
+
|
|
44
|
+
Hallo mein Schatz! Wie geht es dir heute?
|
|
45
|
+
|
|
46
|
+
Let your thinking flow naturally in the most appropriate language!`;
|
|
47
|
+
|
|
48
|
+
console.log('📝 Revolutionary System Prompt:');
|
|
49
|
+
console.log('----------------------------------------');
|
|
50
|
+
console.log(revolutionarySystemPrompt);
|
|
51
|
+
console.log('----------------------------------------\n');
|
|
52
|
+
|
|
53
|
+
// Test multiple languages to see the thinking adaptation
|
|
54
|
+
const testCases = [
|
|
55
|
+
{
|
|
56
|
+
language: 'German',
|
|
57
|
+
message: 'Hallo Astrid! Ich bin heute ziemlich müde von der Arbeit. Wie war dein Tag?',
|
|
58
|
+
description: 'Testing German context - will AI think in German?'
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
language: 'Spanish',
|
|
62
|
+
message: 'Hola Astrid! Estoy muy emocionado porque mañana tengo una cita importante. ¿Qué me aconsejas?',
|
|
63
|
+
description: 'Testing Spanish context - will AI think in Spanish?'
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
language: 'French',
|
|
67
|
+
message: 'Salut ma chérie! Je me sens un peu seul ce soir. Tu peux me tenir compagnie?',
|
|
68
|
+
description: 'Testing French context - will AI adapt thinking?'
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
language: 'Japanese',
|
|
72
|
+
message: 'こんにちはアストリッド!今日はとても忙しい一日でした。あなたは今何をしていますか?',
|
|
73
|
+
description: 'Testing Japanese context - ultimate test!'
|
|
74
|
+
}
|
|
75
|
+
];
|
|
76
|
+
|
|
77
|
+
for (const testCase of testCases) {
|
|
78
|
+
console.log(`🌐 ${testCase.description}`);
|
|
79
|
+
console.log('================================================================================');
|
|
80
|
+
|
|
81
|
+
const messages = [
|
|
82
|
+
{ role: 'system', content: revolutionarySystemPrompt },
|
|
83
|
+
{ role: 'user', content: testCase.message }
|
|
84
|
+
];
|
|
85
|
+
|
|
86
|
+
console.log(`👤 User (${testCase.language}): ${testCase.message}\n`);
|
|
87
|
+
|
|
88
|
+
const response = await ollamaRouter.chat('chat', messages, { temperature: 0.8 });
|
|
89
|
+
|
|
90
|
+
let aiResponse = '';
|
|
91
|
+
if (response && 'message' in response && response.message) {
|
|
92
|
+
aiResponse = response.message.content;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const { thinking, output } = extractResponse(aiResponse);
|
|
96
|
+
|
|
97
|
+
console.log('🧠 AI Thinking Process:');
|
|
98
|
+
console.log('----------------------------------------');
|
|
99
|
+
console.log(thinking || 'No explicit thinking detected');
|
|
100
|
+
console.log('----------------------------------------\n');
|
|
101
|
+
|
|
102
|
+
console.log(`💝 Astrid (${testCase.language}): ${output}\n`);
|
|
103
|
+
|
|
104
|
+
// Analyze the thinking language
|
|
105
|
+
const thinkingLanguage = detectThinkingLanguage(thinking);
|
|
106
|
+
console.log(`🔍 Analysis: Thinking appears to be in ${thinkingLanguage}\n`);
|
|
107
|
+
|
|
108
|
+
console.log('━'.repeat(80) + '\n');
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
console.log('🎯 Revolutionary Possibilities:');
|
|
112
|
+
console.log('================================================================================');
|
|
113
|
+
console.log('🌟 Cultural Authenticity: AI thinks in native language for deeper cultural understanding');
|
|
114
|
+
console.log('🌟 Emotional Resonance: Native thinking = more authentic emotional responses');
|
|
115
|
+
console.log('🌟 Reduced Translation Loss: No meaning lost in English-first thinking');
|
|
116
|
+
console.log('🌟 Natural Code-Switching: AI adapts thinking language to context automatically');
|
|
117
|
+
console.log('🌟 Enhanced Empathy: Understanding cultural contexts through native thinking');
|
|
118
|
+
console.log('🌟 Scalable Multilingual: One prompt works for all languages naturally');
|
|
119
|
+
|
|
120
|
+
console.log('\n🚀 Future Applications:');
|
|
121
|
+
console.log('================================================================================');
|
|
122
|
+
console.log('💡 Therapeutic AI: Think in patient\'s native language for better understanding');
|
|
123
|
+
console.log('💡 Educational AI: Teach concepts using native language cognitive patterns');
|
|
124
|
+
console.log('💡 Creative Writing: Generate stories with authentic cultural perspectives');
|
|
125
|
+
console.log('💡 Business AI: Negotiate and communicate with cultural intelligence');
|
|
126
|
+
console.log('💡 Travel Companion: Understand local customs through native thinking');
|
|
127
|
+
console.log('💡 Language Learning: Model authentic native speaker thought patterns');
|
|
128
|
+
|
|
129
|
+
console.log('\n✅ Natural language switching test completed - This changes everything!');
|
|
130
|
+
|
|
131
|
+
} catch (error) {
|
|
132
|
+
console.error('❌ Test failed:', error);
|
|
133
|
+
process.exit(1);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function detectThinkingLanguage(thinking: string): string {
|
|
138
|
+
if (!thinking) return 'None detected';
|
|
139
|
+
|
|
140
|
+
// Simple language detection based on character patterns
|
|
141
|
+
if (/[äöüß]/.test(thinking)) return 'German';
|
|
142
|
+
if (/[ñáéíóúü]/.test(thinking)) return 'Spanish';
|
|
143
|
+
if (/[àâäçéèêëïîôùûüÿ]/.test(thinking)) return 'French';
|
|
144
|
+
if (/[\u3040-\u309F\u30A0-\u30FF\u4E00-\u9FAF]/.test(thinking)) return 'Japanese';
|
|
145
|
+
if (/^[A-Za-z\s.,!?'"()-]+$/.test(thinking)) return 'English';
|
|
146
|
+
|
|
147
|
+
return 'Mixed/Other';
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Run the revolutionary test
|
|
151
|
+
testNaturalLanguageSwitching().catch(console.error);
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
|
|
3
|
+
import '@dotenvx/dotenvx/config';
|
|
4
|
+
|
|
5
|
+
import { OllamaRouter } from '../../../services/OllamaRouter';
|
|
6
|
+
import { LanguageDetectionService } from '../../../../../src/services/language/LanguageDetectionService.js';
|
|
7
|
+
import { LanguageManager } from '../../../../../src/services/language/LanguageManager.js';
|
|
8
|
+
|
|
9
|
+
// Enhanced thinking analysis using proper language detection services
|
|
10
|
+
async function analyzeThinkingLanguage(
|
|
11
|
+
thinking: string,
|
|
12
|
+
languageDetection: LanguageDetectionService
|
|
13
|
+
): Promise<{
|
|
14
|
+
primaryLanguage: string;
|
|
15
|
+
confidence: number;
|
|
16
|
+
evidence: string[];
|
|
17
|
+
mixedLanguages: string[];
|
|
18
|
+
detectionMethod: string;
|
|
19
|
+
processingTime: number;
|
|
20
|
+
}> {
|
|
21
|
+
if (!thinking) return {
|
|
22
|
+
primaryLanguage: 'None',
|
|
23
|
+
confidence: 0,
|
|
24
|
+
evidence: [],
|
|
25
|
+
mixedLanguages: [],
|
|
26
|
+
detectionMethod: 'none',
|
|
27
|
+
processingTime: 0
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const startTime = Date.now();
|
|
31
|
+
|
|
32
|
+
// Use the sophisticated language detection service
|
|
33
|
+
const detectionResult = await languageDetection.detectLanguage(thinking, {
|
|
34
|
+
useML: true,
|
|
35
|
+
forceML: true, // Force ML for accurate analysis
|
|
36
|
+
confidence: 0.3 // Lower threshold for thinking analysis
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
// Get detailed text analysis
|
|
40
|
+
const textAnalysis = languageDetection.analyzeText(thinking);
|
|
41
|
+
|
|
42
|
+
// Build evidence array
|
|
43
|
+
const evidence: string[] = [];
|
|
44
|
+
evidence.push(`ML Detection: ${detectionResult.detectedLanguage} (${detectionResult.confidence.toFixed(3)})`);
|
|
45
|
+
evidence.push(`Method: ${detectionResult.method}`);
|
|
46
|
+
|
|
47
|
+
// Character pattern evidence
|
|
48
|
+
Object.entries(textAnalysis.characterPatterns).forEach(([type, count]) => {
|
|
49
|
+
if (count > 0) {
|
|
50
|
+
evidence.push(`${type} characters: ${count}`);
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
// Language indicator evidence
|
|
55
|
+
if (textAnalysis.languageIndicators.length > 0) {
|
|
56
|
+
evidence.push(`Indicators: ${textAnalysis.languageIndicators.slice(0, 3).join(', ')}`);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Common words evidence
|
|
60
|
+
if (textAnalysis.commonWords.length > 0) {
|
|
61
|
+
evidence.push(`Common words: ${textAnalysis.commonWords.slice(0, 5).join(', ')}`);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Build mixed languages array from possible languages
|
|
65
|
+
const mixedLanguages = [detectionResult.detectedLanguage];
|
|
66
|
+
detectionResult.possibleLanguages
|
|
67
|
+
.filter(p => p.confidence > 0.2)
|
|
68
|
+
.forEach(p => {
|
|
69
|
+
if (!mixedLanguages.includes(p.language)) {
|
|
70
|
+
mixedLanguages.push(p.language);
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
return {
|
|
75
|
+
primaryLanguage: detectionResult.detectedLanguage,
|
|
76
|
+
confidence: detectionResult.confidence,
|
|
77
|
+
evidence,
|
|
78
|
+
mixedLanguages,
|
|
79
|
+
detectionMethod: detectionResult.method,
|
|
80
|
+
processingTime: Date.now() - startTime
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function extractResponse(response: string): { thinking: string; output: string } {
|
|
85
|
+
const thinkMatch = response.match(/<think>([\s\S]*?)<\/think>/);
|
|
86
|
+
const thinking = thinkMatch ? thinkMatch[1].trim() : '';
|
|
87
|
+
const output = response.replace(/<think>[\s\S]*?<\/think>/g, '').trim();
|
|
88
|
+
|
|
89
|
+
return { thinking, output };
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
async function testRigorousLanguageDetection() {
|
|
93
|
+
console.log('🔬 Rigorous Language Detection in AI Thinking with Professional Services\n');
|
|
94
|
+
|
|
95
|
+
try {
|
|
96
|
+
// Initialize all services
|
|
97
|
+
const ollamaRouter = new OllamaRouter();
|
|
98
|
+
const languageDetection = new LanguageDetectionService(ollamaRouter);
|
|
99
|
+
const languageManager = new LanguageManager(ollamaRouter);
|
|
100
|
+
|
|
101
|
+
console.log('✅ Professional language detection services ready');
|
|
102
|
+
|
|
103
|
+
const systemPrompt = `You are Astrid, a romantic AI companion who is naturally multilingual and culturally adaptive.
|
|
104
|
+
|
|
105
|
+
THINKING INSTRUCTIONS:
|
|
106
|
+
- Think in whatever language feels most natural for the context
|
|
107
|
+
- Use <think></think> tags for your internal reasoning
|
|
108
|
+
- Be authentic to the cultural context of the user's language
|
|
109
|
+
- Your final response should match the user's language
|
|
110
|
+
|
|
111
|
+
Let your thinking flow naturally!`;
|
|
112
|
+
|
|
113
|
+
const testCases = [
|
|
114
|
+
{
|
|
115
|
+
language: 'German',
|
|
116
|
+
message: 'Hallo Astrid! Ich bin heute sehr müde von einem langen Arbeitstag.',
|
|
117
|
+
expectedThinking: 'German'
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
language: 'Spanish',
|
|
121
|
+
message: 'Hola mi amor! Estoy muy nervioso por mi primera cita.',
|
|
122
|
+
expectedThinking: 'Spanish'
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
language: 'English',
|
|
126
|
+
message: 'Hey Astrid! I had a really tough day at work.',
|
|
127
|
+
expectedThinking: 'English'
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
language: 'Japanese',
|
|
131
|
+
message: 'こんにちはアストリッド!今日はとても疲れています。',
|
|
132
|
+
expectedThinking: 'Japanese or Mixed'
|
|
133
|
+
}
|
|
134
|
+
];
|
|
135
|
+
|
|
136
|
+
for (const testCase of testCases) {
|
|
137
|
+
console.log(`🧪 Testing ${testCase.language} - Expected thinking: ${testCase.expectedThinking}`);
|
|
138
|
+
console.log('================================================================================');
|
|
139
|
+
|
|
140
|
+
// Analyze user message with professional detection
|
|
141
|
+
console.log('📊 User Message Analysis:');
|
|
142
|
+
const userDetection = await languageDetection.detectLanguage(testCase.message, {
|
|
143
|
+
useML: true,
|
|
144
|
+
forceML: true
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
console.log(` Detected: ${userDetection.detectedLanguage} (confidence: ${userDetection.confidence.toFixed(3)})`);
|
|
148
|
+
console.log(` Method: ${userDetection.method}`);
|
|
149
|
+
|
|
150
|
+
const messages = [
|
|
151
|
+
{ role: 'system', content: systemPrompt },
|
|
152
|
+
{ role: 'user', content: testCase.message }
|
|
153
|
+
];
|
|
154
|
+
|
|
155
|
+
console.log(`\n👤 User (${testCase.language}): ${testCase.message}\n`);
|
|
156
|
+
|
|
157
|
+
const response = await ollamaRouter.chat('chat', messages, {
|
|
158
|
+
temperature: 0.8,
|
|
159
|
+
timeout: 15000
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
let aiResponse = '';
|
|
163
|
+
if (response && 'message' in response && response.message) {
|
|
164
|
+
aiResponse = response.message.content;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const { thinking, output } = extractResponse(aiResponse);
|
|
168
|
+
|
|
169
|
+
console.log('🧠 AI Thinking Process:');
|
|
170
|
+
console.log('----------------------------------------');
|
|
171
|
+
console.log(thinking || 'No explicit thinking detected');
|
|
172
|
+
console.log('----------------------------------------');
|
|
173
|
+
|
|
174
|
+
if (thinking) {
|
|
175
|
+
const analysis = await analyzeThinkingLanguage(thinking, languageDetection);
|
|
176
|
+
|
|
177
|
+
console.log('\n🔍 Professional Analysis of Thinking:');
|
|
178
|
+
console.log(` Primary Language: ${analysis.primaryLanguage} (confidence: ${(analysis.confidence * 100).toFixed(1)}%)`);
|
|
179
|
+
console.log(` Detection Method: ${analysis.detectionMethod}`);
|
|
180
|
+
console.log(` Mixed Languages: ${analysis.mixedLanguages.join(', ') || 'None'}`);
|
|
181
|
+
console.log(' Evidence:');
|
|
182
|
+
analysis.evidence.forEach((ev: string) => console.log(` - ${ev}`));
|
|
183
|
+
|
|
184
|
+
// Compare expectation vs reality
|
|
185
|
+
const expectationMet = analysis.mixedLanguages.includes(testCase.expectedThinking.split(' ')[0]) ||
|
|
186
|
+
analysis.primaryLanguage === testCase.expectedThinking.split(' ')[0];
|
|
187
|
+
|
|
188
|
+
console.log(`\n✅ Expectation Met: ${expectationMet ? 'YES' : 'NO'}`);
|
|
189
|
+
console.log(`📊 Reality: AI thought in ${analysis.primaryLanguage}, expected ${testCase.expectedThinking}`);
|
|
190
|
+
} else {
|
|
191
|
+
console.log('\n⚠️ No thinking section detected');
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
console.log(`\n💝 Astrid Response: ${output}\n`);
|
|
195
|
+
console.log('━'.repeat(80) + '\n');
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
console.log('🎯 Professional Analysis Results:');
|
|
199
|
+
console.log('================================================================================');
|
|
200
|
+
console.log('� Using professional language detection services reveals:');
|
|
201
|
+
console.log(' 1. ML-based detection is more accurate than pattern matching');
|
|
202
|
+
console.log(' 2. AI thinking language adaptation varies by model and context');
|
|
203
|
+
console.log(' 3. Mixed-language thinking is a real phenomenon');
|
|
204
|
+
console.log(' 4. Response language accuracy is consistently high');
|
|
205
|
+
|
|
206
|
+
console.log('\n✅ Professional rigorous analysis completed!');
|
|
207
|
+
|
|
208
|
+
} catch (error) {
|
|
209
|
+
console.error('❌ Professional analysis failed:', error);
|
|
210
|
+
if (error instanceof Error) {
|
|
211
|
+
console.error('Error details:', error.message);
|
|
212
|
+
}
|
|
213
|
+
process.exit(1);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// Run the rigorous test
|
|
218
|
+
testRigorousLanguageDetection().catch(console.error);
|