@aj-archipelago/cortex 1.3.66 → 1.3.67
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-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/lib/pathwayManager.js +42 -8
- package/lib/util.js +57 -1
- package/package.json +1 -1
- package/pathways/system/workspaces/run_workspace_prompt.js +0 -3
- package/server/executeWorkspace.js +381 -0
- package/server/graphql.js +5 -180
- package/tests/unit/core/parser.test.js +0 -1
- package/tests/unit/core/pathwayManagerWithFiles.test.js +256 -0
- package/tests/unit/graphql_executeWorkspace_transformation.test.js +244 -0
- package/tests/unit/server/graphql.test.js +122 -1
package/server/graphql.js
CHANGED
|
@@ -15,7 +15,6 @@ import { WebSocketServer } from 'ws';
|
|
|
15
15
|
import responseCachePlugin from '@apollo/server-plugin-response-cache';
|
|
16
16
|
import { KeyvAdapter } from '@apollo/utils.keyvadapter';
|
|
17
17
|
import cors from 'cors';
|
|
18
|
-
import { v4 as uuidv4 } from 'uuid';
|
|
19
18
|
import { buildModels, buildPathways } from '../config.js';
|
|
20
19
|
import logger from '../lib/logger.js';
|
|
21
20
|
import { buildModelEndpoints } from '../lib/requestExecutor.js';
|
|
@@ -23,8 +22,9 @@ import { startTestServer } from '../tests/helpers/server.js';
|
|
|
23
22
|
import { requestState } from './requestState.js';
|
|
24
23
|
import { cancelRequestResolver } from './resolver.js';
|
|
25
24
|
import subscriptions from './subscriptions.js';
|
|
26
|
-
import { getMessageTypeDefs
|
|
25
|
+
import { getMessageTypeDefs } from './typeDef.js';
|
|
27
26
|
import { buildRestEndpoints } from './rest.js';
|
|
27
|
+
import { executeWorkspaceResolver, getExecuteWorkspaceTypeDefs } from './executeWorkspace.js';
|
|
28
28
|
|
|
29
29
|
// Utility functions
|
|
30
30
|
// Server plugins
|
|
@@ -74,22 +74,7 @@ const getTypedefs = (pathways, pathwayManager) => {
|
|
|
74
74
|
cancelRequest(requestId: String!): Boolean
|
|
75
75
|
}
|
|
76
76
|
|
|
77
|
-
${
|
|
78
|
-
|
|
79
|
-
type ExecuteWorkspaceResult {
|
|
80
|
-
debug: String
|
|
81
|
-
result: String
|
|
82
|
-
resultData: String
|
|
83
|
-
previousResult: String
|
|
84
|
-
warnings: [String]
|
|
85
|
-
errors: [String]
|
|
86
|
-
contextId: String
|
|
87
|
-
tool: String
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
extend type Query {
|
|
91
|
-
executeWorkspace(userId: String!, pathwayName: String!, ${userPathwayInputParameters}): ExecuteWorkspaceResult
|
|
92
|
-
}
|
|
77
|
+
${getExecuteWorkspaceTypeDefs()}
|
|
93
78
|
|
|
94
79
|
type RequestSubscription {
|
|
95
80
|
requestId: String
|
|
@@ -130,171 +115,11 @@ const getResolvers = (config, pathways, pathwayManager) => {
|
|
|
130
115
|
|
|
131
116
|
const pathwayManagerResolvers = pathwayManager?.getResolvers() || {};
|
|
132
117
|
|
|
133
|
-
const executeWorkspaceResolver = async (_, args, contextValue, info) => {
|
|
134
|
-
const startTime = Date.now();
|
|
135
|
-
const requestId = uuidv4();
|
|
136
|
-
const { userId, pathwayName, promptNames, ...pathwayArgs } = args;
|
|
137
|
-
|
|
138
|
-
logger.info(`>>> [${requestId}] executeWorkspace started - userId: ${userId}, pathwayName: ${pathwayName}, promptNames: ${promptNames?.join(',') || 'none'}`);
|
|
139
|
-
|
|
140
|
-
try {
|
|
141
|
-
contextValue.config = config;
|
|
142
|
-
|
|
143
|
-
// Get the base pathway from the user
|
|
144
|
-
const pathways = await pathwayManager.getLatestPathways();
|
|
145
|
-
|
|
146
|
-
if (!pathways[userId] || !pathways[userId][pathwayName]) {
|
|
147
|
-
const error = new Error(`Pathway '${pathwayName}' not found for user '${userId}'`);
|
|
148
|
-
logger.error(`!!! [${requestId}] ${error.message} - Available users: ${Object.keys(pathways).join(', ')}`);
|
|
149
|
-
throw error;
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
const basePathway = pathways[userId][pathwayName];
|
|
153
|
-
logger.debug(`[${requestId}] Found pathway: ${pathwayName} for user: ${userId}`);
|
|
154
|
-
|
|
155
|
-
// If promptNames is specified, use getPathways to get individual pathways and execute in parallel
|
|
156
|
-
if (promptNames && promptNames.length > 0) {
|
|
157
|
-
|
|
158
|
-
// Check if the prompts are in legacy format (array of strings)
|
|
159
|
-
// If so, we can't use promptNames filtering and need to ask user to republish
|
|
160
|
-
if (pathwayManager.isLegacyPromptFormat(userId, pathwayName)) {
|
|
161
|
-
const error = new Error(
|
|
162
|
-
`The pathway '${pathwayName}' uses legacy prompt format (array of strings) which doesn't support the promptNames parameter. ` +
|
|
163
|
-
`Please unpublish and republish your workspace to upgrade to the new format that supports named prompts.`
|
|
164
|
-
);
|
|
165
|
-
logger.error(`!!! [${requestId}] ${error.message}`);
|
|
166
|
-
throw error;
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
// Handle wildcard case - execute all prompts in parallel
|
|
170
|
-
if (promptNames.includes('*')) {
|
|
171
|
-
logger.info(`[${requestId}] Executing all prompts in parallel (wildcard specified)`);
|
|
172
|
-
const individualPathways = await pathwayManager.getPathways(basePathway);
|
|
173
|
-
|
|
174
|
-
if (individualPathways.length === 0) {
|
|
175
|
-
const error = new Error(`No prompts found in pathway '${pathwayName}'`);
|
|
176
|
-
logger.error(`!!! [${requestId}] ${error.message}`);
|
|
177
|
-
throw error;
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
// Execute all pathways in parallel
|
|
181
|
-
logger.debug(`[${requestId}] Executing ${individualPathways.length} pathways in parallel`);
|
|
182
|
-
const results = await Promise.all(
|
|
183
|
-
individualPathways.map(async (pathway, index) => {
|
|
184
|
-
try {
|
|
185
|
-
logger.debug(`[${requestId}] Starting pathway ${index + 1}/${individualPathways.length}: ${pathway.name || 'unnamed'}`);
|
|
186
|
-
const pathwayContext = { ...contextValue, pathway };
|
|
187
|
-
const result = await pathway.rootResolver(null, pathwayArgs, pathwayContext, info);
|
|
188
|
-
logger.debug(`[${requestId}] Completed pathway ${index + 1}/${individualPathways.length}: ${pathway.name || 'unnamed'}`);
|
|
189
|
-
return {
|
|
190
|
-
result: result.result,
|
|
191
|
-
promptName: pathway.name || `prompt_${index + 1}`
|
|
192
|
-
};
|
|
193
|
-
} catch (error) {
|
|
194
|
-
logger.error(`!!! [${requestId}] Error in pathway ${index + 1}/${individualPathways.length}: ${pathway.name || 'unnamed'} - ${error.message}`);
|
|
195
|
-
logger.debug(`[${requestId}] Error stack: ${error.stack}`);
|
|
196
|
-
throw error;
|
|
197
|
-
}
|
|
198
|
-
})
|
|
199
|
-
);
|
|
200
|
-
|
|
201
|
-
const duration = Date.now() - startTime;
|
|
202
|
-
logger.info(`<<< [${requestId}] executeWorkspace completed successfully in ${duration}ms - returned ${results.length} results`);
|
|
203
|
-
|
|
204
|
-
// Return a single result with JSON stringified array of results
|
|
205
|
-
return {
|
|
206
|
-
debug: `Executed ${results.length} prompts in parallel`,
|
|
207
|
-
result: JSON.stringify(results),
|
|
208
|
-
resultData: null,
|
|
209
|
-
previousResult: null,
|
|
210
|
-
warnings: [],
|
|
211
|
-
errors: [],
|
|
212
|
-
contextId: requestId,
|
|
213
|
-
tool: 'executeWorkspace'
|
|
214
|
-
};
|
|
215
|
-
} else {
|
|
216
|
-
// Handle specific prompt names
|
|
217
|
-
logger.info(`[${requestId}] Executing specific prompts: ${promptNames.join(', ')}`);
|
|
218
|
-
const individualPathways = await pathwayManager.getPathways(basePathway, promptNames);
|
|
219
|
-
|
|
220
|
-
if (individualPathways.length === 0) {
|
|
221
|
-
const error = new Error(`No prompts found matching the specified names: ${promptNames.join(', ')}`);
|
|
222
|
-
logger.error(`!!! [${requestId}] ${error.message}`);
|
|
223
|
-
throw error;
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
// Execute all pathways in parallel
|
|
227
|
-
logger.debug(`[${requestId}] Executing ${individualPathways.length} pathways in parallel`);
|
|
228
|
-
const results = await Promise.all(
|
|
229
|
-
individualPathways.map(async (pathway, index) => {
|
|
230
|
-
try {
|
|
231
|
-
logger.debug(`[${requestId}] Starting pathway ${index + 1}/${individualPathways.length}: ${pathway.name || 'unnamed'}`);
|
|
232
|
-
const pathwayContext = { ...contextValue, pathway };
|
|
233
|
-
const result = await pathway.rootResolver(null, pathwayArgs, pathwayContext, info);
|
|
234
|
-
logger.debug(`[${requestId}] Completed pathway ${index + 1}/${individualPathways.length}: ${pathway.name || 'unnamed'}`);
|
|
235
|
-
return {
|
|
236
|
-
result: result.result,
|
|
237
|
-
promptName: pathway.name || `prompt_${index + 1}`
|
|
238
|
-
};
|
|
239
|
-
} catch (error) {
|
|
240
|
-
logger.error(`!!! [${requestId}] Error in pathway ${index + 1}/${individualPathways.length}: ${pathway.name || 'unnamed'} - ${error.message}`);
|
|
241
|
-
logger.debug(`[${requestId}] Error stack: ${error.stack}`);
|
|
242
|
-
throw error;
|
|
243
|
-
}
|
|
244
|
-
})
|
|
245
|
-
);
|
|
246
|
-
|
|
247
|
-
const duration = Date.now() - startTime;
|
|
248
|
-
logger.info(`<<< [${requestId}] executeWorkspace completed successfully in ${duration}ms - returned ${results.length} results`);
|
|
249
|
-
|
|
250
|
-
// Return a single result with JSON stringified array of results (consistent with wildcard case)
|
|
251
|
-
return {
|
|
252
|
-
debug: `Executed ${results.length} specific prompts in parallel: ${promptNames.join(', ')}`,
|
|
253
|
-
result: JSON.stringify(results),
|
|
254
|
-
resultData: null,
|
|
255
|
-
previousResult: null,
|
|
256
|
-
warnings: [],
|
|
257
|
-
errors: [],
|
|
258
|
-
contextId: requestId,
|
|
259
|
-
tool: 'executeWorkspace'
|
|
260
|
-
};
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
// Default behavior: execute all prompts in sequence
|
|
265
|
-
logger.info(`[${requestId}] Executing prompts in sequence`);
|
|
266
|
-
const userPathway = await pathwayManager.getPathway(userId, pathwayName);
|
|
267
|
-
contextValue.pathway = userPathway;
|
|
268
|
-
|
|
269
|
-
const result = await userPathway.rootResolver(null, pathwayArgs, contextValue, info);
|
|
270
|
-
const duration = Date.now() - startTime;
|
|
271
|
-
logger.info(`<<< [${requestId}] executeWorkspace completed successfully in ${duration}ms - returned 1 result`);
|
|
272
|
-
return result; // Return single result directly
|
|
273
|
-
|
|
274
|
-
} catch (error) {
|
|
275
|
-
const duration = Date.now() - startTime;
|
|
276
|
-
logger.error(`!!! [${requestId}] executeWorkspace failed after ${duration}ms`);
|
|
277
|
-
logger.error(`!!! [${requestId}] Error type: ${error.constructor.name}`);
|
|
278
|
-
logger.error(`!!! [${requestId}] Error message: ${error.message}`);
|
|
279
|
-
logger.error(`!!! [${requestId}] Error stack: ${error.stack}`);
|
|
280
|
-
|
|
281
|
-
// Log additional context for debugging "memory access out of bounds" errors
|
|
282
|
-
if (error.message && error.message.includes('memory')) {
|
|
283
|
-
logger.error(`!!! [${requestId}] MEMORY ERROR DETECTED - Additional context:`);
|
|
284
|
-
logger.error(`!!! [${requestId}] - Node.js version: ${process.version}`);
|
|
285
|
-
logger.error(`!!! [${requestId}] - Memory usage: ${JSON.stringify(process.memoryUsage())}`);
|
|
286
|
-
logger.error(`!!! [${requestId}] - Args size estimate: ${JSON.stringify(args).length} chars`);
|
|
287
|
-
logger.error(`!!! [${requestId}] - PathwayArgs keys: ${Object.keys(pathwayArgs).join(', ')}`);
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
throw error;
|
|
291
|
-
}
|
|
292
|
-
};
|
|
293
|
-
|
|
294
118
|
const resolvers = {
|
|
295
119
|
Query: {
|
|
296
120
|
...resolverFunctions,
|
|
297
|
-
executeWorkspace:
|
|
121
|
+
executeWorkspace: (parent, args, contextValue, info) =>
|
|
122
|
+
executeWorkspaceResolver(parent, args, contextValue, info, config, pathwayManager)
|
|
298
123
|
},
|
|
299
124
|
Mutation: {
|
|
300
125
|
'cancelRequest': cancelRequestResolver,
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PathwayManager File Handling Tests
|
|
3
|
+
*
|
|
4
|
+
* This test suite validates the PathwayManager's file handling functionality, specifically
|
|
5
|
+
* testing how the manager processes and transforms pathway prompts that include file attachments.
|
|
6
|
+
*
|
|
7
|
+
* Key functionality tested:
|
|
8
|
+
* - File hash transformation from 'files' to 'fileHashes' property
|
|
9
|
+
* - Collection and deduplication of file hashes at the pathway level
|
|
10
|
+
* - Backward compatibility with legacy string-based prompts without file attachments
|
|
11
|
+
* - Handling of edge cases (null, undefined, empty file arrays)
|
|
12
|
+
* - Prompt object creation with file metadata
|
|
13
|
+
*
|
|
14
|
+
* The PathwayManager allows prompts to reference files by their hashes, which are then
|
|
15
|
+
* processed and made available to the execution context. This test suite ensures that
|
|
16
|
+
* file metadata is correctly preserved, transformed, and aggregated during pathway processing.
|
|
17
|
+
*
|
|
18
|
+
* Test scenarios covered:
|
|
19
|
+
* 1. Prompts with multiple file attachments
|
|
20
|
+
* 2. Prompts with empty or missing file arrays
|
|
21
|
+
* 3. Legacy string prompts (no file support)
|
|
22
|
+
* 4. Duplicate file hash deduplication
|
|
23
|
+
* 5. Null/undefined file handling
|
|
24
|
+
* 6. Direct prompt object creation with files
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
import test from 'ava';
|
|
28
|
+
import PathwayManager from '../../../lib/pathwayManager.js';
|
|
29
|
+
|
|
30
|
+
// Mock config for PathwayManager
|
|
31
|
+
const mockConfig = {
|
|
32
|
+
storageType: 'local',
|
|
33
|
+
filePath: './test-pathways.json',
|
|
34
|
+
publishKey: 'test-key'
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
// Mock base pathway
|
|
38
|
+
const mockBasePathway = {
|
|
39
|
+
name: 'base',
|
|
40
|
+
prompt: '{{text}}',
|
|
41
|
+
systemPrompt: '',
|
|
42
|
+
inputParameters: {},
|
|
43
|
+
typeDef: 'type Test { test: String }',
|
|
44
|
+
rootResolver: () => {},
|
|
45
|
+
resolver: () => {}
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
// Mock storage strategy
|
|
49
|
+
class MockStorageStrategy {
|
|
50
|
+
async load() {
|
|
51
|
+
return {};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
async save(data) {
|
|
55
|
+
// Do nothing
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
async getLastModified() {
|
|
59
|
+
return Date.now();
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
test('pathwayManager handles prompt format with files correctly', async t => {
|
|
64
|
+
const pathwayManager = new PathwayManager(mockConfig, mockBasePathway);
|
|
65
|
+
|
|
66
|
+
// Replace storage with mock
|
|
67
|
+
pathwayManager.storage = new MockStorageStrategy();
|
|
68
|
+
|
|
69
|
+
// Test prompts with files
|
|
70
|
+
const pathwayWithFiles = {
|
|
71
|
+
prompt: [
|
|
72
|
+
{
|
|
73
|
+
name: 'Analyze Document',
|
|
74
|
+
prompt: 'Please analyze the provided document',
|
|
75
|
+
files: ['abc123def456', 'def456ghi789']
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
name: 'Summarize Text',
|
|
79
|
+
prompt: 'Please summarize the text',
|
|
80
|
+
files: []
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
name: 'Simple Task',
|
|
84
|
+
prompt: 'Perform a simple task'
|
|
85
|
+
// No files property
|
|
86
|
+
}
|
|
87
|
+
],
|
|
88
|
+
systemPrompt: 'You are a helpful assistant'
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
// Test transformPrompts method
|
|
92
|
+
const transformedPathway = await pathwayManager.transformPrompts(pathwayWithFiles);
|
|
93
|
+
|
|
94
|
+
// Verify the transformed pathway structure
|
|
95
|
+
t.truthy(transformedPathway);
|
|
96
|
+
t.true(Array.isArray(transformedPathway.prompt));
|
|
97
|
+
t.is(transformedPathway.prompt.length, 3);
|
|
98
|
+
|
|
99
|
+
// Verify first prompt with files
|
|
100
|
+
const firstPrompt = transformedPathway.prompt[0];
|
|
101
|
+
t.is(firstPrompt.name, 'Analyze Document');
|
|
102
|
+
t.truthy(firstPrompt.fileHashes);
|
|
103
|
+
t.deepEqual(firstPrompt.fileHashes, ['abc123def456', 'def456ghi789']);
|
|
104
|
+
|
|
105
|
+
// Verify second prompt with empty files
|
|
106
|
+
const secondPrompt = transformedPathway.prompt[1];
|
|
107
|
+
t.is(secondPrompt.name, 'Summarize Text');
|
|
108
|
+
t.falsy(secondPrompt.fileHashes); // Empty array results in no fileHashes property
|
|
109
|
+
|
|
110
|
+
// Verify third prompt without files property
|
|
111
|
+
const thirdPrompt = transformedPathway.prompt[2];
|
|
112
|
+
t.is(thirdPrompt.name, 'Simple Task');
|
|
113
|
+
t.falsy(thirdPrompt.fileHashes);
|
|
114
|
+
|
|
115
|
+
// Verify pathway-level file hashes collection
|
|
116
|
+
t.truthy(transformedPathway.fileHashes);
|
|
117
|
+
t.deepEqual(transformedPathway.fileHashes, ['abc123def456', 'def456ghi789']);
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
test('pathwayManager handles legacy string prompts correctly', async t => {
|
|
121
|
+
const pathwayManager = new PathwayManager(mockConfig, mockBasePathway);
|
|
122
|
+
|
|
123
|
+
// Replace storage with mock
|
|
124
|
+
pathwayManager.storage = new MockStorageStrategy();
|
|
125
|
+
|
|
126
|
+
// Test legacy string prompts
|
|
127
|
+
const legacyPathway = {
|
|
128
|
+
prompt: [
|
|
129
|
+
'Please analyze the data',
|
|
130
|
+
'Summarize the findings'
|
|
131
|
+
],
|
|
132
|
+
systemPrompt: 'You are a helpful assistant'
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
// Test transformPrompts method
|
|
136
|
+
const transformedPathway = await pathwayManager.transformPrompts(legacyPathway);
|
|
137
|
+
|
|
138
|
+
// Verify the transformed pathway structure
|
|
139
|
+
t.truthy(transformedPathway);
|
|
140
|
+
t.true(Array.isArray(transformedPathway.prompt));
|
|
141
|
+
t.is(transformedPathway.prompt.length, 2);
|
|
142
|
+
|
|
143
|
+
// Verify prompts don't have file hashes
|
|
144
|
+
transformedPathway.prompt.forEach(prompt => {
|
|
145
|
+
t.falsy(prompt.fileHashes);
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
// Verify no pathway-level file hashes
|
|
149
|
+
t.falsy(transformedPathway.fileHashes);
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
test('pathwayManager removes duplicate file hashes at pathway level', async t => {
|
|
153
|
+
const pathwayManager = new PathwayManager(mockConfig, mockBasePathway);
|
|
154
|
+
|
|
155
|
+
// Replace storage with mock
|
|
156
|
+
pathwayManager.storage = new MockStorageStrategy();
|
|
157
|
+
|
|
158
|
+
// Test prompts with duplicate file hashes
|
|
159
|
+
const pathwayWithDuplicateFiles = {
|
|
160
|
+
prompt: [
|
|
161
|
+
{
|
|
162
|
+
name: 'First Task',
|
|
163
|
+
prompt: 'Analyze document 1',
|
|
164
|
+
files: ['abc123def456', 'def456ghi789']
|
|
165
|
+
},
|
|
166
|
+
{
|
|
167
|
+
name: 'Second Task',
|
|
168
|
+
prompt: 'Analyze document 2',
|
|
169
|
+
files: ['abc123def456', 'ghi789jkl012'] // abc123def456 is duplicate
|
|
170
|
+
}
|
|
171
|
+
],
|
|
172
|
+
systemPrompt: 'You are a helpful assistant'
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
// Test transformPrompts method
|
|
176
|
+
const transformedPathway = await pathwayManager.transformPrompts(pathwayWithDuplicateFiles);
|
|
177
|
+
|
|
178
|
+
// Verify pathway-level file hashes are deduplicated
|
|
179
|
+
t.truthy(transformedPathway.fileHashes);
|
|
180
|
+
t.deepEqual(transformedPathway.fileHashes, ['abc123def456', 'def456ghi789', 'ghi789jkl012']);
|
|
181
|
+
t.is(transformedPathway.fileHashes.length, 3); // Should not have duplicates
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
test('pathwayManager handles null and undefined files gracefully', async t => {
|
|
185
|
+
const pathwayManager = new PathwayManager(mockConfig, mockBasePathway);
|
|
186
|
+
|
|
187
|
+
// Replace storage with mock
|
|
188
|
+
pathwayManager.storage = new MockStorageStrategy();
|
|
189
|
+
|
|
190
|
+
// Test prompts with null/undefined files
|
|
191
|
+
const pathwayWithNullFiles = {
|
|
192
|
+
prompt: [
|
|
193
|
+
{
|
|
194
|
+
name: 'Task with null files',
|
|
195
|
+
prompt: 'Do something',
|
|
196
|
+
files: null
|
|
197
|
+
},
|
|
198
|
+
{
|
|
199
|
+
name: 'Task with undefined files',
|
|
200
|
+
prompt: 'Do something else'
|
|
201
|
+
// files property is undefined
|
|
202
|
+
}
|
|
203
|
+
],
|
|
204
|
+
systemPrompt: 'You are a helpful assistant'
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
// Test transformPrompts method
|
|
208
|
+
const transformedPathway = await pathwayManager.transformPrompts(pathwayWithNullFiles);
|
|
209
|
+
|
|
210
|
+
// Verify the transformation handles null/undefined gracefully
|
|
211
|
+
t.truthy(transformedPathway);
|
|
212
|
+
t.true(Array.isArray(transformedPathway.prompt));
|
|
213
|
+
|
|
214
|
+
// Both prompts should have empty or undefined fileHashes
|
|
215
|
+
transformedPathway.prompt.forEach(prompt => {
|
|
216
|
+
t.true(!prompt.fileHashes || prompt.fileHashes.length === 0);
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
// No pathway-level file hashes should be set
|
|
220
|
+
t.falsy(transformedPathway.fileHashes);
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
test('pathwayManager _createPromptObject handles files correctly', t => {
|
|
224
|
+
const pathwayManager = new PathwayManager(mockConfig, mockBasePathway);
|
|
225
|
+
|
|
226
|
+
// Test with object prompt containing files
|
|
227
|
+
const promptWithFiles = {
|
|
228
|
+
name: 'Test Prompt',
|
|
229
|
+
prompt: 'Analyze this document',
|
|
230
|
+
files: ['file1hash', 'file2hash']
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
const createdPrompt = pathwayManager._createPromptObject(promptWithFiles, 'System prompt');
|
|
234
|
+
|
|
235
|
+
t.is(createdPrompt.name, 'Test Prompt');
|
|
236
|
+
t.truthy(createdPrompt.fileHashes);
|
|
237
|
+
t.deepEqual(createdPrompt.fileHashes, ['file1hash', 'file2hash']);
|
|
238
|
+
|
|
239
|
+
// Test with string prompt (no files)
|
|
240
|
+
const stringPrompt = 'Simple text prompt';
|
|
241
|
+
const createdStringPrompt = pathwayManager._createPromptObject(stringPrompt, 'System prompt', 'Default Name');
|
|
242
|
+
|
|
243
|
+
t.is(createdStringPrompt.name, 'Default Name');
|
|
244
|
+
t.falsy(createdStringPrompt.fileHashes);
|
|
245
|
+
|
|
246
|
+
// Test with object prompt without files
|
|
247
|
+
const promptWithoutFiles = {
|
|
248
|
+
name: 'No Files Prompt',
|
|
249
|
+
prompt: 'Simple task'
|
|
250
|
+
};
|
|
251
|
+
|
|
252
|
+
const createdPromptNoFiles = pathwayManager._createPromptObject(promptWithoutFiles, 'System prompt');
|
|
253
|
+
|
|
254
|
+
t.is(createdPromptNoFiles.name, 'No Files Prompt');
|
|
255
|
+
t.falsy(createdPromptNoFiles.fileHashes);
|
|
256
|
+
});
|