@aj-archipelago/cortex 1.3.66 → 1.4.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/config.js +27 -0
- package/helper-apps/cortex-autogen2/Dockerfile +88 -21
- package/helper-apps/cortex-autogen2/docker-compose.yml +15 -8
- package/helper-apps/cortex-autogen2/host.json +5 -0
- package/helper-apps/cortex-autogen2/pyproject.toml +82 -25
- package/helper-apps/cortex-autogen2/requirements.txt +84 -14
- package/helper-apps/cortex-autogen2/services/redis_publisher.py +129 -3
- package/helper-apps/cortex-autogen2/task_processor.py +432 -116
- package/helper-apps/cortex-autogen2/tools/__init__.py +2 -0
- package/helper-apps/cortex-autogen2/tools/azure_blob_tools.py +32 -0
- package/helper-apps/cortex-autogen2/tools/azure_foundry_agents.py +50 -14
- package/helper-apps/cortex-autogen2/tools/file_tools.py +169 -44
- package/helper-apps/cortex-autogen2/tools/google_cse.py +117 -0
- package/helper-apps/cortex-autogen2/tools/search_tools.py +655 -98
- package/helper-apps/cortex-doc-to-pdf/DocToPdfFunction/__init__.py +3 -0
- package/helper-apps/cortex-doc-to-pdf/DocToPdfFunction/function.json +20 -0
- package/helper-apps/cortex-doc-to-pdf/Dockerfile +46 -0
- package/helper-apps/cortex-doc-to-pdf/README.md +408 -0
- package/helper-apps/cortex-doc-to-pdf/converter.py +157 -0
- package/helper-apps/cortex-doc-to-pdf/docker-compose.yml +23 -0
- package/helper-apps/cortex-doc-to-pdf/document_converter.py +181 -0
- package/helper-apps/cortex-doc-to-pdf/examples/README.md +252 -0
- package/helper-apps/cortex-doc-to-pdf/examples/nodejs-client.js +266 -0
- package/helper-apps/cortex-doc-to-pdf/examples/package-lock.json +297 -0
- package/helper-apps/cortex-doc-to-pdf/examples/package.json +23 -0
- package/helper-apps/cortex-doc-to-pdf/function_app.py +85 -0
- package/helper-apps/cortex-doc-to-pdf/host.json +16 -0
- package/helper-apps/cortex-doc-to-pdf/request_handlers.py +193 -0
- package/helper-apps/cortex-doc-to-pdf/requirements.txt +3 -0
- package/helper-apps/cortex-doc-to-pdf/tests/run_tests.sh +26 -0
- package/helper-apps/cortex-doc-to-pdf/tests/test_conversion.py +320 -0
- package/helper-apps/cortex-doc-to-pdf/tests/test_streaming.py +419 -0
- package/helper-apps/cortex-file-handler/package-lock.json +1 -0
- package/helper-apps/cortex-file-handler/package.json +1 -0
- package/helper-apps/cortex-file-handler/src/services/ConversionService.js +81 -8
- package/helper-apps/cortex-file-handler/tests/FileConversionService.test.js +54 -7
- package/helper-apps/cortex-file-handler/tests/getOperations.test.js +19 -7
- package/lib/encodeCache.js +5 -0
- package/lib/keyValueStorageClient.js +5 -0
- package/lib/logger.js +1 -1
- package/lib/pathwayManager.js +42 -8
- package/lib/pathwayTools.js +8 -1
- package/lib/redisSubscription.js +6 -0
- package/lib/requestExecutor.js +4 -0
- package/lib/util.js +145 -1
- package/package.json +1 -1
- package/pathways/basePathway.js +3 -3
- package/pathways/bing_afagent.js +1 -0
- package/pathways/gemini_15_vision.js +1 -1
- package/pathways/google_cse.js +2 -2
- package/pathways/image_gemini_25.js +85 -0
- package/pathways/image_prompt_optimizer_gemini_25.js +149 -0
- package/pathways/image_qwen.js +28 -0
- package/pathways/image_seedream4.js +26 -0
- package/pathways/rag.js +1 -1
- package/pathways/rag_jarvis.js +1 -1
- package/pathways/system/entity/sys_entity_continue.js +1 -1
- package/pathways/system/entity/sys_generator_results.js +1 -1
- package/pathways/system/entity/tools/sys_tool_google_search.js +15 -2
- package/pathways/system/entity/tools/sys_tool_grok_x_search.js +3 -3
- package/pathways/system/entity/tools/sys_tool_image.js +28 -23
- package/pathways/system/entity/tools/sys_tool_image_gemini.js +135 -0
- package/pathways/system/workspaces/run_workspace_prompt.js +0 -3
- package/server/executeWorkspace.js +381 -0
- package/server/graphql.js +14 -182
- package/server/modelExecutor.js +4 -0
- package/server/pathwayResolver.js +19 -18
- package/server/plugins/claude3VertexPlugin.js +13 -8
- package/server/plugins/gemini15ChatPlugin.js +15 -10
- package/server/plugins/gemini15VisionPlugin.js +2 -23
- package/server/plugins/gemini25ImagePlugin.js +155 -0
- package/server/plugins/modelPlugin.js +3 -2
- package/server/plugins/openAiChatPlugin.js +6 -6
- package/server/plugins/replicateApiPlugin.js +268 -12
- package/server/plugins/veoVideoPlugin.js +15 -1
- package/server/rest.js +2 -0
- package/server/typeDef.js +96 -10
- package/tests/integration/apptekTranslatePlugin.integration.test.js +1 -1
- package/tests/unit/core/parser.test.js +0 -1
- package/tests/unit/core/pathwayManager.test.js +2 -4
- package/tests/unit/core/pathwayManagerWithFiles.test.js +256 -0
- package/tests/unit/graphql_executeWorkspace_transformation.test.js +244 -0
- package/tests/unit/plugins/gemini25ImagePlugin.test.js +294 -0
- package/tests/unit/server/graphql.test.js +122 -1
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
import test from 'ava';
|
|
2
|
+
import Gemini25ImagePlugin from '../../../server/plugins/gemini25ImagePlugin.js';
|
|
3
|
+
import { PathwayResolver } from '../../../server/pathwayResolver.js';
|
|
4
|
+
import { config } from '../../../config.js';
|
|
5
|
+
import { requestState } from '../../../server/requestState.js';
|
|
6
|
+
|
|
7
|
+
// Mock logger to prevent issues in tests
|
|
8
|
+
const mockLogger = {
|
|
9
|
+
debug: () => {},
|
|
10
|
+
info: () => {},
|
|
11
|
+
warn: () => {},
|
|
12
|
+
error: () => {}
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
// Mock the logger module globally
|
|
16
|
+
global.logger = mockLogger;
|
|
17
|
+
|
|
18
|
+
function createResolverWithPlugin(pluginClass, modelName = 'test-model') {
|
|
19
|
+
const pathway = {
|
|
20
|
+
name: 'test-pathway',
|
|
21
|
+
model: modelName,
|
|
22
|
+
prompt: 'test prompt',
|
|
23
|
+
toolCallback: () => {} // Mock tool callback
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const model = {
|
|
27
|
+
name: modelName,
|
|
28
|
+
type: 'GEMINI-2.5-IMAGE'
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const resolver = new PathwayResolver({
|
|
32
|
+
config,
|
|
33
|
+
pathway,
|
|
34
|
+
args: {},
|
|
35
|
+
endpoints: { [modelName]: model }
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
resolver.modelExecutor.plugin = new pluginClass(pathway, model);
|
|
39
|
+
return resolver;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
test('Gemini25ImagePlugin - filters undefined tool calls from buffer', async t => {
|
|
43
|
+
const resolver = createResolverWithPlugin(Gemini25ImagePlugin);
|
|
44
|
+
const plugin = resolver.modelExecutor.plugin;
|
|
45
|
+
|
|
46
|
+
// Simulate buffer with undefined elements
|
|
47
|
+
plugin.toolCallsBuffer = [
|
|
48
|
+
undefined,
|
|
49
|
+
{
|
|
50
|
+
id: 'call_1_1234567890',
|
|
51
|
+
type: 'function',
|
|
52
|
+
function: {
|
|
53
|
+
name: 'test_tool',
|
|
54
|
+
arguments: '{"param": "value"}'
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
];
|
|
58
|
+
|
|
59
|
+
// Mock the tool callback
|
|
60
|
+
let capturedToolCalls = null;
|
|
61
|
+
plugin.pathwayToolCallback = (args, message, resolver) => {
|
|
62
|
+
capturedToolCalls = message.tool_calls;
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
// Mock requestProgress and pathwayResolver
|
|
66
|
+
const requestProgress = { progress: 0, started: true };
|
|
67
|
+
const pathwayResolver = { args: {} };
|
|
68
|
+
|
|
69
|
+
// Mock requestState
|
|
70
|
+
requestState[plugin.requestId] = { pathwayResolver };
|
|
71
|
+
|
|
72
|
+
// Simulate a tool_calls finish reason
|
|
73
|
+
const eventData = {
|
|
74
|
+
candidates: [{
|
|
75
|
+
finishReason: 'STOP'
|
|
76
|
+
}]
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
// Set hadToolCalls to true to trigger tool_calls finish reason
|
|
80
|
+
plugin.hadToolCalls = true;
|
|
81
|
+
|
|
82
|
+
// Process the stream event
|
|
83
|
+
plugin.processStreamEvent({ data: JSON.stringify(eventData) }, requestProgress);
|
|
84
|
+
|
|
85
|
+
// Verify that undefined elements were filtered out
|
|
86
|
+
t.truthy(capturedToolCalls, 'Tool callback should have been called');
|
|
87
|
+
t.is(capturedToolCalls.length, 1, 'Should have filtered out undefined elements');
|
|
88
|
+
t.is(capturedToolCalls[0].function.name, 'test_tool', 'Valid tool call should be preserved');
|
|
89
|
+
|
|
90
|
+
// Clean up
|
|
91
|
+
delete requestState[plugin.requestId];
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
test('Gemini25ImagePlugin - handles empty buffer gracefully', async t => {
|
|
95
|
+
const resolver = createResolverWithPlugin(Gemini25ImagePlugin);
|
|
96
|
+
const plugin = resolver.modelExecutor.plugin;
|
|
97
|
+
|
|
98
|
+
// Empty buffer
|
|
99
|
+
plugin.toolCallsBuffer = [];
|
|
100
|
+
|
|
101
|
+
// Mock the tool callback
|
|
102
|
+
let callbackCalled = false;
|
|
103
|
+
plugin.pathwayToolCallback = () => {
|
|
104
|
+
callbackCalled = true;
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
// Mock requestProgress and pathwayResolver
|
|
108
|
+
const requestProgress = { progress: 0, started: true };
|
|
109
|
+
const pathwayResolver = { args: {} };
|
|
110
|
+
|
|
111
|
+
// Mock requestState
|
|
112
|
+
requestState[plugin.requestId] = { pathwayResolver };
|
|
113
|
+
|
|
114
|
+
// Simulate a tool_calls finish reason
|
|
115
|
+
const eventData = {
|
|
116
|
+
candidates: [{
|
|
117
|
+
finishReason: 'STOP'
|
|
118
|
+
}]
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
// Set hadToolCalls to true
|
|
122
|
+
plugin.hadToolCalls = true;
|
|
123
|
+
|
|
124
|
+
// Process the stream event
|
|
125
|
+
plugin.processStreamEvent({ data: JSON.stringify(eventData) }, requestProgress);
|
|
126
|
+
|
|
127
|
+
// Verify that callback was not called with empty buffer
|
|
128
|
+
t.falsy(callbackCalled, 'Tool callback should not be called with empty buffer');
|
|
129
|
+
|
|
130
|
+
// Clean up
|
|
131
|
+
delete requestState[plugin.requestId];
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
test('Gemini25ImagePlugin - handles image artifacts in streaming', async t => {
|
|
135
|
+
const resolver = createResolverWithPlugin(Gemini25ImagePlugin);
|
|
136
|
+
const plugin = resolver.modelExecutor.plugin;
|
|
137
|
+
|
|
138
|
+
// Mock requestProgress
|
|
139
|
+
const requestProgress = { progress: 0, started: true };
|
|
140
|
+
|
|
141
|
+
// Simulate event with image artifact
|
|
142
|
+
const eventData = {
|
|
143
|
+
candidates: [{
|
|
144
|
+
content: {
|
|
145
|
+
parts: [{
|
|
146
|
+
inlineData: {
|
|
147
|
+
data: 'base64imagedata',
|
|
148
|
+
mimeType: 'image/png'
|
|
149
|
+
}
|
|
150
|
+
}]
|
|
151
|
+
}
|
|
152
|
+
}]
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
// Process the stream event
|
|
156
|
+
plugin.processStreamEvent({ data: JSON.stringify(eventData) }, requestProgress);
|
|
157
|
+
|
|
158
|
+
// Verify that image artifacts were captured
|
|
159
|
+
t.truthy(requestProgress.artifacts, 'Artifacts should be created');
|
|
160
|
+
t.is(requestProgress.artifacts.length, 1, 'Should have one image artifact');
|
|
161
|
+
t.is(requestProgress.artifacts[0].type, 'image', 'Artifact should be of type image');
|
|
162
|
+
t.is(requestProgress.artifacts[0].data, 'base64imagedata', 'Image data should be preserved');
|
|
163
|
+
t.is(requestProgress.artifacts[0].mimeType, 'image/png', 'MIME type should be preserved');
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
test('Gemini25ImagePlugin - handles multiple image artifacts', async t => {
|
|
167
|
+
const resolver = createResolverWithPlugin(Gemini25ImagePlugin);
|
|
168
|
+
const plugin = resolver.modelExecutor.plugin;
|
|
169
|
+
|
|
170
|
+
// Mock requestProgress
|
|
171
|
+
const requestProgress = { progress: 0, started: true };
|
|
172
|
+
|
|
173
|
+
// Simulate event with multiple image artifacts
|
|
174
|
+
const eventData = {
|
|
175
|
+
candidates: [{
|
|
176
|
+
content: {
|
|
177
|
+
parts: [
|
|
178
|
+
{
|
|
179
|
+
inlineData: {
|
|
180
|
+
data: 'base64image1',
|
|
181
|
+
mimeType: 'image/png'
|
|
182
|
+
}
|
|
183
|
+
},
|
|
184
|
+
{
|
|
185
|
+
inlineData: {
|
|
186
|
+
data: 'base64image2',
|
|
187
|
+
mimeType: 'image/jpeg'
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
]
|
|
191
|
+
}
|
|
192
|
+
}]
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
// Process the stream event
|
|
196
|
+
plugin.processStreamEvent({ data: JSON.stringify(eventData) }, requestProgress);
|
|
197
|
+
|
|
198
|
+
// Verify that multiple image artifacts were captured
|
|
199
|
+
t.truthy(requestProgress.artifacts, 'Artifacts should be created');
|
|
200
|
+
t.is(requestProgress.artifacts.length, 2, 'Should have two image artifacts');
|
|
201
|
+
t.is(requestProgress.artifacts[0].type, 'image', 'First artifact should be of type image');
|
|
202
|
+
t.is(requestProgress.artifacts[0].data, 'base64image1', 'First image data should be preserved');
|
|
203
|
+
t.is(requestProgress.artifacts[0].mimeType, 'image/png', 'First MIME type should be preserved');
|
|
204
|
+
t.is(requestProgress.artifacts[1].type, 'image', 'Second artifact should be of type image');
|
|
205
|
+
t.is(requestProgress.artifacts[1].data, 'base64image2', 'Second image data should be preserved');
|
|
206
|
+
t.is(requestProgress.artifacts[1].mimeType, 'image/jpeg', 'Second MIME type should be preserved');
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
test('Gemini25ImagePlugin - handles mixed content with text and images', async t => {
|
|
210
|
+
const resolver = createResolverWithPlugin(Gemini25ImagePlugin);
|
|
211
|
+
const plugin = resolver.modelExecutor.plugin;
|
|
212
|
+
|
|
213
|
+
// Mock requestProgress
|
|
214
|
+
const requestProgress = { progress: 0, started: true };
|
|
215
|
+
|
|
216
|
+
// Simulate event with mixed content (text + image)
|
|
217
|
+
const eventData = {
|
|
218
|
+
candidates: [{
|
|
219
|
+
content: {
|
|
220
|
+
parts: [
|
|
221
|
+
{
|
|
222
|
+
text: 'Here is an image:'
|
|
223
|
+
},
|
|
224
|
+
{
|
|
225
|
+
inlineData: {
|
|
226
|
+
data: 'base64imagedata',
|
|
227
|
+
mimeType: 'image/png'
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
]
|
|
231
|
+
}
|
|
232
|
+
}]
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
// Process the stream event
|
|
236
|
+
plugin.processStreamEvent({ data: JSON.stringify(eventData) }, requestProgress);
|
|
237
|
+
|
|
238
|
+
// Verify that both text content and image artifacts were handled
|
|
239
|
+
t.truthy(requestProgress.artifacts, 'Artifacts should be created');
|
|
240
|
+
t.is(requestProgress.artifacts.length, 1, 'Should have one image artifact');
|
|
241
|
+
t.is(requestProgress.artifacts[0].type, 'image', 'Artifact should be of type image');
|
|
242
|
+
t.is(requestProgress.artifacts[0].data, 'base64imagedata', 'Image data should be preserved');
|
|
243
|
+
|
|
244
|
+
// Clean up
|
|
245
|
+
delete requestState[plugin.requestId];
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
test('Gemini25ImagePlugin - handles response_modalities parameter', async t => {
|
|
249
|
+
const resolver = createResolverWithPlugin(Gemini25ImagePlugin);
|
|
250
|
+
const plugin = resolver.modelExecutor.plugin;
|
|
251
|
+
|
|
252
|
+
// Test with response_modalities parameter
|
|
253
|
+
const parameters = {
|
|
254
|
+
response_modalities: '["TEXT", "IMAGE"]'
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
const prompt = {
|
|
258
|
+
prompt: 'test prompt'
|
|
259
|
+
};
|
|
260
|
+
|
|
261
|
+
const cortexRequest = {
|
|
262
|
+
pathway: {}
|
|
263
|
+
};
|
|
264
|
+
|
|
265
|
+
const requestParams = plugin.getRequestParameters('test text', parameters, prompt, cortexRequest);
|
|
266
|
+
|
|
267
|
+
// Verify that response_modalities was added to generationConfig
|
|
268
|
+
t.truthy(requestParams.generationConfig.response_modalities, 'response_modalities should be set');
|
|
269
|
+
t.deepEqual(requestParams.generationConfig.response_modalities, ['TEXT', 'IMAGE'], 'response_modalities should be parsed correctly');
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
test('Gemini25ImagePlugin - handles response_modalities from pathway', async t => {
|
|
273
|
+
const resolver = createResolverWithPlugin(Gemini25ImagePlugin);
|
|
274
|
+
const plugin = resolver.modelExecutor.plugin;
|
|
275
|
+
|
|
276
|
+
// Test with response_modalities from pathway
|
|
277
|
+
const parameters = {};
|
|
278
|
+
|
|
279
|
+
const prompt = {
|
|
280
|
+
prompt: 'test prompt'
|
|
281
|
+
};
|
|
282
|
+
|
|
283
|
+
const cortexRequest = {
|
|
284
|
+
pathway: {
|
|
285
|
+
response_modalities: ['TEXT', 'IMAGE']
|
|
286
|
+
}
|
|
287
|
+
};
|
|
288
|
+
|
|
289
|
+
const requestParams = plugin.getRequestParameters('test text', parameters, prompt, cortexRequest);
|
|
290
|
+
|
|
291
|
+
// Verify that response_modalities was added to generationConfig
|
|
292
|
+
t.truthy(requestParams.generationConfig.response_modalities, 'response_modalities should be set');
|
|
293
|
+
t.deepEqual(requestParams.generationConfig.response_modalities, ['TEXT', 'IMAGE'], 'response_modalities should be set correctly');
|
|
294
|
+
});
|
|
@@ -2,6 +2,9 @@ import test from 'ava';
|
|
|
2
2
|
import sinon from 'sinon';
|
|
3
3
|
import { getResolvers } from '../../../server/graphql.js';
|
|
4
4
|
|
|
5
|
+
// Mock callPathway to avoid actual external calls
|
|
6
|
+
const mockCallPathway = sinon.stub();
|
|
7
|
+
|
|
5
8
|
// Mock logger to avoid actual logging during tests
|
|
6
9
|
const mockLogger = {
|
|
7
10
|
info: sinon.stub(),
|
|
@@ -17,7 +20,7 @@ const mockConfig = {
|
|
|
17
20
|
|
|
18
21
|
test.beforeEach(t => {
|
|
19
22
|
// Reset stubs before each test
|
|
20
|
-
|
|
23
|
+
mockCallPathway.resetHistory();
|
|
21
24
|
mockLogger.info.resetHistory();
|
|
22
25
|
mockLogger.debug.resetHistory();
|
|
23
26
|
mockLogger.error.resetHistory();
|
|
@@ -168,3 +171,121 @@ test('executeWorkspace does not check format when promptNames not provided', asy
|
|
|
168
171
|
t.false(mockPathwayManager.isLegacyPromptFormat.called);
|
|
169
172
|
t.true(mockPathwayManager.getPathway.calledOnce);
|
|
170
173
|
});
|
|
174
|
+
|
|
175
|
+
test('executeWorkspace helper function DRY refactoring - structure verification', async t => {
|
|
176
|
+
// This test verifies that the DRY refactoring doesn't break existing functionality
|
|
177
|
+
// by testing that all three code paths (wildcard, specific prompts, default)
|
|
178
|
+
// still work with fallback to legacy execution
|
|
179
|
+
|
|
180
|
+
const mockRootResolver = sinon.stub().resolves({ result: 'legacy-result' });
|
|
181
|
+
|
|
182
|
+
// Test wildcard case with legacy fallback
|
|
183
|
+
const mockPathwayManager = {
|
|
184
|
+
getLatestPathways: sinon.stub().resolves({
|
|
185
|
+
'test-user': {
|
|
186
|
+
'test-pathway': {
|
|
187
|
+
prompt: [
|
|
188
|
+
{ name: 'prompt1' }, // No cortexPathwayName - will fallback
|
|
189
|
+
{ name: 'prompt2' } // No cortexPathwayName - will fallback
|
|
190
|
+
],
|
|
191
|
+
systemPrompt: 'Test system prompt'
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}),
|
|
195
|
+
isLegacyPromptFormat: sinon.stub().returns(false),
|
|
196
|
+
getPathways: sinon.stub().resolves([
|
|
197
|
+
{
|
|
198
|
+
name: 'prompt1',
|
|
199
|
+
systemPrompt: 'System prompt 1',
|
|
200
|
+
prompt: [{ messages: ['message1'] }],
|
|
201
|
+
fileHashes: [],
|
|
202
|
+
rootResolver: mockRootResolver
|
|
203
|
+
},
|
|
204
|
+
{
|
|
205
|
+
name: 'prompt2',
|
|
206
|
+
systemPrompt: 'System prompt 2',
|
|
207
|
+
prompt: [{ messages: ['message2'] }],
|
|
208
|
+
fileHashes: [],
|
|
209
|
+
rootResolver: mockRootResolver
|
|
210
|
+
}
|
|
211
|
+
]),
|
|
212
|
+
getResolvers: sinon.stub().returns({ Mutation: {} })
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
const resolvers = getResolvers(mockConfig, {}, mockPathwayManager);
|
|
216
|
+
const executeWorkspaceResolver = resolvers.Query.executeWorkspace;
|
|
217
|
+
|
|
218
|
+
const mockContextValue = { config: mockConfig };
|
|
219
|
+
const mockInfo = {};
|
|
220
|
+
|
|
221
|
+
const args = {
|
|
222
|
+
userId: 'test-user',
|
|
223
|
+
pathwayName: 'test-pathway',
|
|
224
|
+
promptNames: ['*'], // Wildcard to execute all
|
|
225
|
+
text: 'test input'
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
const result = await executeWorkspaceResolver(null, args, mockContextValue, mockInfo);
|
|
229
|
+
|
|
230
|
+
// Verify that legacy resolvers were called (indicating the DRY helper function worked)
|
|
231
|
+
t.is(mockRootResolver.callCount, 2); // Called twice for both prompts
|
|
232
|
+
|
|
233
|
+
// Verify result structure matches expected format
|
|
234
|
+
t.truthy(result);
|
|
235
|
+
t.truthy(result.result);
|
|
236
|
+
t.true(result.debug.includes('Executed 2 prompts in parallel'));
|
|
237
|
+
|
|
238
|
+
// Parse the result to verify both prompts were executed
|
|
239
|
+
const parsedResult = JSON.parse(result.result);
|
|
240
|
+
t.is(parsedResult.length, 2);
|
|
241
|
+
t.is(parsedResult[0].promptName, 'prompt1');
|
|
242
|
+
t.is(parsedResult[1].promptName, 'prompt2');
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
test('executeWorkspace helper function DRY refactoring - default case structure', async t => {
|
|
246
|
+
// Test that the default case still works with the DRY helper function
|
|
247
|
+
|
|
248
|
+
const mockRootResolver = sinon.stub().resolves({ result: 'default-legacy-result' });
|
|
249
|
+
|
|
250
|
+
const mockPathwayManager = {
|
|
251
|
+
getLatestPathways: sinon.stub().resolves({
|
|
252
|
+
'test-user': {
|
|
253
|
+
'test-pathway': {
|
|
254
|
+
prompt: [
|
|
255
|
+
{ name: 'default-prompt' } // No cortexPathwayName
|
|
256
|
+
],
|
|
257
|
+
systemPrompt: 'Test system prompt'
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}),
|
|
261
|
+
getPathway: sinon.stub().resolves({
|
|
262
|
+
prompt: [{ name: 'default-prompt' }], // No cortexPathwayName
|
|
263
|
+
systemPrompt: 'Test system prompt',
|
|
264
|
+
fileHashes: [],
|
|
265
|
+
rootResolver: mockRootResolver
|
|
266
|
+
}),
|
|
267
|
+
getResolvers: sinon.stub().returns({ Mutation: {} })
|
|
268
|
+
};
|
|
269
|
+
|
|
270
|
+
const resolvers = getResolvers(mockConfig, {}, mockPathwayManager);
|
|
271
|
+
const executeWorkspaceResolver = resolvers.Query.executeWorkspace;
|
|
272
|
+
|
|
273
|
+
const mockContextValue = { config: mockConfig };
|
|
274
|
+
const mockInfo = {};
|
|
275
|
+
|
|
276
|
+
const args = {
|
|
277
|
+
userId: 'test-user',
|
|
278
|
+
pathwayName: 'test-pathway',
|
|
279
|
+
text: 'test input'
|
|
280
|
+
// No promptNames provided - uses default case
|
|
281
|
+
};
|
|
282
|
+
|
|
283
|
+
const result = await executeWorkspaceResolver(null, args, mockContextValue, mockInfo);
|
|
284
|
+
|
|
285
|
+
// Verify that legacy resolver was called (indicating DRY helper function worked for default case)
|
|
286
|
+
t.is(mockRootResolver.callCount, 1);
|
|
287
|
+
|
|
288
|
+
// Verify result structure
|
|
289
|
+
t.truthy(result);
|
|
290
|
+
t.is(result.result, 'default-legacy-result');
|
|
291
|
+
});
|