@aj-archipelago/cortex 1.3.21 → 1.3.22
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/helper-apps/cortex-realtime-voice-server/src/cortex/memory.ts +2 -2
- package/lib/util.js +1 -1
- package/package.json +1 -1
- 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_openai_chat_o1.js +1 -1
- package/pathways/system/rest_streaming/sys_openai_chat_o3_mini.js +1 -1
- package/pathways/transcribe_gemini.js +397 -0
- package/server/pathwayResolver.js +7 -7
- package/server/plugins/claude3VertexPlugin.js +109 -3
- package/server/plugins/gemini15VisionPlugin.js +7 -0
- package/server/plugins/modelPlugin.js +1 -1
- package/server/rest.js +24 -3
- package/tests/claude3VertexToolConversion.test.js +411 -0
- package/tests/memoryfunction.test.js +560 -46
- package/tests/openai_api.test.js +332 -0
package/tests/openai_api.test.js
CHANGED
|
@@ -60,6 +60,36 @@ test('POST /chat/completions', async (t) => {
|
|
|
60
60
|
t.true(Array.isArray(response.body.choices));
|
|
61
61
|
});
|
|
62
62
|
|
|
63
|
+
test('POST /chat/completions with multimodal content', async (t) => {
|
|
64
|
+
const response = await got.post(`${API_BASE}/chat/completions`, {
|
|
65
|
+
json: {
|
|
66
|
+
model: 'claude-3.5-sonnet',
|
|
67
|
+
messages: [{
|
|
68
|
+
role: 'user',
|
|
69
|
+
content: [
|
|
70
|
+
{
|
|
71
|
+
type: 'text',
|
|
72
|
+
text: 'What do you see in this image?'
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
type: 'image',
|
|
76
|
+
image_url: {
|
|
77
|
+
url: 'data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEASABIAAD/4gHYSUNDX1BST0ZJTEUAAQEAAAHIAAAAAAQwAABtbnRyUkdCIFhZWiAH4AABAAEAAAAAAABhY3NwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAA9tYAAQAAAADTLQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAlkZXNjAAAA8AAAACRyWFlaAAABFAAAABRnWFlaAAABKAAAABRiWFlaAAABPAAAABR3dHB0AAABUAAAABRyVFJDAAABZAAAAChnVFJDAAABZAAAAChiVFJDAAABZAAAAChjcHJ0AAABjAAAADxtbHVjAAAAAAAAAAEAAAAMZW5VUwAAAAgAAAAcAHMAUgBHAEJYWVogAAAAAAAAb6IAADj1AAADkFhZWiAAAAAAAABimQAAt4UAABjaWFlaIAAAAAAAACSgAAAPhAAAts9YWVogAAAAAAAA9tYAAQAAAADTLXBhcmEAAAAAAAQAAAACZmYAAPKnAAANWQAAE9AAAApbAAAAAAAAAABtbHVjAAAAAAAAAAEAAAAMZW5VUwAAACAAAAAcAEcAbwBvAGcAbABlACAASQBuAGMALgAgADIAMAAxADb/2wBDABQODxIPDRQSEBIXFRQdHx4eHRoaHSQrJyEwPENBMDQ4NDQ0QUJCSkNLS0tNSkpQUFFQR1BTYWNgY2FQYWFQYWj/2wBDARUXFyAeIBohHh4oIiE2LCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCj/wAARCAAIAAoDASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAb/xAAUEAEAAAAAAAAAAAAAAAAAAAAA/8QAFQEBAQAAAAAAAAAAAAAAAAAAAAX/xAAUEQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIRAxEAPwCdABmX/9k='
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
]
|
|
81
|
+
}],
|
|
82
|
+
stream: false,
|
|
83
|
+
},
|
|
84
|
+
responseType: 'json',
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
t.is(response.statusCode, 200);
|
|
88
|
+
t.is(response.body.object, 'chat.completion');
|
|
89
|
+
t.true(Array.isArray(response.body.choices));
|
|
90
|
+
t.truthy(response.body.choices[0].message.content);
|
|
91
|
+
});
|
|
92
|
+
|
|
63
93
|
async function connectToSSEEndpoint(url, endpoint, payload, t, customAssertions) {
|
|
64
94
|
return new Promise(async (resolve, reject) => {
|
|
65
95
|
try {
|
|
@@ -143,5 +173,307 @@ test('POST SSE: /v1/chat/completions should send a series of events and a [DONE]
|
|
|
143
173
|
};
|
|
144
174
|
|
|
145
175
|
await connectToSSEEndpoint(url, '/chat/completions', payload, t, chatCompletionsAssertions);
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
test('POST SSE: /v1/chat/completions with multimodal content should send a series of events and a [DONE] event', async (t) => {
|
|
179
|
+
const payload = {
|
|
180
|
+
model: 'claude-3.5-sonnet',
|
|
181
|
+
messages: [{
|
|
182
|
+
role: 'user',
|
|
183
|
+
content: [
|
|
184
|
+
{
|
|
185
|
+
type: 'text',
|
|
186
|
+
text: 'What do you see in this image?'
|
|
187
|
+
},
|
|
188
|
+
{
|
|
189
|
+
type: 'image',
|
|
190
|
+
image_url: {
|
|
191
|
+
url: 'data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEASABIAAD/4gHYSUNDX1BST0ZJTEUAAQEAAAHIAAAAAAQwAABtbnRyUkdCIFhZWiAH4AABAAEAAAAAAABhY3NwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAA9tYAAQAAAADTLQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAlkZXNjAAAA8AAAACRyWFlaAAABFAAAABRnWFlaAAABKAAAABRiWFlaAAABPAAAABR3dHB0AAABUAAAABRyVFJDAAABZAAAAChnVFJDAAABZAAAAChiVFJDAAABZAAAAChjcHJ0AAABjAAAADxtbHVjAAAAAAAAAAEAAAAMZW5VUwAAAAgAAAAcAHMAUgBHAEJYWVogAAAAAAAAb6IAADj1AAADkFhZWiAAAAAAAABimQAAt4UAABjaWFlaIAAAAAAAACSgAAAPhAAAts9YWVogAAAAAAAA9tYAAQAAAADTLXBhcmEAAAAAAAQAAAACZmYAAPKnAAANWQAAE9AAAApbAAAAAAAAAABtbHVjAAAAAAAAAAEAAAAMZW5VUwAAACAAAAAcAEcAbwBvAGcAbABlACAASQBuAGMALgAgADIAMAAxADb/2wBDABQODxIPDRQSEBIXFRQdHx4eHRoaHSQrJyEwPENBMDQ4NDQ0QUJCSkNLS0tNSkpQUFFQR1BTYWNgY2FQYWFQYWj/2wBDARUXFyAeIBohHh4oIiE2LCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCj/wAARCAAIAAoDASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAb/xAAUEAEAAAAAAAAAAAAAAAAAAAAA/8QAFQEBAQAAAAAAAAAAAAAAAAAAAAX/xAAUEQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIRAxEAPwCdABmX/9k='
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
]
|
|
195
|
+
}],
|
|
196
|
+
stream: true,
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
const url = `http://localhost:${process.env.CORTEX_PORT}/v1`;
|
|
200
|
+
|
|
201
|
+
const multimodalChatCompletionsAssertions = (t, messageJson) => {
|
|
202
|
+
t.truthy(messageJson.id);
|
|
203
|
+
t.is(messageJson.object, 'chat.completion.chunk');
|
|
204
|
+
t.truthy(messageJson.choices[0].delta);
|
|
205
|
+
if (messageJson.choices[0].finish_reason === 'stop') {
|
|
206
|
+
t.truthy(messageJson.choices[0].delta);
|
|
207
|
+
}
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
await connectToSSEEndpoint(url, '/chat/completions', payload, t, multimodalChatCompletionsAssertions);
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
test('POST /chat/completions should handle multimodal content for non-multimodal model', async (t) => {
|
|
214
|
+
const response = await got.post(`${API_BASE}/chat/completions`, {
|
|
215
|
+
json: {
|
|
216
|
+
model: 'gpt-3.5-turbo',
|
|
217
|
+
messages: [{
|
|
218
|
+
role: 'user',
|
|
219
|
+
content: [
|
|
220
|
+
{
|
|
221
|
+
type: 'text',
|
|
222
|
+
text: 'What do you see in this image?'
|
|
223
|
+
},
|
|
224
|
+
{
|
|
225
|
+
type: 'image',
|
|
226
|
+
image_url: {
|
|
227
|
+
url: 'data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEASABIAAD...'
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
]
|
|
231
|
+
}],
|
|
232
|
+
stream: false,
|
|
233
|
+
},
|
|
234
|
+
responseType: 'json',
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
t.is(response.statusCode, 200);
|
|
238
|
+
t.is(response.body.object, 'chat.completion');
|
|
239
|
+
t.true(Array.isArray(response.body.choices));
|
|
240
|
+
t.truthy(response.body.choices[0].message.content);
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
test('POST SSE: /v1/chat/completions should handle streaming multimodal content for non-multimodal model', async (t) => {
|
|
244
|
+
const payload = {
|
|
245
|
+
model: 'gpt-3.5-turbo',
|
|
246
|
+
messages: [{
|
|
247
|
+
role: 'user',
|
|
248
|
+
content: [
|
|
249
|
+
{
|
|
250
|
+
type: 'text',
|
|
251
|
+
text: 'What do you see in this image?'
|
|
252
|
+
},
|
|
253
|
+
{
|
|
254
|
+
type: 'image',
|
|
255
|
+
image_url: {
|
|
256
|
+
url: 'data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEASABIAAD...'
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
]
|
|
260
|
+
}],
|
|
261
|
+
stream: true,
|
|
262
|
+
};
|
|
263
|
+
|
|
264
|
+
const streamingAssertions = (t, messageJson) => {
|
|
265
|
+
t.truthy(messageJson.id);
|
|
266
|
+
t.is(messageJson.object, 'chat.completion.chunk');
|
|
267
|
+
t.truthy(messageJson.choices[0].delta);
|
|
268
|
+
if (messageJson.choices[0].finish_reason === 'stop') {
|
|
269
|
+
t.truthy(messageJson.choices[0].delta);
|
|
270
|
+
}
|
|
271
|
+
};
|
|
272
|
+
|
|
273
|
+
await connectToSSEEndpoint(
|
|
274
|
+
`http://localhost:${process.env.CORTEX_PORT}/v1`,
|
|
275
|
+
'/chat/completions',
|
|
276
|
+
payload,
|
|
277
|
+
t,
|
|
278
|
+
streamingAssertions
|
|
279
|
+
);
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
test('POST /chat/completions should handle malformed multimodal content', async (t) => {
|
|
283
|
+
const response = await got.post(`${API_BASE}/chat/completions`, {
|
|
284
|
+
json: {
|
|
285
|
+
model: 'claude-3.5-sonnet',
|
|
286
|
+
messages: [{
|
|
287
|
+
role: 'user',
|
|
288
|
+
content: [
|
|
289
|
+
{
|
|
290
|
+
type: 'text',
|
|
291
|
+
// Missing text field
|
|
292
|
+
},
|
|
293
|
+
{
|
|
294
|
+
type: 'image',
|
|
295
|
+
// Missing image_url
|
|
296
|
+
}
|
|
297
|
+
]
|
|
298
|
+
}],
|
|
299
|
+
stream: false,
|
|
300
|
+
},
|
|
301
|
+
responseType: 'json',
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
t.is(response.statusCode, 200);
|
|
305
|
+
t.is(response.body.object, 'chat.completion');
|
|
306
|
+
t.true(Array.isArray(response.body.choices));
|
|
307
|
+
t.truthy(response.body.choices[0].message.content);
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
test('POST /chat/completions should handle invalid image data', async (t) => {
|
|
311
|
+
const response = await got.post(`${API_BASE}/chat/completions`, {
|
|
312
|
+
json: {
|
|
313
|
+
model: 'claude-3.5-sonnet',
|
|
314
|
+
messages: [{
|
|
315
|
+
role: 'user',
|
|
316
|
+
content: [
|
|
317
|
+
{
|
|
318
|
+
type: 'text',
|
|
319
|
+
text: 'What do you see in this image?'
|
|
320
|
+
},
|
|
321
|
+
{
|
|
322
|
+
type: 'image',
|
|
323
|
+
image_url: {
|
|
324
|
+
url: 'not-a-valid-base64-image'
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
]
|
|
328
|
+
}],
|
|
329
|
+
stream: false,
|
|
330
|
+
},
|
|
331
|
+
responseType: 'json',
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
t.is(response.statusCode, 200);
|
|
335
|
+
t.is(response.body.object, 'chat.completion');
|
|
336
|
+
t.true(Array.isArray(response.body.choices));
|
|
337
|
+
t.truthy(response.body.choices[0].message.content);
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
test('POST /completions should handle model parameters', async (t) => {
|
|
341
|
+
const response = await got.post(`${API_BASE}/completions`, {
|
|
342
|
+
json: {
|
|
343
|
+
model: 'gpt-3.5-turbo',
|
|
344
|
+
prompt: 'Say this is a test',
|
|
345
|
+
temperature: 0.7,
|
|
346
|
+
max_tokens: 100,
|
|
347
|
+
top_p: 1,
|
|
348
|
+
frequency_penalty: 0,
|
|
349
|
+
presence_penalty: 0,
|
|
350
|
+
stream: false,
|
|
351
|
+
},
|
|
352
|
+
responseType: 'json',
|
|
353
|
+
});
|
|
354
|
+
|
|
355
|
+
t.is(response.statusCode, 200);
|
|
356
|
+
t.is(response.body.object, 'text_completion');
|
|
357
|
+
t.true(Array.isArray(response.body.choices));
|
|
358
|
+
t.truthy(response.body.choices[0].text);
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
test('POST /chat/completions should handle function calling', async (t) => {
|
|
362
|
+
const response = await got.post(`${API_BASE}/chat/completions`, {
|
|
363
|
+
json: {
|
|
364
|
+
model: 'gpt-3.5-turbo',
|
|
365
|
+
messages: [{ role: 'user', content: 'What is the weather in Boston?' }],
|
|
366
|
+
functions: [{
|
|
367
|
+
name: 'get_weather',
|
|
368
|
+
description: 'Get the current weather in a given location',
|
|
369
|
+
parameters: {
|
|
370
|
+
type: 'object',
|
|
371
|
+
properties: {
|
|
372
|
+
location: {
|
|
373
|
+
type: 'string',
|
|
374
|
+
description: 'The city and state, e.g. San Francisco, CA'
|
|
375
|
+
},
|
|
376
|
+
unit: {
|
|
377
|
+
type: 'string',
|
|
378
|
+
enum: ['celsius', 'fahrenheit']
|
|
379
|
+
}
|
|
380
|
+
},
|
|
381
|
+
required: ['location']
|
|
382
|
+
}
|
|
383
|
+
}],
|
|
384
|
+
stream: false,
|
|
385
|
+
},
|
|
386
|
+
responseType: 'json',
|
|
387
|
+
});
|
|
388
|
+
|
|
389
|
+
t.is(response.statusCode, 200);
|
|
390
|
+
t.is(response.body.object, 'chat.completion');
|
|
391
|
+
t.true(Array.isArray(response.body.choices));
|
|
392
|
+
const choice = response.body.choices[0];
|
|
393
|
+
t.true(['function_call', 'stop'].includes(choice.finish_reason));
|
|
394
|
+
if (choice.finish_reason === 'function_call') {
|
|
395
|
+
t.truthy(choice.message.function_call);
|
|
396
|
+
t.truthy(choice.message.function_call.name);
|
|
397
|
+
t.truthy(choice.message.function_call.arguments);
|
|
398
|
+
}
|
|
399
|
+
});
|
|
400
|
+
|
|
401
|
+
test('POST /chat/completions should validate response format', async (t) => {
|
|
402
|
+
const response = await got.post(`${API_BASE}/chat/completions`, {
|
|
403
|
+
json: {
|
|
404
|
+
model: 'gpt-3.5-turbo',
|
|
405
|
+
messages: [{ role: 'user', content: 'Hello!' }],
|
|
406
|
+
stream: false,
|
|
407
|
+
},
|
|
408
|
+
responseType: 'json',
|
|
409
|
+
});
|
|
410
|
+
|
|
411
|
+
t.is(response.statusCode, 200);
|
|
412
|
+
t.is(response.body.object, 'chat.completion');
|
|
413
|
+
t.true(Array.isArray(response.body.choices));
|
|
414
|
+
t.truthy(response.body.id);
|
|
415
|
+
t.truthy(response.body.created);
|
|
416
|
+
t.truthy(response.body.model);
|
|
417
|
+
|
|
418
|
+
const choice = response.body.choices[0];
|
|
419
|
+
t.is(typeof choice.index, 'number');
|
|
420
|
+
t.truthy(choice.message);
|
|
421
|
+
t.truthy(choice.message.role);
|
|
422
|
+
t.truthy(choice.message.content);
|
|
423
|
+
t.truthy(choice.finish_reason);
|
|
424
|
+
});
|
|
425
|
+
|
|
426
|
+
test('POST /chat/completions should handle system messages', async (t) => {
|
|
427
|
+
const response = await got.post(`${API_BASE}/chat/completions`, {
|
|
428
|
+
json: {
|
|
429
|
+
model: 'gpt-3.5-turbo',
|
|
430
|
+
messages: [
|
|
431
|
+
{ role: 'system', content: 'You are a helpful assistant.' },
|
|
432
|
+
{ role: 'user', content: 'Hello!' }
|
|
433
|
+
],
|
|
434
|
+
stream: false,
|
|
435
|
+
},
|
|
436
|
+
responseType: 'json',
|
|
437
|
+
});
|
|
438
|
+
|
|
439
|
+
t.is(response.statusCode, 200);
|
|
440
|
+
t.is(response.body.object, 'chat.completion');
|
|
441
|
+
t.true(Array.isArray(response.body.choices));
|
|
442
|
+
t.truthy(response.body.choices[0].message.content);
|
|
443
|
+
});
|
|
444
|
+
|
|
445
|
+
test('POST /chat/completions should handle errors gracefully', async (t) => {
|
|
446
|
+
const response = await got.post(`${API_BASE}/chat/completions`, {
|
|
447
|
+
json: {
|
|
448
|
+
// Missing required model field
|
|
449
|
+
messages: [{ role: 'user', content: 'Hello!' }],
|
|
450
|
+
},
|
|
451
|
+
responseType: 'json',
|
|
452
|
+
});
|
|
453
|
+
|
|
454
|
+
t.is(response.statusCode, 200);
|
|
455
|
+
t.is(response.body.object, 'chat.completion');
|
|
456
|
+
t.true(Array.isArray(response.body.choices));
|
|
457
|
+
t.truthy(response.body.choices[0].message.content);
|
|
458
|
+
});
|
|
459
|
+
|
|
460
|
+
test('POST /chat/completions should handle token limits', async (t) => {
|
|
461
|
+
const response = await got.post(`${API_BASE}/chat/completions`, {
|
|
462
|
+
json: {
|
|
463
|
+
model: 'gpt-3.5-turbo',
|
|
464
|
+
messages: [{
|
|
465
|
+
role: 'user',
|
|
466
|
+
content: 'Hello!'.repeat(5000) // Very long message
|
|
467
|
+
}],
|
|
468
|
+
max_tokens: 100,
|
|
469
|
+
stream: false,
|
|
470
|
+
},
|
|
471
|
+
responseType: 'json',
|
|
472
|
+
});
|
|
473
|
+
|
|
474
|
+
t.is(response.statusCode, 200);
|
|
475
|
+
t.is(response.body.object, 'chat.completion');
|
|
476
|
+
t.true(Array.isArray(response.body.choices));
|
|
477
|
+
t.truthy(response.body.choices[0].message.content);
|
|
146
478
|
});
|
|
147
479
|
|