@aj-archipelago/cortex 1.3.21 → 1.3.23
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/README.md +64 -0
- package/config.js +26 -1
- package/helper-apps/cortex-realtime-voice-server/src/cortex/memory.ts +2 -2
- package/helper-apps/cortex-realtime-voice-server/src/realtime/client.ts +9 -4
- package/helper-apps/cortex-realtime-voice-server/src/realtime/realtimeTypes.ts +1 -0
- package/lib/util.js +5 -25
- package/package.json +5 -2
- package/pathways/system/entity/memory/shared/sys_memory_helpers.js +228 -0
- package/pathways/system/entity/memory/sys_memory_format.js +30 -0
- package/pathways/system/entity/memory/sys_memory_manager.js +85 -27
- package/pathways/system/entity/memory/sys_memory_process.js +154 -0
- package/pathways/system/entity/memory/sys_memory_required.js +4 -2
- package/pathways/system/entity/memory/sys_memory_topic.js +22 -0
- package/pathways/system/entity/memory/sys_memory_update.js +50 -150
- package/pathways/system/entity/memory/sys_read_memory.js +67 -69
- package/pathways/system/entity/memory/sys_save_memory.js +1 -1
- package/pathways/system/entity/memory/sys_search_memory.js +1 -1
- package/pathways/system/entity/sys_entity_start.js +9 -6
- package/pathways/system/entity/sys_generator_image.js +5 -41
- package/pathways/system/entity/sys_generator_memory.js +3 -1
- package/pathways/system/entity/sys_generator_reasoning.js +1 -1
- package/pathways/system/entity/sys_router_tool.js +3 -4
- package/pathways/system/rest_streaming/sys_claude_35_sonnet.js +1 -1
- package/pathways/system/rest_streaming/sys_claude_3_haiku.js +1 -1
- package/pathways/system/rest_streaming/sys_google_gemini_chat.js +1 -1
- package/pathways/system/rest_streaming/sys_ollama_chat.js +21 -0
- package/pathways/system/rest_streaming/sys_ollama_completion.js +14 -0
- package/pathways/system/rest_streaming/sys_openai_chat_o1.js +1 -1
- package/pathways/system/rest_streaming/sys_openai_chat_o3_mini.js +1 -1
- package/pathways/transcribe_gemini.js +525 -0
- package/server/modelExecutor.js +8 -0
- package/server/pathwayResolver.js +13 -8
- package/server/plugins/claude3VertexPlugin.js +150 -18
- package/server/plugins/gemini15ChatPlugin.js +90 -1
- package/server/plugins/gemini15VisionPlugin.js +16 -3
- package/server/plugins/modelPlugin.js +12 -9
- package/server/plugins/ollamaChatPlugin.js +158 -0
- package/server/plugins/ollamaCompletionPlugin.js +147 -0
- package/server/rest.js +70 -8
- package/tests/claude3VertexToolConversion.test.js +411 -0
- package/tests/memoryfunction.test.js +560 -46
- package/tests/multimodal_conversion.test.js +169 -0
- package/tests/openai_api.test.js +332 -0
- package/tests/transcribe_gemini.test.js +217 -0
|
@@ -0,0 +1,411 @@
|
|
|
1
|
+
import test from 'ava';
|
|
2
|
+
import Claude3VertexPlugin from '../server/plugins/claude3VertexPlugin.js';
|
|
3
|
+
import { mockPathwayResolverMessages } from './mocks.js';
|
|
4
|
+
import { config } from '../config.js';
|
|
5
|
+
|
|
6
|
+
const { pathway, modelName, model } = mockPathwayResolverMessages;
|
|
7
|
+
|
|
8
|
+
// Helper function to create a plugin instance
|
|
9
|
+
const createPlugin = () => new Claude3VertexPlugin(pathway, model);
|
|
10
|
+
|
|
11
|
+
// Test OpenAI tools block conversion
|
|
12
|
+
test('OpenAI tools block conversion', async (t) => {
|
|
13
|
+
const plugin = createPlugin();
|
|
14
|
+
const prompt = mockPathwayResolverMessages.pathway.prompt;
|
|
15
|
+
|
|
16
|
+
const parameters = {
|
|
17
|
+
tools: [
|
|
18
|
+
{
|
|
19
|
+
type: 'function',
|
|
20
|
+
function: {
|
|
21
|
+
name: 'get_weather',
|
|
22
|
+
description: 'Get current temperature for a given location.',
|
|
23
|
+
parameters: {
|
|
24
|
+
type: 'object',
|
|
25
|
+
properties: {
|
|
26
|
+
location: {
|
|
27
|
+
type: 'string',
|
|
28
|
+
description: 'City and country e.g. Bogotá, Colombia'
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
required: ['location'],
|
|
32
|
+
additionalProperties: false
|
|
33
|
+
},
|
|
34
|
+
strict: true
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
]
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
const cortexRequest = { tools: parameters.tools };
|
|
41
|
+
const result = await plugin.getRequestParameters('test', parameters, prompt, cortexRequest);
|
|
42
|
+
|
|
43
|
+
t.deepEqual(result.tools, [{
|
|
44
|
+
name: 'get_weather',
|
|
45
|
+
description: 'Get current temperature for a given location.',
|
|
46
|
+
input_schema: {
|
|
47
|
+
type: 'object',
|
|
48
|
+
properties: {
|
|
49
|
+
location: {
|
|
50
|
+
type: 'string',
|
|
51
|
+
description: 'City and country e.g. Bogotá, Colombia'
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
required: ['location']
|
|
55
|
+
}
|
|
56
|
+
}]);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
// Test tool call conversion without tools block
|
|
60
|
+
test('Tool call conversion without tools block', async (t) => {
|
|
61
|
+
const plugin = createPlugin();
|
|
62
|
+
const prompt = mockPathwayResolverMessages.pathway.prompt;
|
|
63
|
+
|
|
64
|
+
const messages = [
|
|
65
|
+
{
|
|
66
|
+
role: 'system',
|
|
67
|
+
content: 'You are a helpful assistant'
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
role: 'user',
|
|
71
|
+
content: 'What\'s in my memory?'
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
role: 'assistant',
|
|
75
|
+
content: [
|
|
76
|
+
{
|
|
77
|
+
type: 'tool_use',
|
|
78
|
+
id: 'tool_1',
|
|
79
|
+
name: 'memory_lookup',
|
|
80
|
+
input: 'search memory for relevant information'
|
|
81
|
+
}
|
|
82
|
+
]
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
role: 'user',
|
|
86
|
+
content: [
|
|
87
|
+
{
|
|
88
|
+
type: 'tool_result',
|
|
89
|
+
tool_use_id: 'tool_1',
|
|
90
|
+
content: 'Memory search results here'
|
|
91
|
+
}
|
|
92
|
+
]
|
|
93
|
+
}
|
|
94
|
+
];
|
|
95
|
+
|
|
96
|
+
// add messages to mockPrompt.messages
|
|
97
|
+
prompt.messages = messages;
|
|
98
|
+
|
|
99
|
+
const cortexRequest = {};
|
|
100
|
+
const result = await plugin.getRequestParameters('test', {}, prompt, cortexRequest);
|
|
101
|
+
|
|
102
|
+
// Check generated tools block
|
|
103
|
+
t.deepEqual(result.tools, [{
|
|
104
|
+
name: 'memory_lookup',
|
|
105
|
+
description: 'Tool for memory_lookup',
|
|
106
|
+
input_schema: {
|
|
107
|
+
type: 'object',
|
|
108
|
+
properties: {
|
|
109
|
+
query: {
|
|
110
|
+
type: 'string',
|
|
111
|
+
description: 'Parameter query for memory_lookup'
|
|
112
|
+
}
|
|
113
|
+
},
|
|
114
|
+
required: ['query']
|
|
115
|
+
}
|
|
116
|
+
}]);
|
|
117
|
+
|
|
118
|
+
// Check converted messages
|
|
119
|
+
t.is(result.messages[1].role, 'assistant');
|
|
120
|
+
t.deepEqual(result.messages[1].content[0], {
|
|
121
|
+
type: 'tool_use',
|
|
122
|
+
id: 'tool_1',
|
|
123
|
+
name: 'memory_lookup',
|
|
124
|
+
input: { query: 'search memory for relevant information' }
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
t.is(result.messages[2].role, 'user');
|
|
128
|
+
t.deepEqual(result.messages[2].content[0], {
|
|
129
|
+
type: 'tool_result',
|
|
130
|
+
tool_use_id: 'tool_1',
|
|
131
|
+
content: 'Memory search results here'
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
// Test multiple tool calls in conversation
|
|
136
|
+
test('Multiple tool calls in conversation', async (t) => {
|
|
137
|
+
const plugin = createPlugin();
|
|
138
|
+
const prompt = mockPathwayResolverMessages.pathway.prompt;
|
|
139
|
+
|
|
140
|
+
const messages = [
|
|
141
|
+
{
|
|
142
|
+
role: 'system',
|
|
143
|
+
content: 'You are a helpful assistant'
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
role: 'user',
|
|
147
|
+
content: 'What\'s in my memory and what\'s the weather in San Francisco?'
|
|
148
|
+
},
|
|
149
|
+
{
|
|
150
|
+
role: 'assistant',
|
|
151
|
+
content: [
|
|
152
|
+
{
|
|
153
|
+
type: 'tool_use',
|
|
154
|
+
id: 'tool_1',
|
|
155
|
+
name: 'memory_lookup',
|
|
156
|
+
input: { query: 'search memory' }
|
|
157
|
+
}
|
|
158
|
+
]
|
|
159
|
+
},
|
|
160
|
+
{
|
|
161
|
+
role: 'user',
|
|
162
|
+
content: [
|
|
163
|
+
{
|
|
164
|
+
type: 'tool_result',
|
|
165
|
+
tool_use_id: 'tool_1',
|
|
166
|
+
content: 'Memory results'
|
|
167
|
+
}
|
|
168
|
+
]
|
|
169
|
+
},
|
|
170
|
+
{
|
|
171
|
+
role: 'assistant',
|
|
172
|
+
content: [
|
|
173
|
+
{
|
|
174
|
+
type: 'tool_use',
|
|
175
|
+
id: 'tool_2',
|
|
176
|
+
name: 'weather_lookup',
|
|
177
|
+
input: { location: 'San Francisco' }
|
|
178
|
+
}
|
|
179
|
+
]
|
|
180
|
+
},
|
|
181
|
+
{
|
|
182
|
+
role: 'user',
|
|
183
|
+
content: [
|
|
184
|
+
{
|
|
185
|
+
type: 'tool_result',
|
|
186
|
+
tool_use_id: 'tool_2',
|
|
187
|
+
content: 'Weather results'
|
|
188
|
+
}
|
|
189
|
+
]
|
|
190
|
+
}
|
|
191
|
+
];
|
|
192
|
+
|
|
193
|
+
// add messages to mockPrompt.messages
|
|
194
|
+
prompt.messages = messages;
|
|
195
|
+
|
|
196
|
+
const cortexRequest = { messages };
|
|
197
|
+
const result = await plugin.getRequestParameters('test', {}, prompt, cortexRequest);
|
|
198
|
+
|
|
199
|
+
// Check that both tools are in the tools block
|
|
200
|
+
t.truthy(result.tools, 'Tools should be defined');
|
|
201
|
+
t.is(result.tools.length, 2, 'Should have 2 tools');
|
|
202
|
+
t.deepEqual(result.tools.map(t => t.name).sort(), ['memory_lookup', 'weather_lookup']);
|
|
203
|
+
|
|
204
|
+
// Check all messages are converted correctly
|
|
205
|
+
t.is(result.messages.length, 5);
|
|
206
|
+
t.deepEqual(result.messages[1].content[0].input, { query: 'search memory' });
|
|
207
|
+
t.deepEqual(result.messages[3].content[0].input, { location: 'San Francisco' });
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
// Test mixed conversation with tools and regular messages
|
|
211
|
+
test('Mixed conversation with tools and regular messages', async (t) => {
|
|
212
|
+
const plugin = createPlugin();
|
|
213
|
+
const prompt = mockPathwayResolverMessages.pathway.prompt;
|
|
214
|
+
|
|
215
|
+
const messages = [
|
|
216
|
+
{
|
|
217
|
+
role: 'system',
|
|
218
|
+
content: 'You are a helpful assistant'
|
|
219
|
+
},
|
|
220
|
+
{
|
|
221
|
+
role: 'user',
|
|
222
|
+
content: 'What\'s in my memory?'
|
|
223
|
+
},
|
|
224
|
+
{
|
|
225
|
+
role: 'assistant',
|
|
226
|
+
content: [
|
|
227
|
+
{
|
|
228
|
+
type: 'tool_use',
|
|
229
|
+
id: 'tool_1',
|
|
230
|
+
name: 'memory_lookup',
|
|
231
|
+
input: 'search memory'
|
|
232
|
+
}
|
|
233
|
+
]
|
|
234
|
+
},
|
|
235
|
+
{
|
|
236
|
+
role: 'user',
|
|
237
|
+
content: [
|
|
238
|
+
{
|
|
239
|
+
type: 'tool_result',
|
|
240
|
+
tool_use_id: 'tool_1',
|
|
241
|
+
content: 'Memory results here'
|
|
242
|
+
}
|
|
243
|
+
]
|
|
244
|
+
}
|
|
245
|
+
];
|
|
246
|
+
|
|
247
|
+
// add messages to mockPrompt.messages
|
|
248
|
+
prompt.messages = messages;
|
|
249
|
+
|
|
250
|
+
const cortexRequest = { messages };
|
|
251
|
+
const result = await plugin.getRequestParameters('test', {}, prompt, cortexRequest);
|
|
252
|
+
|
|
253
|
+
// Check system message
|
|
254
|
+
t.is(result.system, 'You are a helpful assistant');
|
|
255
|
+
|
|
256
|
+
// Check regular messages and tool messages are converted correctly
|
|
257
|
+
t.is(result.messages.length, 3);
|
|
258
|
+
t.deepEqual(result.messages[0].content[0], { type: 'text', text: 'What\'s in my memory?' });
|
|
259
|
+
t.deepEqual(result.messages[1].content[0].input, { query: 'search memory' });
|
|
260
|
+
t.deepEqual(result.messages[2].content[0], {
|
|
261
|
+
type: 'tool_result',
|
|
262
|
+
tool_use_id: 'tool_1',
|
|
263
|
+
content: 'Memory results here'
|
|
264
|
+
});
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
// Test edge cases
|
|
268
|
+
test('Tool conversion edge cases', async (t) => {
|
|
269
|
+
const plugin = createPlugin();
|
|
270
|
+
const prompt = mockPathwayResolverMessages.pathway.prompt;
|
|
271
|
+
|
|
272
|
+
const messages = [
|
|
273
|
+
{
|
|
274
|
+
role: 'system',
|
|
275
|
+
content: 'You are a helpful assistant'
|
|
276
|
+
},
|
|
277
|
+
{
|
|
278
|
+
role: 'user',
|
|
279
|
+
content: 'What\'s in my memory?'
|
|
280
|
+
},
|
|
281
|
+
// Empty tool use
|
|
282
|
+
{
|
|
283
|
+
role: 'assistant',
|
|
284
|
+
content: [
|
|
285
|
+
{
|
|
286
|
+
type: 'tool_use',
|
|
287
|
+
id: 'tool_1',
|
|
288
|
+
name: 'empty_tool',
|
|
289
|
+
input: {}
|
|
290
|
+
}
|
|
291
|
+
]
|
|
292
|
+
},
|
|
293
|
+
// Null input
|
|
294
|
+
{
|
|
295
|
+
role: 'assistant',
|
|
296
|
+
content: [
|
|
297
|
+
{
|
|
298
|
+
type: 'tool_use',
|
|
299
|
+
id: 'tool_2',
|
|
300
|
+
name: 'null_tool',
|
|
301
|
+
input: null
|
|
302
|
+
}
|
|
303
|
+
]
|
|
304
|
+
},
|
|
305
|
+
// Missing tool_use_id
|
|
306
|
+
{
|
|
307
|
+
role: 'user',
|
|
308
|
+
content: [
|
|
309
|
+
{
|
|
310
|
+
type: 'tool_result',
|
|
311
|
+
content: 'Result without ID'
|
|
312
|
+
}
|
|
313
|
+
]
|
|
314
|
+
},
|
|
315
|
+
// Empty tool result
|
|
316
|
+
{
|
|
317
|
+
role: 'user',
|
|
318
|
+
content: [
|
|
319
|
+
{
|
|
320
|
+
type: 'tool_result',
|
|
321
|
+
tool_use_id: 'tool_3',
|
|
322
|
+
content: ''
|
|
323
|
+
}
|
|
324
|
+
]
|
|
325
|
+
}
|
|
326
|
+
];
|
|
327
|
+
|
|
328
|
+
// add messages to mockPrompt.messages
|
|
329
|
+
prompt.messages = messages;
|
|
330
|
+
|
|
331
|
+
const cortexRequest = { messages };
|
|
332
|
+
const result = await plugin.getRequestParameters('test', {}, prompt, cortexRequest);
|
|
333
|
+
|
|
334
|
+
// Check tools block handles empty/null inputs
|
|
335
|
+
t.truthy(result.tools, 'Tools should be defined');
|
|
336
|
+
t.truthy(result.tools.find(t => t.name === 'empty_tool'), 'Should have empty_tool');
|
|
337
|
+
t.truthy(result.tools.find(t => t.name === 'null_tool'), 'Should have null_tool');
|
|
338
|
+
t.deepEqual(result.tools.find(t => t.name === 'empty_tool').input_schema.properties, {});
|
|
339
|
+
t.deepEqual(result.tools.find(t => t.name === 'null_tool').input_schema.properties, {});
|
|
340
|
+
|
|
341
|
+
// Check messages are converted without crashing
|
|
342
|
+
t.is(result.messages.length, 3);
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
// Test combining existing tools block with generated tools
|
|
346
|
+
test('Combining existing tools block with generated tools', async (t) => {
|
|
347
|
+
const plugin = createPlugin();
|
|
348
|
+
const prompt = mockPathwayResolverMessages.pathway.prompt;
|
|
349
|
+
|
|
350
|
+
const parameters = {
|
|
351
|
+
tools: [
|
|
352
|
+
{
|
|
353
|
+
type: 'function',
|
|
354
|
+
function: {
|
|
355
|
+
name: 'get_weather',
|
|
356
|
+
description: 'Get weather',
|
|
357
|
+
parameters: {
|
|
358
|
+
type: 'object',
|
|
359
|
+
properties: {
|
|
360
|
+
location: { type: 'string' }
|
|
361
|
+
},
|
|
362
|
+
required: ['location']
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
]
|
|
367
|
+
};
|
|
368
|
+
|
|
369
|
+
const messages = [
|
|
370
|
+
{
|
|
371
|
+
role: 'system',
|
|
372
|
+
content: 'You are a helpful assistant'
|
|
373
|
+
},
|
|
374
|
+
{
|
|
375
|
+
role: 'user',
|
|
376
|
+
content: 'What\'s in my memory?'
|
|
377
|
+
},
|
|
378
|
+
{
|
|
379
|
+
role: 'assistant',
|
|
380
|
+
content: [
|
|
381
|
+
{
|
|
382
|
+
type: 'tool_use',
|
|
383
|
+
id: 'tool_1',
|
|
384
|
+
name: 'memory_lookup',
|
|
385
|
+
input: 'search memory'
|
|
386
|
+
}
|
|
387
|
+
]
|
|
388
|
+
},
|
|
389
|
+
{
|
|
390
|
+
role: 'user',
|
|
391
|
+
content: [
|
|
392
|
+
{
|
|
393
|
+
type: 'tool_result',
|
|
394
|
+
tool_use_id: 'tool_1',
|
|
395
|
+
content: 'Memory results'
|
|
396
|
+
}
|
|
397
|
+
]
|
|
398
|
+
}
|
|
399
|
+
];
|
|
400
|
+
|
|
401
|
+
// add messages to mockPrompt.messages
|
|
402
|
+
prompt.messages = messages;
|
|
403
|
+
|
|
404
|
+
const cortexRequest = { messages };
|
|
405
|
+
const result = await plugin.getRequestParameters('test', parameters, prompt, cortexRequest);
|
|
406
|
+
|
|
407
|
+
// Check both tools are present
|
|
408
|
+
t.truthy(result.tools, 'Tools should be defined');
|
|
409
|
+
t.is(result.tools.length, 2, 'Should have 2 tools');
|
|
410
|
+
t.deepEqual(result.tools.map(t => t.name).sort(), ['get_weather', 'memory_lookup']);
|
|
411
|
+
});
|