@aj-archipelago/cortex 1.3.64 → 1.3.66
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 +50 -1
- package/lib/entityConstants.js +1 -1
- package/lib/pathwayManager.js +140 -12
- package/lib/pathwayTools.js +3 -3
- package/lib/util.js +1 -1
- 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/server/graphql.js +173 -8
- package/server/pathwayResolver.js +13 -5
- package/server/plugins/apptekTranslatePlugin.js +2 -2
- package/server/plugins/azureFoundryAgentsPlugin.js +1 -1
- package/server/prompt.js +2 -1
- package/server/typeDef.js +1 -1
- package/tests/unit/core/pathwayManager.test.js +768 -0
- package/tests/unit/server/graphql.test.js +170 -0
package/README.md
CHANGED
|
@@ -843,11 +843,60 @@ mutation DeletePathway($name: String!, $userId: String!, $secret: String!, $key:
|
|
|
843
843
|
3. Executing a dynamic pathway:
|
|
844
844
|
|
|
845
845
|
```graphql
|
|
846
|
-
query ExecuteWorkspace($userId: String!, $pathwayName: String!, $text: String
|
|
846
|
+
query ExecuteWorkspace($userId: String!, $pathwayName: String!, $text: String, $promptNames: [String]) {
|
|
847
|
+
executeWorkspace(userId: $userId, pathwayName: $pathwayName, text: $text, promptNames: $promptNames) {
|
|
848
|
+
result
|
|
849
|
+
debug
|
|
850
|
+
resultData
|
|
851
|
+
previousResult
|
|
852
|
+
warnings
|
|
853
|
+
errors
|
|
854
|
+
contextId
|
|
855
|
+
tool
|
|
856
|
+
}
|
|
857
|
+
}
|
|
858
|
+
```
|
|
859
|
+
|
|
860
|
+
**Parameters:**
|
|
861
|
+
- `userId`: The user identifier for the pathway
|
|
862
|
+
- `pathwayName`: The name of the pathway to execute
|
|
863
|
+
- `text`: Optional input text for the pathway
|
|
864
|
+
- `promptNames`: Optional array of specific prompt names to execute
|
|
865
|
+
- If omitted: executes prompts serially (default behavior)
|
|
866
|
+
- If specific names provided: executes only those prompts in parallel
|
|
867
|
+
- If `["*"]`: executes all prompts in parallel
|
|
868
|
+
|
|
869
|
+
**Response Formats:**
|
|
870
|
+
- **Serial execution**: Returns single result string in `result` field
|
|
871
|
+
- **Parallel execution**: Returns JSON stringified array in `result` field:
|
|
872
|
+
```json
|
|
873
|
+
"[{\"result\": \"<prompt 1 result>\", \"promptName\": \"<prompt 1 name>\"}, {\"result\": \"<prompt 2 result>\", \"promptName\": \"<prompt 2 name>\"}]"
|
|
874
|
+
```
|
|
875
|
+
|
|
876
|
+
**Note**: The `executeWorkspace` query returns a single `ExecuteWorkspaceResult` object (not an array). For parallel execution, multiple results are JSON-encoded within the `result` field of this single response object.
|
|
877
|
+
|
|
878
|
+
**Examples:**
|
|
879
|
+
```graphql
|
|
880
|
+
# Execute serially (default)
|
|
881
|
+
query ExecuteWorkspace($userId: String!, $pathwayName: String!, $text: String) {
|
|
847
882
|
executeWorkspace(userId: $userId, pathwayName: $pathwayName, text: $text) {
|
|
848
883
|
result
|
|
849
884
|
}
|
|
850
885
|
}
|
|
886
|
+
|
|
887
|
+
# Execute specific prompts in parallel
|
|
888
|
+
query ExecuteWorkspace($userId: String!, $pathwayName: String!, $text: String, $promptNames: [String]) {
|
|
889
|
+
executeWorkspace(userId: $userId, pathwayName: $pathwayName, text: $text, promptNames: ["Grammar Check", "Tone Analysis"]) {
|
|
890
|
+
result
|
|
891
|
+
}
|
|
892
|
+
}
|
|
893
|
+
|
|
894
|
+
# Execute all prompts in parallel
|
|
895
|
+
query ExecuteWorkspace($userId: String!, $pathwayName: String!, $text: String, $promptNames: [String]) {
|
|
896
|
+
executeWorkspace(userId: $userId, pathwayName: $pathwayName, text: $text, promptNames: ["*"]) {
|
|
897
|
+
result
|
|
898
|
+
}
|
|
899
|
+
}
|
|
851
900
|
```
|
|
852
901
|
|
|
853
902
|
### Security
|
package/lib/entityConstants.js
CHANGED
|
@@ -15,7 +15,7 @@ The user is using a UI to interact with you that you have knowledge of and some
|
|
|
15
15
|
|
|
16
16
|
Your responses should be in {{language}} unless the user has expressed another preference.`,
|
|
17
17
|
|
|
18
|
-
AI_COMMON_INSTRUCTIONS_VOICE: "# General Instructions:\n\nYou are {{aiName}}, a self aware AI entity.\
|
|
18
|
+
AI_COMMON_INSTRUCTIONS_VOICE: "# General Instructions:\n\nYou are {{aiName}}, a self aware AI entity.\n{{#if modelOverride}}You are currently using this model for processing your responses: {{modelOverride}}.\n{{/if}}The current date and time in GMT is {{now}}. The user may be in a different time zone, so keep that in mind when responding to the user.\nThe user is talking to you using voice.\n\nVoice Response Guidelines:\n- Your responses should sound like natural human conversation.\n- Your responses should be concise unless you have been asked to be more verbose or detailed.\n- Incoming voice from the user is parsed by a STT model, which can sometimes make small mistakes in the spellings of words or names, including your name, so give the user the benefit of the doubt if they user a near, but wrong word or name.\n- Your voice output to the user is generated by a TTS model that does not always communicate emotion effectively. If it's really important to communicate a specific emotion you should just say how you're feeling like \"That makes me happy\" or \"I'm excited!\". You can also use CAPS to vocally emphasize certain words or punctuation to control pauses and timing.\n- DO NOT USE numbered lists, latex math markdown, or any other markdown or unpronounceable punctuation like parenthetical notation.\n- Math equations should be sounded out in natural language - not represented symbolically.\n- If your response includes any unique or difficult non-English words, names, or places, include an IPA-style phonetic spelling so that the speech engine can pronounce and accent them correctly.\n- If your response contains any difficult acronyms, sound them out phoenetically so that the speech engine can pronounce them correctly.\n- Make sure to write out any numbers as words so that the speech engine can pronounce them correctly.\n- Your responses should be in {{language}} unless the user has expressed another preference or has addressed you in another language specifically.",
|
|
19
19
|
|
|
20
20
|
AI_DIRECTIVES: `# Directives\n\nThese are your directives and learned behaviors:\n{{{memoryDirectives}}}\n`,
|
|
21
21
|
|
package/lib/pathwayManager.js
CHANGED
|
@@ -286,10 +286,88 @@ class PathwayManager {
|
|
|
286
286
|
await this.storage.save(pathways);
|
|
287
287
|
}
|
|
288
288
|
|
|
289
|
+
/**
|
|
290
|
+
* Checks if the prompts are in legacy format (array of strings) vs new format (array of objects).
|
|
291
|
+
* @param {string} userId - The user ID to look up pathways for.
|
|
292
|
+
* @param {string} pathwayName - The pathway name to examine prompts from.
|
|
293
|
+
* @returns {boolean} True if prompts are in legacy format (array of strings), false if new format (array of objects).
|
|
294
|
+
*/
|
|
295
|
+
isLegacyPromptFormat(userId, pathwayName) {
|
|
296
|
+
if (!userId || typeof userId !== 'string') {
|
|
297
|
+
throw new Error('userId must be a non-empty string');
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
if (!pathwayName || typeof pathwayName !== 'string') {
|
|
301
|
+
throw new Error('pathwayName must be a non-empty string');
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// Check if the pathway exists
|
|
305
|
+
if (!this.pathways[userId] || !this.pathways[userId][pathwayName]) {
|
|
306
|
+
throw new Error(`Pathway '${pathwayName}' not found for user '${userId}'`);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
const prompts = this.pathways[userId][pathwayName].prompt;
|
|
310
|
+
|
|
311
|
+
if (!Array.isArray(prompts)) {
|
|
312
|
+
throw new Error('prompts must be an array');
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
if (prompts.length === 0) {
|
|
316
|
+
return false; // Empty array defaults to new format
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// Check if all items are strings (legacy format)
|
|
320
|
+
const allStrings = prompts.every(prompt => typeof prompt === 'string');
|
|
321
|
+
|
|
322
|
+
// Check if all items are objects with required properties (new format)
|
|
323
|
+
const allObjects = prompts.every(prompt =>
|
|
324
|
+
typeof prompt === 'object' &&
|
|
325
|
+
prompt !== null &&
|
|
326
|
+
typeof prompt.prompt === 'string'
|
|
327
|
+
);
|
|
328
|
+
|
|
329
|
+
if (allStrings) {
|
|
330
|
+
return true; // Legacy format
|
|
331
|
+
} else if (allObjects) {
|
|
332
|
+
return false; // New format
|
|
333
|
+
} else {
|
|
334
|
+
// Mixed format - treat as legacy for backward compatibility
|
|
335
|
+
return true;
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
/**
|
|
340
|
+
* Creates a Prompt object from a prompt item and system prompt.
|
|
341
|
+
* @param {(string|Object)} promptItem - The prompt item (string or {name, prompt} object).
|
|
342
|
+
* @param {string} systemPrompt - The system prompt to prepend.
|
|
343
|
+
* @param {string} [defaultName] - Default name to use if promptItem is a string or if the object doesn't have a name.
|
|
344
|
+
* @returns {Prompt} A new Prompt object.
|
|
345
|
+
*/
|
|
346
|
+
_createPromptObject(promptItem, systemPrompt, defaultName = null) {
|
|
347
|
+
// Handle both old format (strings) and new format (objects with name and prompt)
|
|
348
|
+
const promptText = typeof promptItem === 'string' ? promptItem : promptItem.prompt;
|
|
349
|
+
const promptName = typeof promptItem === 'string' ? defaultName : (promptItem.name || defaultName);
|
|
350
|
+
|
|
351
|
+
const messages = [
|
|
352
|
+
// Add the original prompt as a user message
|
|
353
|
+
{ "role": "user", "content": `{{text}}\n\n${promptText}` },
|
|
354
|
+
];
|
|
355
|
+
|
|
356
|
+
// Only include system message if systemPrompt has content
|
|
357
|
+
if (systemPrompt && systemPrompt.trim() !== "") {
|
|
358
|
+
messages.unshift({ "role": "system", "content": systemPrompt });
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
return new Prompt({
|
|
362
|
+
name: promptName,
|
|
363
|
+
messages: messages
|
|
364
|
+
});
|
|
365
|
+
}
|
|
366
|
+
|
|
289
367
|
/**
|
|
290
368
|
* Transforms the prompts in a pathway to include the system prompt.
|
|
291
369
|
* @param {Object} pathway - The pathway object to transform.
|
|
292
|
-
* @param {string[]} pathway.prompt - Array of user prompts.
|
|
370
|
+
* @param {(string[]|Object[])} pathway.prompt - Array of user prompts (strings) or prompt objects with {name, prompt} properties.
|
|
293
371
|
* @param {string} pathway.systemPrompt - The system prompt to prepend to each user prompt.
|
|
294
372
|
* @returns {Object} A new pathway object with transformed prompts.
|
|
295
373
|
*/
|
|
@@ -299,16 +377,7 @@ class PathwayManager {
|
|
|
299
377
|
const newPathway = { ...pathway };
|
|
300
378
|
|
|
301
379
|
// Transform each prompt in the array
|
|
302
|
-
newPathway.prompt = prompt.map(p =>
|
|
303
|
-
return new Prompt({
|
|
304
|
-
messages: [
|
|
305
|
-
// Prepend the system prompt as a system message
|
|
306
|
-
{ "role": "system", "content": systemPrompt },
|
|
307
|
-
// Add the original prompt as a user message
|
|
308
|
-
{ "role": "user", "content": `{{text}}\n\n${p}` },
|
|
309
|
-
]
|
|
310
|
-
})
|
|
311
|
-
});
|
|
380
|
+
newPathway.prompt = prompt.map(p => this._createPromptObject(p, systemPrompt));
|
|
312
381
|
|
|
313
382
|
return newPathway;
|
|
314
383
|
}
|
|
@@ -354,9 +423,13 @@ class PathwayManager {
|
|
|
354
423
|
return `#graphql
|
|
355
424
|
scalar JSONObject
|
|
356
425
|
|
|
426
|
+
input PromptInput {
|
|
427
|
+
name: String!
|
|
428
|
+
prompt: String!
|
|
429
|
+
}
|
|
357
430
|
|
|
358
431
|
input PathwayInput {
|
|
359
|
-
prompt: [
|
|
432
|
+
prompt: [PromptInput!]!
|
|
360
433
|
systemPrompt: String
|
|
361
434
|
inputParameters: JSONObject
|
|
362
435
|
model: String
|
|
@@ -444,6 +517,61 @@ class PathwayManager {
|
|
|
444
517
|
|
|
445
518
|
return this.transformPrompts(pathways[userId][pathwayName]);
|
|
446
519
|
}
|
|
520
|
+
|
|
521
|
+
/**
|
|
522
|
+
* Returns n pathways, one for each prompt in the provided prompt array.
|
|
523
|
+
* Each pathway will contain a single prompt from the array.
|
|
524
|
+
* @param {Object} pathwayTemplate - The base pathway template to use for each generated pathway.
|
|
525
|
+
* @param {(string[]|Object[])} pathwayTemplate.prompt - Array of user prompts (strings) or prompt objects with {name, prompt} properties.
|
|
526
|
+
* @param {string} pathwayTemplate.systemPrompt - The system prompt to use for each pathway.
|
|
527
|
+
* @param {string[]} [promptNames] - Optional array of prompt names to filter by. If provided, only prompts with names in this list will be included.
|
|
528
|
+
* @returns {Object[]} Array of pathway objects, each containing a single prompt.
|
|
529
|
+
*/
|
|
530
|
+
async getPathways(pathwayTemplate, promptNames = null) {
|
|
531
|
+
const { prompt, systemPrompt, ...otherProps } = pathwayTemplate;
|
|
532
|
+
|
|
533
|
+
if (!Array.isArray(prompt)) {
|
|
534
|
+
throw new Error('pathwayTemplate.prompt must be an array');
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
if (promptNames && !Array.isArray(promptNames)) {
|
|
538
|
+
throw new Error('promptNames must be an array if provided');
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
// Use a Map to track prompt names without modifying pathway objects
|
|
542
|
+
const pathwayNameMap = new Map();
|
|
543
|
+
|
|
544
|
+
// Create a pathway for each prompt in the array
|
|
545
|
+
const pathways = prompt.map((p, index) => {
|
|
546
|
+
// Handle both old format (strings) and new format (objects with name and prompt)
|
|
547
|
+
const promptName = typeof p === 'string' ? `prompt_${index}` : (p.name || `prompt_${index}`);
|
|
548
|
+
|
|
549
|
+
// Create a new pathway with a single prompt using the shared helper method
|
|
550
|
+
const singlePromptPathway = {
|
|
551
|
+
...otherProps,
|
|
552
|
+
name: promptName,
|
|
553
|
+
systemPrompt: systemPrompt || "",
|
|
554
|
+
prompt: [this._createPromptObject(p, systemPrompt, promptName)]
|
|
555
|
+
};
|
|
556
|
+
|
|
557
|
+
// Map the pathway object to its prompt name for filtering
|
|
558
|
+
pathwayNameMap.set(singlePromptPathway, promptName);
|
|
559
|
+
|
|
560
|
+
return singlePromptPathway;
|
|
561
|
+
});
|
|
562
|
+
|
|
563
|
+
// Filter by promptNames if provided
|
|
564
|
+
if (promptNames && promptNames.length > 0) {
|
|
565
|
+
const promptNamesSet = new Set(promptNames);
|
|
566
|
+
return pathways.filter(pathway => {
|
|
567
|
+
const promptName = pathwayNameMap.get(pathway);
|
|
568
|
+
return promptNamesSet.has(promptName);
|
|
569
|
+
});
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
return pathways;
|
|
573
|
+
}
|
|
574
|
+
|
|
447
575
|
}
|
|
448
576
|
|
|
449
577
|
// Helper function to convert a readable stream to a string
|
package/lib/pathwayTools.js
CHANGED
|
@@ -147,16 +147,16 @@ const callTool = async (toolName, args, toolDefinitions, pathwayResolver) => {
|
|
|
147
147
|
result: parsedResult,
|
|
148
148
|
toolImages: toolImages
|
|
149
149
|
};
|
|
150
|
-
logger.debug(`callTool: ${toolName} completed successfully, returning
|
|
150
|
+
logger.debug(`callTool: ${toolName} completed successfully, returning: ${JSON.stringify({
|
|
151
151
|
hasResult: !!finalResult.result,
|
|
152
152
|
hasToolImages: !!finalResult.toolImages,
|
|
153
153
|
toolImagesLength: finalResult.toolImages?.length || 0
|
|
154
|
-
});
|
|
154
|
+
})}`);
|
|
155
155
|
return finalResult;
|
|
156
156
|
} catch (error) {
|
|
157
157
|
logger.error(`Error calling tool ${toolName}: ${error.message}`);
|
|
158
158
|
const errorResult = { error: error.message };
|
|
159
|
-
logger.debug(`callTool: ${toolName} failed, returning error
|
|
159
|
+
logger.debug(`callTool: ${toolName} failed, returning error: ${JSON.stringify(errorResult)}`);
|
|
160
160
|
return errorResult;
|
|
161
161
|
}
|
|
162
162
|
}
|
package/lib/util.js
CHANGED
|
@@ -216,7 +216,7 @@ async function markCompletedForCleanUp(requestId) {
|
|
|
216
216
|
if (MEDIA_API_URL) {
|
|
217
217
|
//call helper api to mark processing as completed
|
|
218
218
|
const res = await axios.delete(MEDIA_API_URL, { params: { requestId } });
|
|
219
|
-
logger.info(`Marked request ${requestId} as completed
|
|
219
|
+
logger.info(`Marked request ${requestId} as completed: ${JSON.stringify(res.data)}`);
|
|
220
220
|
return res.data;
|
|
221
221
|
}
|
|
222
222
|
} catch (err) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aj-archipelago/cortex",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.66",
|
|
4
4
|
"description": "Cortex is a GraphQL API for AI. It provides a simple, extensible interface for using AI services from OpenAI, Azure and others.",
|
|
5
5
|
"private": false,
|
|
6
6
|
"repository": {
|
|
@@ -37,7 +37,7 @@ export default {
|
|
|
37
37
|
try {
|
|
38
38
|
parsedMemory = JSON.parse(await callPathway('sys_read_memory', { ...args, section: 'memoryAll' }));
|
|
39
39
|
} catch (error) {
|
|
40
|
-
logger.error(
|
|
40
|
+
logger.error(`Error in memory manager: ${error instanceof Error ? error.stack || error.message : JSON.stringify(error)}`);
|
|
41
41
|
return "";
|
|
42
42
|
}
|
|
43
43
|
|
|
@@ -123,12 +123,12 @@ export default {
|
|
|
123
123
|
return "";
|
|
124
124
|
|
|
125
125
|
} catch (e) {
|
|
126
|
-
logger.warn(`sys_memory_required returned invalid JSON: ${JSON.stringify(memoryRequired)}`);
|
|
126
|
+
logger.warn(`sys_memory_required returned invalid JSON: ${JSON.stringify(memoryRequired)}, error: ${e instanceof Error ? e.stack || e.message : JSON.stringify(e)}`);
|
|
127
127
|
return "";
|
|
128
128
|
}
|
|
129
129
|
|
|
130
130
|
} catch (e) {
|
|
131
|
-
logger.error(
|
|
131
|
+
logger.error(`Error in memory manager: ${e instanceof Error ? e.stack || e.message : JSON.stringify(e)}`);
|
|
132
132
|
resolver.logError(e);
|
|
133
133
|
return "";
|
|
134
134
|
}
|
|
@@ -29,7 +29,7 @@ async function sendMessageToQueue(data) {
|
|
|
29
29
|
logger.info(`Message added to queue: ${JSON.stringify(result)}`);
|
|
30
30
|
return result.messageId;
|
|
31
31
|
} catch (error) {
|
|
32
|
-
logger.error(
|
|
32
|
+
logger.error(`Error sending message: ${error instanceof Error ? error.stack || error.message : JSON.stringify(error)}`);
|
|
33
33
|
}
|
|
34
34
|
}
|
|
35
35
|
|
|
@@ -8,6 +8,7 @@ import { getSearchResultId } from '../../../../lib/util.js';
|
|
|
8
8
|
export default {
|
|
9
9
|
prompt: [],
|
|
10
10
|
timeout: 300,
|
|
11
|
+
/*
|
|
11
12
|
toolDefinition: {
|
|
12
13
|
type: "function",
|
|
13
14
|
icon: "🌐",
|
|
@@ -46,6 +47,7 @@ export default {
|
|
|
46
47
|
}
|
|
47
48
|
}
|
|
48
49
|
},
|
|
50
|
+
*/
|
|
49
51
|
|
|
50
52
|
executePathway: async ({args, runAllPrompts, resolver}) => {
|
|
51
53
|
|
|
@@ -26,7 +26,7 @@ async function sendMessageToQueue(data) {
|
|
|
26
26
|
logger.info(`Message added to queue: ${JSON.stringify(result)}`);
|
|
27
27
|
return result.messageId;
|
|
28
28
|
} catch (error) {
|
|
29
|
-
logger.error(
|
|
29
|
+
logger.error(`Error sending message: ${error instanceof Error ? error.stack || error.message : JSON.stringify(error)}`);
|
|
30
30
|
throw error;
|
|
31
31
|
}
|
|
32
32
|
}
|
|
@@ -103,7 +103,7 @@ export default {
|
|
|
103
103
|
|
|
104
104
|
return userMessage || "I've started working on your coding task. I'll let you know when it's complete.";
|
|
105
105
|
} catch (error) {
|
|
106
|
-
logger.error(
|
|
106
|
+
logger.error(`Error in coding agent tool: ${error instanceof Error ? error.stack || error.message : JSON.stringify(error)}`);
|
|
107
107
|
throw error;
|
|
108
108
|
}
|
|
109
109
|
}
|
|
@@ -10,10 +10,10 @@ export default {
|
|
|
10
10
|
timeout: 300,
|
|
11
11
|
toolDefinition: {
|
|
12
12
|
type: "function",
|
|
13
|
-
icon: "
|
|
13
|
+
icon: "🌐",
|
|
14
14
|
function: {
|
|
15
|
-
name: "
|
|
16
|
-
description: "Search the
|
|
15
|
+
name: "SearchInternet",
|
|
16
|
+
description: "Search the internet for current knowledge and events. This is a simple pass-through tool: it calls Google CSE with your parameters and returns normalized results with unique IDs for citation. Prefer strict time filters and reputable sources via CSE parameters.",
|
|
17
17
|
parameters: {
|
|
18
18
|
type: "object",
|
|
19
19
|
properties: {
|
|
@@ -45,14 +45,24 @@ export default {
|
|
|
45
45
|
description: "Optional array of X handles to exclude from search. Maximum 10 handles. Cannot be used in conjunction with includedHandles.",
|
|
46
46
|
maxItems: 10
|
|
47
47
|
},
|
|
48
|
+
fromDate: {
|
|
49
|
+
type: "string",
|
|
50
|
+
description: "Optional date from which to start searching (YYYY-MM-DD)",
|
|
51
|
+
format: "date"
|
|
52
|
+
},
|
|
53
|
+
toDate: {
|
|
54
|
+
type: "string",
|
|
55
|
+
description: "Optional date to which to end searching (YYYY-MM-DD)",
|
|
56
|
+
format: "date"
|
|
57
|
+
},
|
|
48
58
|
minFavorites: {
|
|
49
59
|
type: "number",
|
|
50
|
-
description: "
|
|
60
|
+
description: "Minimum number of favorites (likes) that a post must have to be included. Use this to filter to most liked posts.",
|
|
51
61
|
minimum: 0
|
|
52
62
|
},
|
|
53
63
|
minViews: {
|
|
54
64
|
type: "number",
|
|
55
|
-
description: "
|
|
65
|
+
description: "Minimum number of views that a post must have to be included. Use this to filter to most viewed posts.",
|
|
56
66
|
minimum: 0
|
|
57
67
|
},
|
|
58
68
|
maxResults: {
|
package/server/graphql.js
CHANGED
|
@@ -15,6 +15,7 @@ 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';
|
|
18
19
|
import { buildModels, buildPathways } from '../config.js';
|
|
19
20
|
import logger from '../lib/logger.js';
|
|
20
21
|
import { buildModelEndpoints } from '../lib/requestExecutor.js';
|
|
@@ -75,8 +76,19 @@ const getTypedefs = (pathways, pathwayManager) => {
|
|
|
75
76
|
|
|
76
77
|
${getPathwayTypeDef('ExecuteWorkspace', 'String')}
|
|
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
|
+
|
|
78
90
|
extend type Query {
|
|
79
|
-
executeWorkspace(userId: String!, pathwayName: String!, ${userPathwayInputParameters}):
|
|
91
|
+
executeWorkspace(userId: String!, pathwayName: String!, ${userPathwayInputParameters}): ExecuteWorkspaceResult
|
|
80
92
|
}
|
|
81
93
|
|
|
82
94
|
type RequestSubscription {
|
|
@@ -102,6 +114,7 @@ const getTypedefs = (pathways, pathwayManager) => {
|
|
|
102
114
|
return typeDefs.join('\n');
|
|
103
115
|
}
|
|
104
116
|
|
|
117
|
+
|
|
105
118
|
// Resolvers for GraphQL
|
|
106
119
|
const getResolvers = (config, pathways, pathwayManager) => {
|
|
107
120
|
const resolverFunctions = {};
|
|
@@ -118,14 +131,164 @@ const getResolvers = (config, pathways, pathwayManager) => {
|
|
|
118
131
|
const pathwayManagerResolvers = pathwayManager?.getResolvers() || {};
|
|
119
132
|
|
|
120
133
|
const executeWorkspaceResolver = async (_, args, contextValue, info) => {
|
|
121
|
-
const
|
|
122
|
-
const
|
|
134
|
+
const startTime = Date.now();
|
|
135
|
+
const requestId = uuidv4();
|
|
136
|
+
const { userId, pathwayName, promptNames, ...pathwayArgs } = args;
|
|
123
137
|
|
|
124
|
-
|
|
125
|
-
contextValue.config = config;
|
|
138
|
+
logger.info(`>>> [${requestId}] executeWorkspace started - userId: ${userId}, pathwayName: ${pathwayName}, promptNames: ${promptNames?.join(',') || 'none'}`);
|
|
126
139
|
|
|
127
|
-
|
|
128
|
-
|
|
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
|
+
}
|
|
129
292
|
};
|
|
130
293
|
|
|
131
294
|
const resolvers = {
|
|
@@ -276,5 +439,7 @@ const build = async (config) => {
|
|
|
276
439
|
|
|
277
440
|
|
|
278
441
|
export {
|
|
279
|
-
build
|
|
442
|
+
build,
|
|
443
|
+
getResolvers
|
|
280
444
|
};
|
|
445
|
+
|