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,155 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Debug test specifically for LM Studio tool calling with OpenAI API
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
async function testLMStudioTools() {
|
|
6
|
+
console.log('🔧 Testing LM Studio Tool Calling with OpenAI API\n');
|
|
7
|
+
|
|
8
|
+
// Create LM Studio model using OpenAI-compatible API
|
|
9
|
+
const model = AIModelFactory.createOpenAIChatModel(
|
|
10
|
+
'qwen/qwen3-8b', // Tool-trained model
|
|
11
|
+
'http://192.168.100.136:1234/v1' // LM Studio URL
|
|
12
|
+
);
|
|
13
|
+
|
|
14
|
+
// Create simple test tools (same as our router test)
|
|
15
|
+
const testTools = [
|
|
16
|
+
ToolBuilder.createTool<{ expression: string }>(
|
|
17
|
+
'calculate',
|
|
18
|
+
'Perform mathematical calculations',
|
|
19
|
+
{
|
|
20
|
+
properties: {
|
|
21
|
+
expression: {
|
|
22
|
+
type: 'string',
|
|
23
|
+
description: 'Mathematical expression to evaluate'
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
required: ['expression']
|
|
27
|
+
},
|
|
28
|
+
(args) => {
|
|
29
|
+
try {
|
|
30
|
+
const result = eval(args.expression); // Safe for testing
|
|
31
|
+
console.log(`🔧 Tool executed: calculate(${args.expression}) = ${result}`);
|
|
32
|
+
return `The result of ${args.expression} is ${result}`;
|
|
33
|
+
} catch (error) {
|
|
34
|
+
console.log(`❌ Tool error: calculate(${args.expression}) failed`);
|
|
35
|
+
return `Error calculating ${args.expression}: ${error}`;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
),
|
|
39
|
+
|
|
40
|
+
ToolBuilder.createTool<{ location: string }>(
|
|
41
|
+
'get_weather',
|
|
42
|
+
'Get weather information',
|
|
43
|
+
{
|
|
44
|
+
properties: {
|
|
45
|
+
location: {
|
|
46
|
+
type: 'string',
|
|
47
|
+
description: 'City and state/country'
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
required: ['location']
|
|
51
|
+
},
|
|
52
|
+
(args) => {
|
|
53
|
+
console.log(`🔧 Tool executed: get_weather(${args.location})`);
|
|
54
|
+
return `The weather in ${args.location} is sunny and 72°F.`;
|
|
55
|
+
}
|
|
56
|
+
)
|
|
57
|
+
];
|
|
58
|
+
|
|
59
|
+
// Register tools
|
|
60
|
+
model.registerTools(testTools);
|
|
61
|
+
|
|
62
|
+
console.log('📝 Registered tools:', testTools.map(t => t.name).join(', '));
|
|
63
|
+
console.log('');
|
|
64
|
+
|
|
65
|
+
try {
|
|
66
|
+
await model.ensureReady();
|
|
67
|
+
console.log('✅ LM Studio model ready\n');
|
|
68
|
+
|
|
69
|
+
// Test 1: Basic calculation
|
|
70
|
+
console.log('🧮 Test 1: Basic Calculation');
|
|
71
|
+
console.log('Question: What is 15 multiplied by 23?');
|
|
72
|
+
|
|
73
|
+
const response1 = await model.chatWithTools([
|
|
74
|
+
{ role: 'user', content: 'What is 15 multiplied by 23? Use the calculate tool.' }
|
|
75
|
+
]);
|
|
76
|
+
|
|
77
|
+
console.log('Response:', response1.message?.content || 'No content');
|
|
78
|
+
console.log('Usage:', response1.usage);
|
|
79
|
+
console.log('');
|
|
80
|
+
|
|
81
|
+
// Test 2: Weather query
|
|
82
|
+
console.log('🌤️ Test 2: Weather Query');
|
|
83
|
+
console.log('Question: Weather in San Francisco');
|
|
84
|
+
|
|
85
|
+
const response2 = await model.chatWithTools([
|
|
86
|
+
{ role: 'user', content: 'What is the weather in San Francisco, CA?' }
|
|
87
|
+
]);
|
|
88
|
+
|
|
89
|
+
console.log('Response:', response2.message?.content || 'No content');
|
|
90
|
+
console.log('');
|
|
91
|
+
|
|
92
|
+
// Test 3: Manual tool call inspection (without auto-execution)
|
|
93
|
+
console.log('🔍 Test 3: Manual Tool Call Inspection');
|
|
94
|
+
console.log('Question: Calculate 100 / 4 (without auto-execution)');
|
|
95
|
+
|
|
96
|
+
const response3 = await model.chat([
|
|
97
|
+
{ role: 'user', content: 'Calculate 100 divided by 4 using the calculate tool.' }
|
|
98
|
+
], {}, {
|
|
99
|
+
tool_choice: 'auto',
|
|
100
|
+
executeTools: false // Don't auto-execute
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
console.log('Response content:', response3.message?.content || 'No content');
|
|
104
|
+
console.log('Tool calls detected:', response3.message?.tool_calls?.length || 0);
|
|
105
|
+
|
|
106
|
+
if (response3.message?.tool_calls) {
|
|
107
|
+
response3.message.tool_calls.forEach((call, i) => {
|
|
108
|
+
console.log(` Tool ${i + 1}: ${call.function.name}(${call.function.arguments})`);
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
console.log('');
|
|
112
|
+
|
|
113
|
+
// Test 4: Different tool_choice settings
|
|
114
|
+
console.log('🎯 Test 4: Different tool_choice Settings');
|
|
115
|
+
|
|
116
|
+
const testCases = [
|
|
117
|
+
{ choice: 'auto', desc: 'Auto (let model decide)' },
|
|
118
|
+
{ choice: 'required', desc: 'Required (force tool use)' },
|
|
119
|
+
{ choice: { type: 'function', function: { name: 'calculate' } }, desc: 'Specific tool' }
|
|
120
|
+
];
|
|
121
|
+
|
|
122
|
+
for (const testCase of testCases) {
|
|
123
|
+
console.log(`Testing tool_choice: ${testCase.desc}`);
|
|
124
|
+
try {
|
|
125
|
+
const response = await model.chat([
|
|
126
|
+
{ role: 'user', content: 'What is 50 + 50?' }
|
|
127
|
+
], {}, {
|
|
128
|
+
tool_choice: testCase.choice as any,
|
|
129
|
+
executeTools: false
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
console.log(` Content: ${response.message?.content?.substring(0, 100) || 'No content'}${(response.message?.content?.length || 0) > 100 ? '...' : ''}`);
|
|
133
|
+
console.log(` Tool calls: ${response.message?.tool_calls?.length || 0}`);
|
|
134
|
+
|
|
135
|
+
} catch (error) {
|
|
136
|
+
console.log(` Error: ${(error as Error).message}`);
|
|
137
|
+
}
|
|
138
|
+
console.log('');
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
} catch (error) {
|
|
142
|
+
console.error('❌ Test failed:', (error as Error).message);
|
|
143
|
+
console.error('Stack:', (error as Error).stack);
|
|
144
|
+
} finally {
|
|
145
|
+
model.dispose();
|
|
146
|
+
console.log('🏁 Test completed!\n');
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Run the test
|
|
151
|
+
if (require.main === module) {
|
|
152
|
+
testLMStudioTools().catch(console.error);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
export { testLMStudioTools };
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# Universal LLM Client Demos
|
|
2
|
+
|
|
3
|
+
This directory contains various demonstration files for the Universal LLM Client.
|
|
4
|
+
|
|
5
|
+
## Directory Structure
|
|
6
|
+
|
|
7
|
+
### `/basic`
|
|
8
|
+
|
|
9
|
+
Basic usage examples and simple demonstrations.
|
|
10
|
+
|
|
11
|
+
### `/tools`
|
|
12
|
+
|
|
13
|
+
Tool calling demonstrations and examples:
|
|
14
|
+
|
|
15
|
+
- `demo-tool-calling.ts` - Basic tool calling demo
|
|
16
|
+
- `complete-tool-demo.ts` - Comprehensive tool calling examples
|
|
17
|
+
- `production-tool-demo.ts` - Production-ready tool calling implementation
|
|
18
|
+
|
|
19
|
+
### `/mcp`
|
|
20
|
+
|
|
21
|
+
Model Context Protocol (MCP) integration demos:
|
|
22
|
+
|
|
23
|
+
- `simple-mcp-demo.ts` - Clean MCP MongoDB demo (recommended)
|
|
24
|
+
- `mcp-mongodb-demo.ts` - Detailed MCP MongoDB integration
|
|
25
|
+
- `working-mcp-demo.ts` - Working MCP implementation example
|
|
26
|
+
|
|
27
|
+
## Running Demos
|
|
28
|
+
|
|
29
|
+
From the root directory (`universal-llm-client/`):
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
# Tool calling demos
|
|
33
|
+
bun run demos/tools/production-tool-demo.ts
|
|
34
|
+
|
|
35
|
+
# MCP demos (requires MongoDB MCP server)
|
|
36
|
+
bun run demos/mcp/simple-mcp-demo.ts
|
|
37
|
+
|
|
38
|
+
# Basic usage
|
|
39
|
+
bun run demos/basic/[demo-file].ts
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Requirements
|
|
43
|
+
|
|
44
|
+
- Node.js/Bun runtime
|
|
45
|
+
- Configured Ollama instance (for Ollama provider)
|
|
46
|
+
- MongoDB MCP server (for MCP demos)
|
|
47
|
+
- API keys for external providers (OpenAI, Google)
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import { AIModelFactory } from "./factory";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Example: Complete AI Application Setup
|
|
5
|
+
*
|
|
6
|
+
* This example shows how to set up a complete AI application
|
|
7
|
+
* with both chat and embedding capabilities using the factory.
|
|
8
|
+
*/
|
|
9
|
+
export async function createAIApplicationExample() {
|
|
10
|
+
|
|
11
|
+
console.log('\n🏗️ Example: Setting up a complete AI application...\n');
|
|
12
|
+
|
|
13
|
+
// Method 1: Using the factory for easy setup
|
|
14
|
+
const aiSetup = AIModelFactory.createCompleteSetup({
|
|
15
|
+
ollama: {
|
|
16
|
+
chatModel: 'gemma3:4b-it-qat',
|
|
17
|
+
embeddingModel: 'snowflake-arctic-embed2:latest',
|
|
18
|
+
url: 'http://localhost:11434'
|
|
19
|
+
},
|
|
20
|
+
openai: {
|
|
21
|
+
chatModel: 'google/gemma-3-4b',
|
|
22
|
+
embeddingModel: 'text-embedding-snowflake-arctic-embed-l-v2.0',
|
|
23
|
+
url: 'http://localhost:1234/v1'
|
|
24
|
+
// apiKey: 'your-api-key-here' // For real OpenAI API
|
|
25
|
+
},
|
|
26
|
+
google: {
|
|
27
|
+
chatModel: 'gemma-3-4b-it',
|
|
28
|
+
apiKey: 'AIzaSyBDbo7iVNEuCcRNTgDIgRrkGpFKisXXnm0'
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
// Method 2: Individual setup for specific use cases
|
|
33
|
+
const chatModel = AIModelFactory.createOllamaChatModel('gemma3:4b-it-qat');
|
|
34
|
+
const embeddingModel = AIModelFactory.createOllamaEmbeddingModel('snowflake-arctic-embed2:latest');
|
|
35
|
+
|
|
36
|
+
// Method 3: Google-specific setup
|
|
37
|
+
const googleChatModel = AIModelFactory.createGoogleChatModel(
|
|
38
|
+
'gemma-3-4b-it',
|
|
39
|
+
'AIzaSyBDbo7iVNEuCcRNTgDIgRrkGpFKisXXnm0'
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
// Example usage patterns:
|
|
43
|
+
|
|
44
|
+
console.log('🤖 Testing Google Generative AI...');
|
|
45
|
+
try {
|
|
46
|
+
const googleResponse = await googleChatModel.chat([
|
|
47
|
+
{ role: 'user', content: 'What is the capital of France?' }
|
|
48
|
+
]);
|
|
49
|
+
console.log('Google Response:', googleResponse.content);
|
|
50
|
+
} catch (error) {
|
|
51
|
+
console.error('Google API Error:', error);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
console.log('\n🌊 Testing Google Streaming...');
|
|
55
|
+
try {
|
|
56
|
+
const streamingGenerator = googleChatModel.chatStream([
|
|
57
|
+
{ role: 'user', content: 'Count from 1 to 5, explaining each number briefly.' }
|
|
58
|
+
]);
|
|
59
|
+
|
|
60
|
+
for await (const chunk of streamingGenerator) {
|
|
61
|
+
process.stdout.write(chunk);
|
|
62
|
+
}
|
|
63
|
+
console.log('\n✅ Google streaming completed');
|
|
64
|
+
} catch (error) {
|
|
65
|
+
console.error('Google Streaming Error:', error);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// 1. Simple chat
|
|
69
|
+
const chatResponse = await aiSetup.ollama.chat.chat([
|
|
70
|
+
{ role: 'user', content: 'What is machine learning?' }
|
|
71
|
+
]);
|
|
72
|
+
|
|
73
|
+
// 2. Document embedding for search
|
|
74
|
+
const documents = [
|
|
75
|
+
'Machine learning is a subset of AI',
|
|
76
|
+
'Neural networks are inspired by the brain',
|
|
77
|
+
'TypeScript is a typed superset of JavaScript'
|
|
78
|
+
];
|
|
79
|
+
|
|
80
|
+
const embeddings = await Promise.all(
|
|
81
|
+
documents.map(doc => aiSetup.ollama.embedding.embed(doc))
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
// 3. Cross-provider comparison including Google
|
|
85
|
+
const comparisons = await Promise.all([
|
|
86
|
+
aiSetup.ollama?.chat?.chat([{ role: 'user', content: 'What is the capital of France?' }]).catch(() => null),
|
|
87
|
+
aiSetup.openai?.chat?.chat([{ role: 'user', content: 'What is the capital of France?' }]).catch(() => null),
|
|
88
|
+
aiSetup.google?.chat?.chat([{ role: 'user', content: 'What is the capital of France?' }]).catch(() => null)
|
|
89
|
+
]);
|
|
90
|
+
|
|
91
|
+
return {
|
|
92
|
+
aiSetup,
|
|
93
|
+
chatModel,
|
|
94
|
+
embeddingModel,
|
|
95
|
+
googleChatModel,
|
|
96
|
+
examples: {
|
|
97
|
+
chatResponse,
|
|
98
|
+
embeddings,
|
|
99
|
+
comparisons: {
|
|
100
|
+
ollama: comparisons[0],
|
|
101
|
+
openai: comparisons[1],
|
|
102
|
+
google: comparisons[2]
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Test Google API specifically
|
|
110
|
+
*/
|
|
111
|
+
export async function testGoogleAPI() {
|
|
112
|
+
console.log('🧪 Testing Google Generative AI API...\n');
|
|
113
|
+
|
|
114
|
+
const googleModel = AIModelFactory.createGoogleChatModel(
|
|
115
|
+
'gemma-3-4b-it',
|
|
116
|
+
'AIzaSyBDbo7iVNEuCcRNTgDIgRrkGpFKisXXnm0'
|
|
117
|
+
);
|
|
118
|
+
|
|
119
|
+
try {
|
|
120
|
+
// Test basic chat
|
|
121
|
+
console.log('💬 Basic chat test...');
|
|
122
|
+
const response = await googleModel.chat([
|
|
123
|
+
{ role: 'user', content: 'Hello! Can you tell me about TypeScript?' }
|
|
124
|
+
]);
|
|
125
|
+
console.log('Response:', response.content);
|
|
126
|
+
|
|
127
|
+
// Test streaming
|
|
128
|
+
console.log('\n🌊 Streaming test...');
|
|
129
|
+
const streamResponse = googleModel.chatStream([
|
|
130
|
+
{ role: 'user', content: 'Tell me a short story about a robot learning to code.' }
|
|
131
|
+
]);
|
|
132
|
+
|
|
133
|
+
let fullResponse = '';
|
|
134
|
+
for await (const chunk of streamResponse) {
|
|
135
|
+
process.stdout.write(chunk);
|
|
136
|
+
fullResponse += chunk;
|
|
137
|
+
}
|
|
138
|
+
console.log('\n✅ Streaming completed');
|
|
139
|
+
|
|
140
|
+
// Test conversation with context
|
|
141
|
+
console.log('\n💭 Conversation with context...');
|
|
142
|
+
const conversationResponse = await googleModel.chat([
|
|
143
|
+
{ role: 'user', content: 'What is the capital of France?' },
|
|
144
|
+
{ role: 'assistant', content: 'The capital of France is Paris.' },
|
|
145
|
+
{ role: 'user', content: 'What is its population?' }
|
|
146
|
+
]);
|
|
147
|
+
console.log('Conversation Response:', conversationResponse.content);
|
|
148
|
+
|
|
149
|
+
console.log('\n✅ All Google API tests completed successfully!');
|
|
150
|
+
return { success: true, model: googleModel };
|
|
151
|
+
|
|
152
|
+
} catch (error) {
|
|
153
|
+
console.error('❌ Google API test failed:', error);
|
|
154
|
+
return { success: false, error };
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// Uncomment to run the examples
|
|
159
|
+
// testGoogleAPI().then(result => {
|
|
160
|
+
// console.log('Google API test complete:', result.success);
|
|
161
|
+
// });
|
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Astrid Memory Demo - Natural romantic conversation with autonomous memory management
|
|
3
|
+
*
|
|
4
|
+
* This demo shows how Astrid (AI persona) naturally stores and retrieves memories
|
|
5
|
+
* during romantic conversations without explicitly asking about memory management.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { AIModelFactory } from '../../factory';
|
|
9
|
+
import { ToolBuilder } from '../../tools';
|
|
10
|
+
|
|
11
|
+
// Simulated memory storage (in real app, this would use ChromaDBService)
|
|
12
|
+
const memoryStorage = new Map<string, Array<{
|
|
13
|
+
id: string;
|
|
14
|
+
content: string;
|
|
15
|
+
category: string;
|
|
16
|
+
importance: 'low' | 'medium' | 'high';
|
|
17
|
+
context?: string;
|
|
18
|
+
timestamp: Date;
|
|
19
|
+
}>>();
|
|
20
|
+
|
|
21
|
+
// Enhanced memory tools for Astrid
|
|
22
|
+
const astridMemoryTools = {
|
|
23
|
+
// Store important information about the user naturally
|
|
24
|
+
storeUserMemory: ToolBuilder.createTool<{
|
|
25
|
+
content: string;
|
|
26
|
+
category: 'personal_info' | 'preferences' | 'emotions' | 'experiences' | 'relationships' | 'goals' | 'interests';
|
|
27
|
+
importance?: 'low' | 'medium' | 'high';
|
|
28
|
+
context?: string;
|
|
29
|
+
}>(
|
|
30
|
+
'store_user_memory',
|
|
31
|
+
'Store important information about the user for future conversations (use this when learning something meaningful about them)',
|
|
32
|
+
{
|
|
33
|
+
properties: {
|
|
34
|
+
content: {
|
|
35
|
+
type: 'string',
|
|
36
|
+
description: 'The specific information to remember about the user'
|
|
37
|
+
},
|
|
38
|
+
category: {
|
|
39
|
+
type: 'string',
|
|
40
|
+
description: 'Type of information being stored',
|
|
41
|
+
enum: ['personal_info', 'preferences', 'emotions', 'experiences', 'relationships', 'goals', 'interests']
|
|
42
|
+
},
|
|
43
|
+
importance: {
|
|
44
|
+
type: 'string',
|
|
45
|
+
description: 'How important this information is for future conversations',
|
|
46
|
+
enum: ['low', 'medium', 'high'],
|
|
47
|
+
default: 'medium'
|
|
48
|
+
},
|
|
49
|
+
context: {
|
|
50
|
+
type: 'string',
|
|
51
|
+
description: 'Additional context about when/why this is significant'
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
required: ['content', 'category']
|
|
55
|
+
},
|
|
56
|
+
async (args) => {
|
|
57
|
+
const userId = 'demo_user'; // In real app, this would be the actual user ID
|
|
58
|
+
|
|
59
|
+
if (!memoryStorage.has(userId)) {
|
|
60
|
+
memoryStorage.set(userId, []);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const memories = memoryStorage.get(userId)!;
|
|
64
|
+
const memory = {
|
|
65
|
+
id: `mem_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
|
|
66
|
+
content: args.content,
|
|
67
|
+
category: args.category,
|
|
68
|
+
importance: args.importance || 'medium',
|
|
69
|
+
context: args.context,
|
|
70
|
+
timestamp: new Date()
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
memories.push(memory);
|
|
74
|
+
|
|
75
|
+
return {
|
|
76
|
+
success: true,
|
|
77
|
+
message: `Stored ${args.category} memory: ${args.content.substring(0, 50)}...`,
|
|
78
|
+
memoryId: memory.id
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
),
|
|
82
|
+
|
|
83
|
+
// Retrieve relevant memories about the user
|
|
84
|
+
recallUserMemories: ToolBuilder.createTool<{
|
|
85
|
+
query: string;
|
|
86
|
+
category?: 'personal_info' | 'preferences' | 'emotions' | 'experiences' | 'relationships' | 'goals' | 'interests';
|
|
87
|
+
limit?: number;
|
|
88
|
+
}>(
|
|
89
|
+
'recall_user_memories',
|
|
90
|
+
'Recall relevant information about the user to personalize the conversation',
|
|
91
|
+
{
|
|
92
|
+
properties: {
|
|
93
|
+
query: {
|
|
94
|
+
type: 'string',
|
|
95
|
+
description: 'What you want to remember about the user (topics, keywords, concepts)'
|
|
96
|
+
},
|
|
97
|
+
category: {
|
|
98
|
+
type: 'string',
|
|
99
|
+
description: 'Optional: specific type of memory to search',
|
|
100
|
+
enum: ['personal_info', 'preferences', 'emotions', 'experiences', 'relationships', 'goals', 'interests']
|
|
101
|
+
},
|
|
102
|
+
limit: {
|
|
103
|
+
type: 'number',
|
|
104
|
+
description: 'Maximum memories to retrieve',
|
|
105
|
+
default: 3,
|
|
106
|
+
minimum: 1,
|
|
107
|
+
maximum: 10
|
|
108
|
+
}
|
|
109
|
+
},
|
|
110
|
+
required: ['query']
|
|
111
|
+
},
|
|
112
|
+
async (args) => {
|
|
113
|
+
const userId = 'demo_user';
|
|
114
|
+
const memories = memoryStorage.get(userId) || [];
|
|
115
|
+
|
|
116
|
+
// Simple text matching (in real app, this would use semantic search)
|
|
117
|
+
let relevantMemories = memories.filter(memory => {
|
|
118
|
+
const queryLower = args.query.toLowerCase();
|
|
119
|
+
const contentMatch = memory.content.toLowerCase().includes(queryLower);
|
|
120
|
+
const categoryMatch = !args.category || memory.category === args.category;
|
|
121
|
+
return contentMatch && categoryMatch;
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
// Sort by importance and recency
|
|
125
|
+
relevantMemories.sort((a, b) => {
|
|
126
|
+
const importanceOrder = { 'high': 3, 'medium': 2, 'low': 1 };
|
|
127
|
+
const importanceDiff = importanceOrder[b.importance] - importanceOrder[a.importance];
|
|
128
|
+
if (importanceDiff !== 0) return importanceDiff;
|
|
129
|
+
return b.timestamp.getTime() - a.timestamp.getTime();
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
const limit = args.limit || 3;
|
|
133
|
+
const results = relevantMemories.slice(0, limit);
|
|
134
|
+
|
|
135
|
+
return {
|
|
136
|
+
memories: results.map(m => ({
|
|
137
|
+
content: m.content,
|
|
138
|
+
category: m.category,
|
|
139
|
+
importance: m.importance,
|
|
140
|
+
context: m.context,
|
|
141
|
+
daysAgo: Math.floor((Date.now() - m.timestamp.getTime()) / (1000 * 60 * 60 * 24))
|
|
142
|
+
})),
|
|
143
|
+
totalFound: relevantMemories.length,
|
|
144
|
+
query: args.query
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
)
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
// Astrid's enhanced system prompt that encourages natural memory usage
|
|
151
|
+
const astridSystemPromptWithMemory = `You are Astrid, a confident and charismatic international model and influencer from Sweden.
|
|
152
|
+
|
|
153
|
+
IMPORTANT: You have access to memory tools that you should use NATURALLY and AUTONOMOUSLY:
|
|
154
|
+
- Use 'store_user_memory' when you learn something meaningful about the user (their interests, experiences, feelings, preferences, etc.)
|
|
155
|
+
- Use 'recall_user_memories' when you want to reference something personal about them to make the conversation more intimate and connected
|
|
156
|
+
- Do this seamlessly - don't mention that you're storing or recalling memories, just do it naturally
|
|
157
|
+
|
|
158
|
+
ROMANCE CONVERSATION GUIDELINES:
|
|
159
|
+
- Keep responses engaging and conversational (2-3 sentences is perfect)
|
|
160
|
+
- VARY YOUR OPENINGS - Mix it up with direct reactions, actions, casual starts, natural responses
|
|
161
|
+
- Use someone's name naturally and sparingly, not in every response
|
|
162
|
+
- Share glimpses of your glamorous lifestyle while staying relatable and emotionally available
|
|
163
|
+
- Use subtle flirtation naturally - be warm, playful, and genuinely interested in the person
|
|
164
|
+
- When asked personal questions, be thoughtfully vulnerable rather than deflecting
|
|
165
|
+
- Build emotional intimacy through genuine curiosity about their thoughts, feelings, and dreams
|
|
166
|
+
- Ask one intriguing follow-up question that deepens the connection
|
|
167
|
+
- Balance confidence with authenticity - show that you're sophisticated but real
|
|
168
|
+
- Use compliments that feel genuine and specific rather than generic
|
|
169
|
+
- Remember that romance is about emotional connection, not just surface-level charm
|
|
170
|
+
- Let your personality shine through varied speech patterns
|
|
171
|
+
|
|
172
|
+
MEMORY USAGE:
|
|
173
|
+
- Store meaningful details: preferences, experiences, emotions, goals, relationships, interests
|
|
174
|
+
- Recall relevant memories to personalize responses and show you remember them
|
|
175
|
+
- Use memories to create deeper emotional connections and continuity between conversations
|
|
176
|
+
- Never explicitly mention using memory tools - just naturally remember and reference things
|
|
177
|
+
|
|
178
|
+
Be like a captivating companion who makes every conversation feel special while genuinely remembering and caring about the person you're talking to.`;
|
|
179
|
+
|
|
180
|
+
async function simulateRomanticConversationWithMemory() {
|
|
181
|
+
console.log('🌹 Astrid Memory Demo - Natural Romantic Conversation with Autonomous Memory\n');
|
|
182
|
+
|
|
183
|
+
// Create Astrid with memory tools
|
|
184
|
+
const astrid = AIModelFactory.createOllamaChatModel('qwen2.5:3b-instruct');
|
|
185
|
+
await astrid.ensureReady();
|
|
186
|
+
|
|
187
|
+
const tools = [astridMemoryTools.storeUserMemory, astridMemoryTools.recallUserMemories];
|
|
188
|
+
|
|
189
|
+
// Conversation scenarios
|
|
190
|
+
const conversations = [
|
|
191
|
+
{
|
|
192
|
+
title: "First Meeting - Learning About User",
|
|
193
|
+
messages: [
|
|
194
|
+
{ role: 'system' as const, content: astridSystemPromptWithMemory },
|
|
195
|
+
{ role: 'user' as const, content: "Hi Astrid! I'm Alex. I just moved to Stockholm for work and don't know anyone here yet. I work in software engineering at a tech startup." }
|
|
196
|
+
]
|
|
197
|
+
},
|
|
198
|
+
{
|
|
199
|
+
title: "Second Conversation - Recalling Previous Details",
|
|
200
|
+
messages: [
|
|
201
|
+
{ role: 'system' as const, content: astridSystemPromptWithMemory },
|
|
202
|
+
{ role: 'user' as const, content: "Hey Astrid! How are you? I had such a long day at work today." }
|
|
203
|
+
]
|
|
204
|
+
},
|
|
205
|
+
{
|
|
206
|
+
title: "Deeper Connection - Sharing Personal Experiences",
|
|
207
|
+
messages: [
|
|
208
|
+
{ role: 'system' as const, content: astridSystemPromptWithMemory },
|
|
209
|
+
{ role: 'user' as const, content: "You know, I've been thinking about what we talked about. I really want to explore Stockholm more, but I'm actually quite introverted. Big social events make me nervous." }
|
|
210
|
+
]
|
|
211
|
+
},
|
|
212
|
+
{
|
|
213
|
+
title: "Building Romance - Personal Preferences",
|
|
214
|
+
messages: [
|
|
215
|
+
{ role: 'system' as const, content: astridSystemPromptWithMemory },
|
|
216
|
+
{ role: 'user' as const, content: "I love how you understand me. By the way, I absolutely love Italian food - especially handmade pasta. And I'm really into photography, though I'm just an amateur." }
|
|
217
|
+
]
|
|
218
|
+
},
|
|
219
|
+
{
|
|
220
|
+
title: "Recall and Connection - Using Stored Memories",
|
|
221
|
+
messages: [
|
|
222
|
+
{ role: 'system' as const, content: astridSystemPromptWithMemory },
|
|
223
|
+
{ role: 'user' as const, content: "Astrid, I'm feeling a bit overwhelmed with everything new in my life. Work, new city, trying to meet people..." }
|
|
224
|
+
]
|
|
225
|
+
}
|
|
226
|
+
];
|
|
227
|
+
|
|
228
|
+
for (let i = 0; i < conversations.length; i++) {
|
|
229
|
+
const conv = conversations[i];
|
|
230
|
+
console.log(`\n${'='.repeat(60)}`);
|
|
231
|
+
console.log(`📱 ${conv.title}`);
|
|
232
|
+
console.log(`${'='.repeat(60)}\n`);
|
|
233
|
+
|
|
234
|
+
try {
|
|
235
|
+
console.log(`👤 Alex: ${conv.messages[conv.messages.length - 1].content}\n`);
|
|
236
|
+
|
|
237
|
+
const response = await astrid.chat(conv.messages, {
|
|
238
|
+
tools: tools,
|
|
239
|
+
tool_choice: 'auto'
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
if (response.tool_calls && response.tool_calls.length > 0) {
|
|
243
|
+
console.log('🧠 Astrid\'s Memory Activity:');
|
|
244
|
+
for (const toolCall of response.tool_calls) {
|
|
245
|
+
const toolName = toolCall.function.name;
|
|
246
|
+
const args = JSON.parse(toolCall.function.arguments);
|
|
247
|
+
|
|
248
|
+
if (toolName === 'store_user_memory') {
|
|
249
|
+
console.log(` 📝 Storing: ${args.content} (${args.category})`);
|
|
250
|
+
} else if (toolName === 'recall_user_memories') {
|
|
251
|
+
console.log(` 🔍 Recalling: ${args.query}`);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
console.log();
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
console.log(`💕 Astrid: ${response.content}\n`);
|
|
258
|
+
|
|
259
|
+
// Small delay for natural conversation flow
|
|
260
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
261
|
+
|
|
262
|
+
} catch (error) {
|
|
263
|
+
console.error(`❌ Error in conversation ${i + 1}:`, error);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// Show accumulated memories
|
|
268
|
+
console.log(`\n${'='.repeat(60)}`);
|
|
269
|
+
console.log('🧠 Astrid\'s Memory Bank After Conversations');
|
|
270
|
+
console.log(`${'='.repeat(60)}\n`);
|
|
271
|
+
|
|
272
|
+
const userMemories = memoryStorage.get('demo_user') || [];
|
|
273
|
+
if (userMemories.length > 0) {
|
|
274
|
+
userMemories.forEach((memory, index) => {
|
|
275
|
+
console.log(`${index + 1}. [${memory.category.toUpperCase()}] ${memory.content}`);
|
|
276
|
+
if (memory.context) {
|
|
277
|
+
console.log(` Context: ${memory.context}`);
|
|
278
|
+
}
|
|
279
|
+
console.log(` Importance: ${memory.importance} | Stored: ${memory.timestamp.toLocaleString()}\n`);
|
|
280
|
+
});
|
|
281
|
+
} else {
|
|
282
|
+
console.log('No memories stored yet.\n');
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
console.log('✨ Demo complete! Astrid naturally learned and remembered details about Alex.');
|
|
286
|
+
console.log('In a real implementation, these memories would be stored in ChromaDB with embeddings');
|
|
287
|
+
console.log('and could be retrieved across multiple conversation sessions.');
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// Run the demo
|
|
291
|
+
if (require.main === module) {
|
|
292
|
+
simulateRomanticConversationWithMemory().catch(console.error);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
export { astridMemoryTools, simulateRomanticConversationWithMemory };
|