@aj-archipelago/cortex 1.3.65 → 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/entityConstants.js +1 -1
- package/lib/pathwayManager.js +42 -8
- package/lib/pathwayTools.js +3 -3
- package/lib/util.js +58 -2
- package/package.json +1 -1
- package/pathways/system/entity/memory/sys_memory_format.js +1 -0
- package/pathways/system/entity/memory/sys_memory_manager.js +3 -3
- package/pathways/system/entity/sys_entity_start.js +1 -1
- package/pathways/system/entity/tools/sys_tool_bing_search_afagent.js +2 -0
- package/pathways/system/entity/tools/sys_tool_codingagent.js +2 -2
- package/pathways/system/entity/tools/sys_tool_google_search.js +3 -3
- package/pathways/system/entity/tools/sys_tool_grok_x_search.js +12 -2
- package/pathways/system/workspaces/run_workspace_prompt.js +0 -3
- package/server/executeWorkspace.js +381 -0
- package/server/graphql.js +5 -180
- package/server/pathwayResolver.js +3 -3
- package/server/plugins/apptekTranslatePlugin.js +2 -2
- package/server/plugins/azureFoundryAgentsPlugin.js +1 -1
- 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
|
@@ -0,0 +1,381 @@
|
|
|
1
|
+
// executeWorkspace.js
|
|
2
|
+
// Handles the executeWorkspace GraphQL query resolver and related functionality
|
|
3
|
+
//
|
|
4
|
+
// This module contains the implementation of the executeWorkspace resolver, which is responsible
|
|
5
|
+
// for executing user-defined pathways (workspaces) with various execution modes:
|
|
6
|
+
// - Sequential execution of all prompts (default)
|
|
7
|
+
// - Parallel execution of specific named prompts
|
|
8
|
+
// - Parallel execution of all prompts (wildcard mode)
|
|
9
|
+
//
|
|
10
|
+
// The resolver supports both legacy pathway formats and new dynamic pathways with cortexPathwayName.
|
|
11
|
+
|
|
12
|
+
import { v4 as uuidv4 } from 'uuid';
|
|
13
|
+
import logger from '../lib/logger.js';
|
|
14
|
+
import { callPathway } from '../lib/pathwayTools.js';
|
|
15
|
+
import { getPathwayTypeDef, userPathwayInputParameters } from './typeDef.js';
|
|
16
|
+
|
|
17
|
+
// Helper function to resolve file hashes and add them to chatHistory
|
|
18
|
+
const resolveAndAddFileContent = async (pathways, pathwayArgs, requestId, config) => {
|
|
19
|
+
let fileContentAdded = false;
|
|
20
|
+
|
|
21
|
+
// Check if any pathway has file hashes
|
|
22
|
+
const pathwaysWithFiles = Array.isArray(pathways) ? pathways : [pathways];
|
|
23
|
+
|
|
24
|
+
for (const pathway of pathwaysWithFiles) {
|
|
25
|
+
if (pathway.fileHashes && pathway.fileHashes.length > 0) {
|
|
26
|
+
try {
|
|
27
|
+
const { resolveFileHashesToContent } = await import('../lib/util.js');
|
|
28
|
+
const fileContent = await resolveFileHashesToContent(pathway.fileHashes, config);
|
|
29
|
+
|
|
30
|
+
// Add file content to chatHistory if not already present (only do this once)
|
|
31
|
+
if (!fileContentAdded) {
|
|
32
|
+
// Initialize chatHistory if it doesn't exist
|
|
33
|
+
if (!pathwayArgs.chatHistory) {
|
|
34
|
+
pathwayArgs.chatHistory = [];
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Find the last user message or create one
|
|
38
|
+
let lastUserMessage = null;
|
|
39
|
+
for (let i = pathwayArgs.chatHistory.length - 1; i >= 0; i--) {
|
|
40
|
+
if (pathwayArgs.chatHistory[i].role === 'user') {
|
|
41
|
+
lastUserMessage = pathwayArgs.chatHistory[i];
|
|
42
|
+
break;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (!lastUserMessage) {
|
|
47
|
+
lastUserMessage = {
|
|
48
|
+
role: 'user',
|
|
49
|
+
content: []
|
|
50
|
+
};
|
|
51
|
+
pathwayArgs.chatHistory.push(lastUserMessage);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Ensure content is an array
|
|
55
|
+
if (!Array.isArray(lastUserMessage.content)) {
|
|
56
|
+
lastUserMessage.content = [
|
|
57
|
+
JSON.stringify({
|
|
58
|
+
type: "text",
|
|
59
|
+
text: lastUserMessage.content || ""
|
|
60
|
+
})
|
|
61
|
+
];
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Add file content
|
|
65
|
+
lastUserMessage.content.push(...fileContent);
|
|
66
|
+
fileContentAdded = true;
|
|
67
|
+
}
|
|
68
|
+
} catch (error) {
|
|
69
|
+
logger.error(`[${requestId}] Failed to resolve file hashes for pathway ${pathway.name || 'unnamed'}: ${error.message}`);
|
|
70
|
+
// Continue execution without files
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Only process files once for multiple pathways
|
|
74
|
+
if (fileContentAdded) break;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return fileContentAdded;
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
// Helper function to execute pathway with cortex pathway name or fallback to legacy
|
|
82
|
+
const executePathwayWithFallback = async (pathway, pathwayArgs, contextValue, info, requestId, originalPrompt = null, config) => {
|
|
83
|
+
const cortexPathwayName = (originalPrompt && typeof originalPrompt === 'object' && originalPrompt.cortexPathwayName)
|
|
84
|
+
? originalPrompt.cortexPathwayName
|
|
85
|
+
: null;
|
|
86
|
+
|
|
87
|
+
if (cortexPathwayName) {
|
|
88
|
+
// Use the specific cortex pathway
|
|
89
|
+
// Transform parameters for cortex pathway
|
|
90
|
+
const cortexArgs = {
|
|
91
|
+
model: pathway.model || pathwayArgs.model || "labeeb-agent", // Use pathway model or default
|
|
92
|
+
chatHistory: [],
|
|
93
|
+
systemPrompt: pathway.systemPrompt
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
// If we have existing chatHistory, use it as base
|
|
97
|
+
if (pathwayArgs.chatHistory && pathwayArgs.chatHistory.length > 0) {
|
|
98
|
+
cortexArgs.chatHistory = JSON.parse(JSON.stringify(pathwayArgs.chatHistory));
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// If we have text parameter, we need to add it to the chatHistory
|
|
102
|
+
if (pathwayArgs.text) {
|
|
103
|
+
// Find the last user message or create a new one
|
|
104
|
+
let lastUserMessage = null;
|
|
105
|
+
for (let i = cortexArgs.chatHistory.length - 1; i >= 0; i--) {
|
|
106
|
+
if (cortexArgs.chatHistory[i].role === 'user') {
|
|
107
|
+
lastUserMessage = cortexArgs.chatHistory[i];
|
|
108
|
+
break;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (lastUserMessage) {
|
|
113
|
+
// Ensure content is an array
|
|
114
|
+
if (!Array.isArray(lastUserMessage.content)) {
|
|
115
|
+
lastUserMessage.content = [JSON.stringify({
|
|
116
|
+
type: "text",
|
|
117
|
+
text: lastUserMessage.content || ""
|
|
118
|
+
})];
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Add the text parameter as a text content item
|
|
122
|
+
const textFromPrompt = originalPrompt?.prompt || pathwayArgs.text;
|
|
123
|
+
lastUserMessage.content.unshift(JSON.stringify({
|
|
124
|
+
type: "text",
|
|
125
|
+
text: `${pathwayArgs.text}\n\n${textFromPrompt}`
|
|
126
|
+
}));
|
|
127
|
+
} else {
|
|
128
|
+
// Create new user message with text
|
|
129
|
+
const textFromPrompt = originalPrompt?.prompt || pathwayArgs.text;
|
|
130
|
+
cortexArgs.chatHistory.push({
|
|
131
|
+
role: 'user',
|
|
132
|
+
content: [JSON.stringify({
|
|
133
|
+
type: "text",
|
|
134
|
+
text: `${pathwayArgs.text}\n\n${textFromPrompt}`
|
|
135
|
+
})]
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Create a pathwayResolver to capture extended data like artifacts
|
|
141
|
+
const { PathwayResolver } = await import('./pathwayResolver.js');
|
|
142
|
+
const cortexPathway = config.get(`pathways.${cortexPathwayName}`);
|
|
143
|
+
if (!cortexPathway) {
|
|
144
|
+
throw new Error(`Cortex pathway ${cortexPathwayName} not found`);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const pathwayResolver = new PathwayResolver({
|
|
148
|
+
config,
|
|
149
|
+
pathway: cortexPathway,
|
|
150
|
+
args: cortexArgs
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
const result = await callPathway(cortexPathwayName, cortexArgs, pathwayResolver);
|
|
154
|
+
|
|
155
|
+
// Extract resultData from pathwayResolver (includes artifacts and other extended data)
|
|
156
|
+
const resultData = pathwayResolver.pathwayResultData
|
|
157
|
+
? JSON.stringify(pathwayResolver.pathwayResultData)
|
|
158
|
+
: null;
|
|
159
|
+
|
|
160
|
+
// Return result with extended data
|
|
161
|
+
return {
|
|
162
|
+
result,
|
|
163
|
+
resultData,
|
|
164
|
+
warnings: pathwayResolver.warnings,
|
|
165
|
+
errors: pathwayResolver.errors
|
|
166
|
+
};
|
|
167
|
+
} else {
|
|
168
|
+
// Fallback to original pathway execution for legacy prompts
|
|
169
|
+
const pathwayContext = { ...contextValue, pathway };
|
|
170
|
+
return await pathway.rootResolver(null, pathwayArgs, pathwayContext, info);
|
|
171
|
+
}
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
// Main executeWorkspace resolver
|
|
175
|
+
export const executeWorkspaceResolver = async (_, args, contextValue, info, config, pathwayManager) => {
|
|
176
|
+
const startTime = Date.now();
|
|
177
|
+
const requestId = uuidv4();
|
|
178
|
+
const { userId, pathwayName, promptNames, ...pathwayArgs } = args;
|
|
179
|
+
|
|
180
|
+
logger.info(`>>> [${requestId}] executeWorkspace started - userId: ${userId}, pathwayName: ${pathwayName}, promptNames: ${promptNames?.join(',') || 'none'}`);
|
|
181
|
+
|
|
182
|
+
try {
|
|
183
|
+
contextValue.config = config;
|
|
184
|
+
|
|
185
|
+
// Get the base pathway from the user
|
|
186
|
+
const pathways = await pathwayManager.getLatestPathways();
|
|
187
|
+
|
|
188
|
+
if (!pathways[userId] || !pathways[userId][pathwayName]) {
|
|
189
|
+
const error = new Error(`Pathway '${pathwayName}' not found for user '${userId}'`);
|
|
190
|
+
logger.error(`!!! [${requestId}] ${error.message} - Available users: ${Object.keys(pathways).join(', ')}`);
|
|
191
|
+
throw error;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
const basePathway = pathways[userId][pathwayName];
|
|
195
|
+
|
|
196
|
+
// If promptNames is specified, use getPathways to get individual pathways and execute in parallel
|
|
197
|
+
if (promptNames && promptNames.length > 0) {
|
|
198
|
+
|
|
199
|
+
// Check if the prompts are in legacy format (array of strings)
|
|
200
|
+
// If so, we can't use promptNames filtering and need to ask user to republish
|
|
201
|
+
if (pathwayManager.isLegacyPromptFormat(userId, pathwayName)) {
|
|
202
|
+
const error = new Error(
|
|
203
|
+
`The pathway '${pathwayName}' uses legacy prompt format (array of strings) which doesn't support the promptNames parameter. ` +
|
|
204
|
+
`Please unpublish and republish your workspace to upgrade to the new format that supports named prompts.`
|
|
205
|
+
);
|
|
206
|
+
logger.error(`!!! [${requestId}] ${error.message}`);
|
|
207
|
+
throw error;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// Handle wildcard case - execute all prompts in parallel
|
|
211
|
+
if (promptNames.includes('*')) {
|
|
212
|
+
logger.info(`[${requestId}] Executing all prompts in parallel (wildcard specified)`);
|
|
213
|
+
const individualPathways = await pathwayManager.getPathways(basePathway);
|
|
214
|
+
|
|
215
|
+
if (individualPathways.length === 0) {
|
|
216
|
+
const error = new Error(`No prompts found in pathway '${pathwayName}'`);
|
|
217
|
+
logger.error(`!!! [${requestId}] ${error.message}`);
|
|
218
|
+
throw error;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// Resolve file content for any pathways that have file hashes
|
|
222
|
+
await resolveAndAddFileContent(individualPathways, pathwayArgs, requestId, config);
|
|
223
|
+
|
|
224
|
+
// Execute all pathways in parallel
|
|
225
|
+
const results = await Promise.all(
|
|
226
|
+
individualPathways.map(async (pathway, index) => {
|
|
227
|
+
try {
|
|
228
|
+
// Check if the prompt has a cortexPathwayName (new format)
|
|
229
|
+
const originalPrompt = basePathway.prompt[index];
|
|
230
|
+
|
|
231
|
+
const result = await executePathwayWithFallback(pathway, pathwayArgs, contextValue, info, requestId, originalPrompt, config);
|
|
232
|
+
|
|
233
|
+
return {
|
|
234
|
+
result: result.result,
|
|
235
|
+
resultData: result.resultData,
|
|
236
|
+
warnings: result.warnings,
|
|
237
|
+
errors: result.errors,
|
|
238
|
+
promptName: pathway.name || `prompt_${index + 1}`
|
|
239
|
+
};
|
|
240
|
+
} catch (error) {
|
|
241
|
+
logger.error(`!!! [${requestId}] Error in pathway ${index + 1}/${individualPathways.length}: ${pathway.name || 'unnamed'} - ${error.message}`);
|
|
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
|
|
251
|
+
return {
|
|
252
|
+
debug: `Executed ${results.length} prompts in parallel`,
|
|
253
|
+
result: JSON.stringify(results),
|
|
254
|
+
resultData: null,
|
|
255
|
+
previousResult: null,
|
|
256
|
+
warnings: [],
|
|
257
|
+
errors: [],
|
|
258
|
+
contextId: requestId,
|
|
259
|
+
tool: 'executeWorkspace'
|
|
260
|
+
};
|
|
261
|
+
} else {
|
|
262
|
+
// Handle specific prompt names
|
|
263
|
+
logger.info(`[${requestId}] Executing specific prompts: ${promptNames.join(', ')}`);
|
|
264
|
+
const individualPathways = await pathwayManager.getPathways(basePathway, promptNames);
|
|
265
|
+
|
|
266
|
+
if (individualPathways.length === 0) {
|
|
267
|
+
const error = new Error(`No prompts found matching the specified names: ${promptNames.join(', ')}`);
|
|
268
|
+
logger.error(`!!! [${requestId}] ${error.message}`);
|
|
269
|
+
throw error;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// Resolve file content for any pathways that have file hashes
|
|
273
|
+
await resolveAndAddFileContent(individualPathways, pathwayArgs, requestId, config);
|
|
274
|
+
|
|
275
|
+
// Execute all pathways in parallel
|
|
276
|
+
const results = await Promise.all(
|
|
277
|
+
individualPathways.map(async (pathway, index) => {
|
|
278
|
+
try {
|
|
279
|
+
// Find the original prompt by name to get the cortexPathwayName
|
|
280
|
+
const originalPrompt = basePathway.prompt.find(p =>
|
|
281
|
+
(typeof p === 'object' && p.name === pathway.name) ||
|
|
282
|
+
(typeof p === 'string' && pathway.name === `prompt_${basePathway.prompt.indexOf(p)}`)
|
|
283
|
+
);
|
|
284
|
+
|
|
285
|
+
const result = await executePathwayWithFallback(pathway, pathwayArgs, contextValue, info, requestId, originalPrompt, config);
|
|
286
|
+
|
|
287
|
+
return {
|
|
288
|
+
result: result.result,
|
|
289
|
+
resultData: result.resultData,
|
|
290
|
+
warnings: result.warnings,
|
|
291
|
+
errors: result.errors,
|
|
292
|
+
promptName: pathway.name || `prompt_${index + 1}`
|
|
293
|
+
};
|
|
294
|
+
} catch (error) {
|
|
295
|
+
logger.error(`!!! [${requestId}] Error in pathway ${index + 1}/${individualPathways.length}: ${pathway.name || 'unnamed'} - ${error.message}`);
|
|
296
|
+
throw error;
|
|
297
|
+
}
|
|
298
|
+
})
|
|
299
|
+
);
|
|
300
|
+
|
|
301
|
+
const duration = Date.now() - startTime;
|
|
302
|
+
logger.info(`<<< [${requestId}] executeWorkspace completed successfully in ${duration}ms - returned ${results.length} results`);
|
|
303
|
+
|
|
304
|
+
// Return a single result with JSON stringified array of results (consistent with wildcard case)
|
|
305
|
+
return {
|
|
306
|
+
debug: `Executed ${results.length} specific prompts in parallel: ${promptNames.join(', ')}`,
|
|
307
|
+
result: JSON.stringify(results),
|
|
308
|
+
resultData: null,
|
|
309
|
+
previousResult: null,
|
|
310
|
+
warnings: [],
|
|
311
|
+
errors: [],
|
|
312
|
+
contextId: requestId,
|
|
313
|
+
tool: 'executeWorkspace'
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
// Default behavior: execute all prompts in sequence
|
|
319
|
+
logger.info(`[${requestId}] Executing prompts in sequence`);
|
|
320
|
+
const userPathway = await pathwayManager.getPathway(userId, pathwayName);
|
|
321
|
+
contextValue.pathway = userPathway;
|
|
322
|
+
|
|
323
|
+
// Handle file hashes if present in the pathway
|
|
324
|
+
await resolveAndAddFileContent(userPathway, pathwayArgs, requestId, config);
|
|
325
|
+
|
|
326
|
+
// Check if any prompt has cortexPathwayName (for dynamic pathways)
|
|
327
|
+
let result;
|
|
328
|
+
if (userPathway.prompt && Array.isArray(userPathway.prompt)) {
|
|
329
|
+
const firstPrompt = userPathway.prompt[0];
|
|
330
|
+
|
|
331
|
+
result = await executePathwayWithFallback(userPathway, pathwayArgs, contextValue, info, requestId, firstPrompt, config);
|
|
332
|
+
} else {
|
|
333
|
+
// No prompt array, use legacy execution
|
|
334
|
+
result = await userPathway.rootResolver(null, pathwayArgs, contextValue, info);
|
|
335
|
+
}
|
|
336
|
+
const duration = Date.now() - startTime;
|
|
337
|
+
logger.info(`<<< [${requestId}] executeWorkspace completed successfully in ${duration}ms - returned 1 result`);
|
|
338
|
+
return result; // Return single result directly
|
|
339
|
+
|
|
340
|
+
} catch (error) {
|
|
341
|
+
const duration = Date.now() - startTime;
|
|
342
|
+
logger.error(`!!! [${requestId}] executeWorkspace failed after ${duration}ms`);
|
|
343
|
+
logger.error(`!!! [${requestId}] Error type: ${error.constructor.name}`);
|
|
344
|
+
logger.error(`!!! [${requestId}] Error message: ${error.message}`);
|
|
345
|
+
logger.error(`!!! [${requestId}] Error stack: ${error.stack}`);
|
|
346
|
+
|
|
347
|
+
// Log additional context for debugging "memory access out of bounds" errors
|
|
348
|
+
if (error.message && error.message.includes('memory')) {
|
|
349
|
+
logger.error(`!!! [${requestId}] MEMORY ERROR DETECTED - Additional context:`);
|
|
350
|
+
logger.error(`!!! [${requestId}] - Node.js version: ${process.version}`);
|
|
351
|
+
logger.error(`!!! [${requestId}] - Memory usage: ${JSON.stringify(process.memoryUsage())}`);
|
|
352
|
+
logger.error(`!!! [${requestId}] - Args size estimate: ${JSON.stringify(args).length} chars`);
|
|
353
|
+
logger.error(`!!! [${requestId}] - PathwayArgs keys: ${Object.keys(pathwayArgs).join(', ')}`);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
throw error;
|
|
357
|
+
}
|
|
358
|
+
};
|
|
359
|
+
|
|
360
|
+
// Type definitions for executeWorkspace
|
|
361
|
+
export const getExecuteWorkspaceTypeDefs = () => {
|
|
362
|
+
return `
|
|
363
|
+
${getPathwayTypeDef('ExecuteWorkspace', 'String')}
|
|
364
|
+
|
|
365
|
+
type ExecuteWorkspaceResult {
|
|
366
|
+
debug: String
|
|
367
|
+
result: String
|
|
368
|
+
resultData: String
|
|
369
|
+
previousResult: String
|
|
370
|
+
warnings: [String]
|
|
371
|
+
errors: [String]
|
|
372
|
+
contextId: String
|
|
373
|
+
tool: String
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
extend type Query {
|
|
377
|
+
executeWorkspace(userId: String!, pathwayName: String!, ${userPathwayInputParameters}): ExecuteWorkspaceResult
|
|
378
|
+
}
|
|
379
|
+
`;
|
|
380
|
+
};
|
|
381
|
+
|
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,
|
|
@@ -293,7 +293,7 @@ class PathwayResolver {
|
|
|
293
293
|
requestProgress = this.modelExecutor.plugin.processStreamEvent(event, requestProgress);
|
|
294
294
|
} catch (error) {
|
|
295
295
|
streamErrorOccurred = true;
|
|
296
|
-
logger.error(`Stream error: ${error.message}`);
|
|
296
|
+
logger.error(`Stream error: ${error instanceof Error ? error.stack || error.message : JSON.stringify(error)}`);
|
|
297
297
|
incomingMessage.off('data', processStream);
|
|
298
298
|
return;
|
|
299
299
|
}
|
|
@@ -304,7 +304,7 @@ class PathwayResolver {
|
|
|
304
304
|
streamEnded = requestProgress.progress === 1;
|
|
305
305
|
}
|
|
306
306
|
} catch (error) {
|
|
307
|
-
logger.error(`Could not publish the stream message: "${event.data}", ${error}`);
|
|
307
|
+
logger.error(`Could not publish the stream message: "${event.data}", ${error instanceof Error ? error.stack || error.message : JSON.stringify(error)}`);
|
|
308
308
|
}
|
|
309
309
|
|
|
310
310
|
}
|
|
@@ -325,7 +325,7 @@ class PathwayResolver {
|
|
|
325
325
|
}
|
|
326
326
|
|
|
327
327
|
} catch (error) {
|
|
328
|
-
logger.error(`Could not subscribe to stream: ${error}`);
|
|
328
|
+
logger.error(`Could not subscribe to stream: ${error instanceof Error ? error.stack || error.message : JSON.stringify(error)}`);
|
|
329
329
|
}
|
|
330
330
|
|
|
331
331
|
if (streamErrorOccurred) {
|
|
@@ -95,7 +95,7 @@ class ApptekTranslatePlugin extends ModelPlugin {
|
|
|
95
95
|
detectedLanguage = result.split('\n')[0].split(';')[0];
|
|
96
96
|
} else {
|
|
97
97
|
logger.error(`Apptek Language detection failed with status: ${resultResponse.status}`);
|
|
98
|
-
logger.debug({
|
|
98
|
+
logger.debug(`Apptek language detection response: ${JSON.stringify({ status: resultResponse.status, textSnippet: text?.slice?.(0, 200) || text })}`)
|
|
99
99
|
}
|
|
100
100
|
|
|
101
101
|
if (!detectedLanguage) {
|
|
@@ -111,7 +111,7 @@ class ApptekTranslatePlugin extends ModelPlugin {
|
|
|
111
111
|
text,
|
|
112
112
|
});
|
|
113
113
|
|
|
114
|
-
logger.verbose(
|
|
114
|
+
logger.verbose(`Successfully used language pathway as fallback: ${JSON.stringify({ detectedLanguage })}`);
|
|
115
115
|
if (!detectedLanguage) {
|
|
116
116
|
throw new Error('Language detection failed using fallback language pathway');
|
|
117
117
|
}
|
|
@@ -182,7 +182,7 @@ class AzureFoundryAgentsPlugin extends ModelPlugin {
|
|
|
182
182
|
|
|
183
183
|
// Check if run failed
|
|
184
184
|
if (runStatus.status === 'failed') {
|
|
185
|
-
logger.error(`[Azure Foundry Agent] Run failed: ${runId}
|
|
185
|
+
logger.error(`[Azure Foundry Agent] Run failed: ${runId} ${runStatus?.lastError ? JSON.stringify(runStatus.lastError) : ''}`);
|
|
186
186
|
return null;
|
|
187
187
|
}
|
|
188
188
|
|