@aj-archipelago/cortex 1.3.35 → 1.3.36
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 +9 -9
- package/config/default.example.json +0 -20
- package/config.js +160 -6
- package/lib/pathwayTools.js +79 -1
- package/lib/requestExecutor.js +3 -1
- package/lib/util.js +7 -0
- package/package.json +1 -1
- package/pathways/basePathway.js +2 -0
- package/pathways/call_tools.js +379 -0
- package/pathways/system/entity/memory/shared/sys_memory_helpers.js +1 -1
- package/pathways/system/entity/memory/sys_search_memory.js +2 -2
- package/pathways/system/entity/sys_entity_agent.js +289 -0
- package/pathways/system/entity/sys_generator_memory.js +1 -1
- package/pathways/system/entity/sys_generator_results.js +1 -1
- package/pathways/system/entity/sys_get_entities.js +19 -0
- package/pathways/system/entity/tools/shared/sys_entity_tools.js +150 -0
- package/pathways/system/entity/tools/sys_tool_bing_search.js +147 -0
- package/pathways/system/entity/tools/sys_tool_callmodel.js +62 -0
- package/pathways/system/entity/tools/sys_tool_coding.js +53 -0
- package/pathways/system/entity/tools/sys_tool_codingagent.js +100 -0
- package/pathways/system/entity/tools/sys_tool_cognitive_search.js +231 -0
- package/pathways/system/entity/tools/sys_tool_image.js +57 -0
- package/pathways/system/entity/tools/sys_tool_readfile.js +119 -0
- package/pathways/system/entity/tools/sys_tool_reasoning.js +75 -0
- package/pathways/system/entity/tools/sys_tool_remember.js +59 -0
- package/pathways/vision.js +1 -1
- package/server/modelExecutor.js +4 -12
- package/server/pathwayResolver.js +53 -40
- package/server/plugins/azureBingPlugin.js +42 -4
- package/server/plugins/azureCognitivePlugin.js +40 -12
- package/server/plugins/claude3VertexPlugin.js +67 -18
- package/server/plugins/modelPlugin.js +3 -2
- package/server/plugins/openAiReasoningPlugin.js +3 -3
- package/server/plugins/openAiReasoningVisionPlugin.js +48 -0
- package/server/plugins/openAiVisionPlugin.js +192 -7
- package/tests/agentic.test.js +256 -0
- package/tests/call_tools.test.js +216 -0
- package/tests/claude3VertexToolConversion.test.js +78 -0
- package/tests/mocks.js +11 -3
- package/tests/multimodal_conversion.test.js +1 -1
- package/tests/openAiToolPlugin.test.js +242 -0
- package/pathways/test_palm_chat.js +0 -31
- package/server/plugins/palmChatPlugin.js +0 -233
- package/server/plugins/palmCodeCompletionPlugin.js +0 -45
- package/server/plugins/palmCompletionPlugin.js +0 -135
- package/tests/palmChatPlugin.test.js +0 -219
- package/tests/palmCompletionPlugin.test.js +0 -58
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
import test from 'ava';
|
|
2
|
+
import serverFactory from '../index.js';
|
|
3
|
+
|
|
4
|
+
let testServer;
|
|
5
|
+
|
|
6
|
+
// List of models to test - comment out models you don't want to test
|
|
7
|
+
const modelsToTest = [
|
|
8
|
+
'oai-gpt41-mini',
|
|
9
|
+
'claude-35-sonnet-vertex',
|
|
10
|
+
];
|
|
11
|
+
|
|
12
|
+
// Add timing data structure
|
|
13
|
+
const modelTimings = {};
|
|
14
|
+
|
|
15
|
+
// Helper function to track timing
|
|
16
|
+
const trackTiming = (model, startTime) => {
|
|
17
|
+
const duration = Date.now() - startTime;
|
|
18
|
+
if (!modelTimings[model]) {
|
|
19
|
+
modelTimings[model] = [];
|
|
20
|
+
}
|
|
21
|
+
modelTimings[model].push(duration);
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
// Helper function to calculate average timing
|
|
25
|
+
const calculateAverageTiming = (timings) => {
|
|
26
|
+
return timings.reduce((a, b) => a + b, 0) / timings.length;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
// Helper function to print model rankings
|
|
30
|
+
const printModelRankings = () => {
|
|
31
|
+
const averageTimings = Object.entries(modelTimings).map(([model, timings]) => ({
|
|
32
|
+
model,
|
|
33
|
+
avgTime: calculateAverageTiming(timings)
|
|
34
|
+
}));
|
|
35
|
+
|
|
36
|
+
averageTimings.sort((a, b) => a.avgTime - b.avgTime);
|
|
37
|
+
|
|
38
|
+
console.log('\nModel Performance Rankings:');
|
|
39
|
+
console.log('-------------------------');
|
|
40
|
+
averageTimings.forEach((entry, index) => {
|
|
41
|
+
console.log(`${index + 1}. ${entry.model}: ${Math.round(entry.avgTime)}ms average`);
|
|
42
|
+
});
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
// Modify runTestForModels to run tests sequentially
|
|
46
|
+
const runTestForModels = (testName, testFn) => {
|
|
47
|
+
for (const model of modelsToTest) {
|
|
48
|
+
test.serial(`${testName}-${model} (sequential)`, async t => {
|
|
49
|
+
console.log(`\nRunning ${testName} for ${model}...`);
|
|
50
|
+
const startTime = Date.now();
|
|
51
|
+
|
|
52
|
+
try {
|
|
53
|
+
await testFn(t, model);
|
|
54
|
+
trackTiming(model, startTime);
|
|
55
|
+
console.log(`✓ ${model} completed in ${Date.now() - startTime}ms`);
|
|
56
|
+
} catch (error) {
|
|
57
|
+
console.log(`✗ ${model} failed after ${Date.now() - startTime}ms`);
|
|
58
|
+
console.error(error);
|
|
59
|
+
throw error; // Re-throw to fail the test
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
test.before(async () => {
|
|
66
|
+
const { server, startServer } = await serverFactory();
|
|
67
|
+
startServer && await startServer();
|
|
68
|
+
testServer = server;
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
test.after.always('cleanup', async () => {
|
|
72
|
+
if (testServer) {
|
|
73
|
+
await testServer.stop();
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
// Add after.always hook to print rankings
|
|
78
|
+
test.after.always('print rankings', async () => {
|
|
79
|
+
printModelRankings();
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
// Test basic tool calling with a search request
|
|
83
|
+
runTestForModels('call_tools handles search request correctly', async (t, model) => {
|
|
84
|
+
t.timeout(120000); // 2 minutes timeout for search
|
|
85
|
+
const response = await testServer.executeOperation({
|
|
86
|
+
query: `
|
|
87
|
+
query TestToolCalling($text: String!, $chatHistory: [MultiMessage]!, $model: String) {
|
|
88
|
+
call_tools(
|
|
89
|
+
text: $text,
|
|
90
|
+
chatHistory: $chatHistory,
|
|
91
|
+
model: $model
|
|
92
|
+
) {
|
|
93
|
+
result
|
|
94
|
+
contextId
|
|
95
|
+
tool
|
|
96
|
+
warnings
|
|
97
|
+
errors
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
`,
|
|
101
|
+
variables: {
|
|
102
|
+
text: 'What are the latest developments in renewable energy?',
|
|
103
|
+
chatHistory: [{
|
|
104
|
+
role: 'user',
|
|
105
|
+
content: ['What are the latest developments in renewable energy?']
|
|
106
|
+
}],
|
|
107
|
+
model: model
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
t.is(response.body?.singleResult?.errors, undefined);
|
|
112
|
+
const result = response.body?.singleResult?.data?.call_tools.result;
|
|
113
|
+
t.true(result.length > 0, 'Should have a non-empty result');
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
// Test tool calling with a code execution request
|
|
117
|
+
runTestForModels('call_tools handles code execution request correctly', async (t, model) => {
|
|
118
|
+
t.timeout(120000); // 2 minutes timeout for code execution
|
|
119
|
+
const response = await testServer.executeOperation({
|
|
120
|
+
query: `
|
|
121
|
+
query TestToolCalling($text: String!, $chatHistory: [MultiMessage]!, $model: String) {
|
|
122
|
+
call_tools(
|
|
123
|
+
text: $text,
|
|
124
|
+
chatHistory: $chatHistory,
|
|
125
|
+
model: $model
|
|
126
|
+
) {
|
|
127
|
+
result
|
|
128
|
+
contextId
|
|
129
|
+
tool
|
|
130
|
+
warnings
|
|
131
|
+
errors
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
`,
|
|
135
|
+
variables: {
|
|
136
|
+
text: 'Write a Python function to calculate fibonacci numbers',
|
|
137
|
+
chatHistory: [{
|
|
138
|
+
role: 'user',
|
|
139
|
+
content: ['Write a Python function to calculate fibonacci numbers']
|
|
140
|
+
}],
|
|
141
|
+
model: model
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
t.is(response.body?.singleResult?.errors, undefined);
|
|
146
|
+
const result = response.body?.singleResult?.data?.call_tools.result;
|
|
147
|
+
t.true(result.length > 0, 'Should have a non-empty result');
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
// Test tool calling with a reasoning request
|
|
151
|
+
runTestForModels('call_tools handles reasoning request correctly', async (t, model) => {
|
|
152
|
+
t.timeout(120000); // 2 minutes timeout for reasoning
|
|
153
|
+
const response = await testServer.executeOperation({
|
|
154
|
+
query: `
|
|
155
|
+
query TestToolCalling($text: String!, $chatHistory: [MultiMessage]!, $model: String) {
|
|
156
|
+
call_tools(
|
|
157
|
+
text: $text,
|
|
158
|
+
chatHistory: $chatHistory,
|
|
159
|
+
model: $model
|
|
160
|
+
) {
|
|
161
|
+
result
|
|
162
|
+
contextId
|
|
163
|
+
tool
|
|
164
|
+
warnings
|
|
165
|
+
errors
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
`,
|
|
169
|
+
variables: {
|
|
170
|
+
text: 'Explain the implications of quantum computing on cryptography',
|
|
171
|
+
chatHistory: [{
|
|
172
|
+
role: 'user',
|
|
173
|
+
content: ['Explain the implications of quantum computing on cryptography']
|
|
174
|
+
}],
|
|
175
|
+
model: model
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
t.is(response.body?.singleResult?.errors, undefined);
|
|
180
|
+
const result = response.body?.singleResult?.data?.call_tools.result;
|
|
181
|
+
t.true(result.length > 0, 'Should have a non-empty result');
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
// Test tool calling with a document request
|
|
185
|
+
runTestForModels('call_tools handles document request correctly', async (t, model) => {
|
|
186
|
+
t.timeout(120000); // 2 minutes timeout for document processing
|
|
187
|
+
const response = await testServer.executeOperation({
|
|
188
|
+
query: `
|
|
189
|
+
query TestToolCalling($text: String!, $chatHistory: [MultiMessage]!, $model: String) {
|
|
190
|
+
call_tools(
|
|
191
|
+
text: $text,
|
|
192
|
+
chatHistory: $chatHistory,
|
|
193
|
+
model: $model
|
|
194
|
+
) {
|
|
195
|
+
result
|
|
196
|
+
contextId
|
|
197
|
+
tool
|
|
198
|
+
warnings
|
|
199
|
+
errors
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
`,
|
|
203
|
+
variables: {
|
|
204
|
+
text: 'Summarize the key points from my document about project management',
|
|
205
|
+
chatHistory: [{
|
|
206
|
+
role: 'user',
|
|
207
|
+
content: ['Summarize the key points from my document about project management']
|
|
208
|
+
}],
|
|
209
|
+
model: model
|
|
210
|
+
}
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
t.is(response.body?.singleResult?.errors, undefined);
|
|
214
|
+
const result = response.body?.singleResult?.data?.call_tools.result;
|
|
215
|
+
t.true(result.length > 0, 'Should have a non-empty result');
|
|
216
|
+
});
|
|
@@ -408,4 +408,82 @@ test('Combining existing tools block with generated tools', async (t) => {
|
|
|
408
408
|
t.truthy(result.tools, 'Tools should be defined');
|
|
409
409
|
t.is(result.tools.length, 2, 'Should have 2 tools');
|
|
410
410
|
t.deepEqual(result.tools.map(t => t.name).sort(), ['get_weather', 'memory_lookup']);
|
|
411
|
+
});
|
|
412
|
+
|
|
413
|
+
// Test preventing duplicate tool definitions
|
|
414
|
+
test('Prevent duplicate tool definitions', async (t) => {
|
|
415
|
+
const plugin = createPlugin();
|
|
416
|
+
const prompt = mockPathwayResolverMessages.pathway.prompt;
|
|
417
|
+
|
|
418
|
+
const parameters = {
|
|
419
|
+
tools: [
|
|
420
|
+
{
|
|
421
|
+
type: 'function',
|
|
422
|
+
function: {
|
|
423
|
+
name: 'memory_lookup',
|
|
424
|
+
description: 'Look up information in memory',
|
|
425
|
+
parameters: {
|
|
426
|
+
type: 'object',
|
|
427
|
+
properties: {
|
|
428
|
+
query: {
|
|
429
|
+
type: 'string',
|
|
430
|
+
description: 'The search query'
|
|
431
|
+
}
|
|
432
|
+
},
|
|
433
|
+
required: ['query']
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
]
|
|
438
|
+
};
|
|
439
|
+
|
|
440
|
+
const messages = [
|
|
441
|
+
{
|
|
442
|
+
role: 'system',
|
|
443
|
+
content: 'You are a helpful assistant'
|
|
444
|
+
},
|
|
445
|
+
{
|
|
446
|
+
role: 'user',
|
|
447
|
+
content: 'What\'s in my memory?'
|
|
448
|
+
},
|
|
449
|
+
{
|
|
450
|
+
role: 'assistant',
|
|
451
|
+
content: [
|
|
452
|
+
{
|
|
453
|
+
type: 'tool_use',
|
|
454
|
+
id: 'tool_1',
|
|
455
|
+
name: 'memory_lookup',
|
|
456
|
+
input: { query: 'search memory' }
|
|
457
|
+
}
|
|
458
|
+
]
|
|
459
|
+
}
|
|
460
|
+
];
|
|
461
|
+
|
|
462
|
+
// Set up the mock prompt with messages
|
|
463
|
+
prompt.messages = messages;
|
|
464
|
+
|
|
465
|
+
const cortexRequest = { messages };
|
|
466
|
+
const result = await plugin.getRequestParameters('test', parameters, prompt, cortexRequest);
|
|
467
|
+
|
|
468
|
+
// Check that we only have one memory_lookup tool definition
|
|
469
|
+
t.truthy(result.tools, 'Tools should be defined');
|
|
470
|
+
t.is(result.tools.length, 1, 'Should have exactly 1 tool');
|
|
471
|
+
t.is(result.tools[0].name, 'memory_lookup', 'Tool should be memory_lookup');
|
|
472
|
+
t.is(result.tools[0].description, 'Look up information in memory', 'Should preserve original tool description');
|
|
473
|
+
|
|
474
|
+
// Verify the tool_use call is still properly converted
|
|
475
|
+
t.truthy(result.messages, 'Messages should be defined');
|
|
476
|
+
t.is(result.messages.length, 1, 'Should have 1 message after conversion');
|
|
477
|
+
|
|
478
|
+
// Check the converted message
|
|
479
|
+
const message = result.messages[0];
|
|
480
|
+
t.is(message.role, 'assistant', 'Message should be from assistant');
|
|
481
|
+
t.truthy(message.content, 'Message should have content');
|
|
482
|
+
t.is(message.content.length, 1, 'Message should have one content item');
|
|
483
|
+
t.deepEqual(message.content[0], {
|
|
484
|
+
type: 'tool_use',
|
|
485
|
+
id: 'tool_1',
|
|
486
|
+
name: 'memory_lookup',
|
|
487
|
+
input: { query: 'search memory' }
|
|
488
|
+
});
|
|
411
489
|
});
|
package/tests/mocks.js
CHANGED
|
@@ -68,9 +68,17 @@ export const mockConfig = {
|
|
|
68
68
|
|
|
69
69
|
export const mockPathwayResolverMessages = {
|
|
70
70
|
model: {
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
71
|
+
name: 'testModel',
|
|
72
|
+
type: 'OPENAI-CHAT',
|
|
73
|
+
url: 'https://api.openai.com/v1/chat/completions',
|
|
74
|
+
endpoints: [{
|
|
75
|
+
name: 'Test Endpoint',
|
|
76
|
+
url: 'https://api.openai.com/v1/chat/completions',
|
|
77
|
+
headers: {
|
|
78
|
+
'Authorization': `Bearer ${process.env.OPENAI_API_KEY}`,
|
|
79
|
+
'Content-Type': 'application/json'
|
|
80
|
+
}
|
|
81
|
+
}]
|
|
74
82
|
},
|
|
75
83
|
modelName: 'testModel',
|
|
76
84
|
pathway: mockPathwayMessages,
|
|
@@ -209,7 +209,7 @@ test('Unsupported mime type conversion', async (t) => {
|
|
|
209
209
|
|
|
210
210
|
t.is(modifiedMessages[0].content.length, 2);
|
|
211
211
|
t.is(modifiedMessages[0].content[0].text, 'Can you analyze this PDF?');
|
|
212
|
-
t.
|
|
212
|
+
t.true(modifiedMessages[0].content[1].text.includes('image_url'));
|
|
213
213
|
});
|
|
214
214
|
|
|
215
215
|
// Test pathological cases
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
import test from 'ava';
|
|
2
|
+
import OpenAIVisionPlugin from '../server/plugins/openAiVisionPlugin.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 = () => {
|
|
10
|
+
const plugin = new OpenAIVisionPlugin(pathway, {
|
|
11
|
+
name: 'test-model',
|
|
12
|
+
type: 'OPENAI-VISION'
|
|
13
|
+
});
|
|
14
|
+
return plugin;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
// Test OpenAI tools block conversion
|
|
18
|
+
test('OpenAI tools block conversion', async (t) => {
|
|
19
|
+
const plugin = createPlugin();
|
|
20
|
+
const prompt = mockPathwayResolverMessages.pathway.prompt;
|
|
21
|
+
|
|
22
|
+
const parameters = {
|
|
23
|
+
tools: [
|
|
24
|
+
{
|
|
25
|
+
type: 'function',
|
|
26
|
+
function: {
|
|
27
|
+
name: 'get_weather',
|
|
28
|
+
description: 'Get current temperature for a given location.',
|
|
29
|
+
parameters: {
|
|
30
|
+
type: 'object',
|
|
31
|
+
properties: {
|
|
32
|
+
location: {
|
|
33
|
+
type: 'string',
|
|
34
|
+
description: 'City and country e.g. Bogotá, Colombia'
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
required: ['location'],
|
|
38
|
+
additionalProperties: false
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
]
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const cortexRequest = { tools: parameters.tools };
|
|
46
|
+
const result = await plugin.getRequestParameters('test', parameters, prompt, cortexRequest);
|
|
47
|
+
|
|
48
|
+
t.deepEqual(result.tools, parameters.tools);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
// Test tool call response handling
|
|
52
|
+
test('Tool call response handling', async (t) => {
|
|
53
|
+
const plugin = createPlugin();
|
|
54
|
+
|
|
55
|
+
const responseData = {
|
|
56
|
+
choices: [{
|
|
57
|
+
message: {
|
|
58
|
+
role: 'assistant',
|
|
59
|
+
content: 'I will check the weather for you.',
|
|
60
|
+
tool_calls: [{
|
|
61
|
+
id: 'call_123',
|
|
62
|
+
type: 'function',
|
|
63
|
+
function: {
|
|
64
|
+
name: 'get_weather',
|
|
65
|
+
arguments: '{"location": "Bogotá, Colombia"}'
|
|
66
|
+
}
|
|
67
|
+
}]
|
|
68
|
+
}
|
|
69
|
+
}]
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
const result = plugin.parseResponse(responseData);
|
|
73
|
+
|
|
74
|
+
t.deepEqual(result, {
|
|
75
|
+
role: 'assistant',
|
|
76
|
+
content: 'I will check the weather for you.',
|
|
77
|
+
tool_calls: [{
|
|
78
|
+
id: 'call_123',
|
|
79
|
+
type: 'function',
|
|
80
|
+
function: {
|
|
81
|
+
name: 'get_weather',
|
|
82
|
+
arguments: '{"location": "Bogotá, Colombia"}'
|
|
83
|
+
}
|
|
84
|
+
}]
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
// Test tool result message handling
|
|
89
|
+
test('Tool result message handling', async (t) => {
|
|
90
|
+
const plugin = createPlugin();
|
|
91
|
+
const prompt = mockPathwayResolverMessages.pathway.prompt;
|
|
92
|
+
|
|
93
|
+
const messages = [
|
|
94
|
+
{
|
|
95
|
+
role: 'assistant',
|
|
96
|
+
content: 'I will check the weather for you.',
|
|
97
|
+
tool_calls: [{
|
|
98
|
+
id: 'call_123',
|
|
99
|
+
type: 'function',
|
|
100
|
+
function: {
|
|
101
|
+
name: 'get_weather',
|
|
102
|
+
arguments: '{"location": "Bogotá, Colombia"}'
|
|
103
|
+
}
|
|
104
|
+
}]
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
role: 'tool',
|
|
108
|
+
content: 'The weather in Bogotá is 18°C and sunny.',
|
|
109
|
+
tool_call_id: 'call_123'
|
|
110
|
+
}
|
|
111
|
+
];
|
|
112
|
+
|
|
113
|
+
const result = await plugin.tryParseMessages(messages);
|
|
114
|
+
|
|
115
|
+
t.deepEqual(result, messages);
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
// Test mixed content with tools and images
|
|
119
|
+
test('Mixed content with tools and images', async (t) => {
|
|
120
|
+
const plugin = createPlugin();
|
|
121
|
+
const prompt = mockPathwayResolverMessages.pathway.prompt;
|
|
122
|
+
|
|
123
|
+
// Mock the validateImageUrl method to always return true
|
|
124
|
+
plugin.validateImageUrl = async () => true;
|
|
125
|
+
|
|
126
|
+
const messages = [
|
|
127
|
+
{
|
|
128
|
+
role: 'user',
|
|
129
|
+
content: [
|
|
130
|
+
{ type: 'text', text: 'What\'s the weather in this image?' },
|
|
131
|
+
{ type: 'image_url', image_url: { url: 'https://example.com/image.jpg' } }
|
|
132
|
+
]
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
role: 'assistant',
|
|
136
|
+
content: 'I will analyze the image and check the weather.',
|
|
137
|
+
tool_calls: [{
|
|
138
|
+
id: 'call_123',
|
|
139
|
+
type: 'function',
|
|
140
|
+
function: {
|
|
141
|
+
name: 'get_weather',
|
|
142
|
+
arguments: '{"location": "Bogotá, Colombia"}'
|
|
143
|
+
}
|
|
144
|
+
}]
|
|
145
|
+
}
|
|
146
|
+
];
|
|
147
|
+
|
|
148
|
+
const result = await plugin.tryParseMessages(messages);
|
|
149
|
+
|
|
150
|
+
t.is(result[0].role, 'user');
|
|
151
|
+
t.is(result[0].content[0].type, 'text');
|
|
152
|
+
t.is(result[0].content[1].type, 'image_url');
|
|
153
|
+
t.is(result[1].role, 'assistant');
|
|
154
|
+
t.truthy(result[1].tool_calls);
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
// Test error handling in tool calls
|
|
158
|
+
test('Error handling in tool calls', async (t) => {
|
|
159
|
+
const plugin = createPlugin();
|
|
160
|
+
|
|
161
|
+
const responseData = {
|
|
162
|
+
choices: [{
|
|
163
|
+
message: {
|
|
164
|
+
role: 'assistant',
|
|
165
|
+
content: 'I will check the weather for you.',
|
|
166
|
+
tool_calls: [{
|
|
167
|
+
id: 'call_123',
|
|
168
|
+
type: 'function',
|
|
169
|
+
function: {
|
|
170
|
+
name: 'get_weather',
|
|
171
|
+
arguments: 'invalid json'
|
|
172
|
+
}
|
|
173
|
+
}]
|
|
174
|
+
}
|
|
175
|
+
}]
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
const result = plugin.parseResponse(responseData);
|
|
179
|
+
|
|
180
|
+
t.deepEqual(result, {
|
|
181
|
+
role: 'assistant',
|
|
182
|
+
content: 'I will check the weather for you.',
|
|
183
|
+
tool_calls: [{
|
|
184
|
+
id: 'call_123',
|
|
185
|
+
type: 'function',
|
|
186
|
+
function: {
|
|
187
|
+
name: 'get_weather',
|
|
188
|
+
arguments: 'invalid json'
|
|
189
|
+
}
|
|
190
|
+
}]
|
|
191
|
+
});
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
// Test multiple tool calls in sequence
|
|
195
|
+
test('Multiple tool calls in sequence', async (t) => {
|
|
196
|
+
const plugin = createPlugin();
|
|
197
|
+
const prompt = mockPathwayResolverMessages.pathway.prompt;
|
|
198
|
+
|
|
199
|
+
const messages = [
|
|
200
|
+
{
|
|
201
|
+
role: 'assistant',
|
|
202
|
+
content: 'I will check multiple things for you.',
|
|
203
|
+
tool_calls: [
|
|
204
|
+
{
|
|
205
|
+
id: 'call_123',
|
|
206
|
+
type: 'function',
|
|
207
|
+
function: {
|
|
208
|
+
name: 'get_weather',
|
|
209
|
+
arguments: '{"location": "Bogotá, Colombia"}'
|
|
210
|
+
}
|
|
211
|
+
},
|
|
212
|
+
{
|
|
213
|
+
id: 'call_124',
|
|
214
|
+
type: 'function',
|
|
215
|
+
function: {
|
|
216
|
+
name: 'get_time',
|
|
217
|
+
arguments: '{"location": "Bogotá, Colombia"}'
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
]
|
|
221
|
+
},
|
|
222
|
+
{
|
|
223
|
+
role: 'tool',
|
|
224
|
+
content: 'The weather in Bogotá is 18°C and sunny.',
|
|
225
|
+
tool_call_id: 'call_123'
|
|
226
|
+
},
|
|
227
|
+
{
|
|
228
|
+
role: 'tool',
|
|
229
|
+
content: 'The current time in Bogotá is 14:30.',
|
|
230
|
+
tool_call_id: 'call_124'
|
|
231
|
+
}
|
|
232
|
+
];
|
|
233
|
+
|
|
234
|
+
const result = await plugin.tryParseMessages(messages);
|
|
235
|
+
|
|
236
|
+
t.is(result.length, 3);
|
|
237
|
+
t.is(result[0].role, 'assistant');
|
|
238
|
+
t.is(result[0].tool_calls.length, 2);
|
|
239
|
+
t.is(result[1].role, 'tool');
|
|
240
|
+
t.is(result[2].role, 'tool');
|
|
241
|
+
});
|
|
242
|
+
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
//test_palm_chat.mjs
|
|
2
|
-
// Test for handling of prompts in the PaLM chat format for Cortex
|
|
3
|
-
|
|
4
|
-
import { Prompt } from '../server/prompt.js';
|
|
5
|
-
|
|
6
|
-
// Description: Have a chat with a bot that uses context to understand the conversation
|
|
7
|
-
export default {
|
|
8
|
-
prompt:
|
|
9
|
-
[
|
|
10
|
-
new Prompt({
|
|
11
|
-
context: "Instructions:\nYou an AI entity working a global media network. You are truthful, kind, and helpful. Your expertise includes journalism, journalistic ethics, researching and composing documents, and technology. You know the current date and time - it is {{now}}.",
|
|
12
|
-
examples: [
|
|
13
|
-
{
|
|
14
|
-
input: {"content": "What is your expertise?"},
|
|
15
|
-
output: {"content": "I am an expert in journalism and journalistic ethics."}
|
|
16
|
-
}],
|
|
17
|
-
messages: [
|
|
18
|
-
{"author": "user", "content": "Hi how are you today?"},
|
|
19
|
-
{"author": "assistant", "content": "I am doing well. How are you?"},
|
|
20
|
-
{"author": "user", "content": "I am doing well. What is your name?"},
|
|
21
|
-
{"author": "assistant", "content": "My name is Hula. What is your name?"},
|
|
22
|
-
{"author": "user", "content": "My name is Bob. What is your expertise?"},
|
|
23
|
-
]}),
|
|
24
|
-
],
|
|
25
|
-
inputParameters: {
|
|
26
|
-
chatHistory: [],
|
|
27
|
-
contextId: ``,
|
|
28
|
-
},
|
|
29
|
-
model: 'palm-chat',
|
|
30
|
-
useInputChunking: false,
|
|
31
|
-
}
|