@minded-ai/mindedjs 1.0.120 → 1.0.122-beta-1

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.
Files changed (130) hide show
  1. package/dist/agent.d.ts +4 -1
  2. package/dist/agent.d.ts.map +1 -1
  3. package/dist/agent.js +70 -10
  4. package/dist/agent.js.map +1 -1
  5. package/dist/browserTask/README.md +419 -0
  6. package/dist/browserTask/browserAgent.py +632 -0
  7. package/dist/browserTask/captcha_isolated.png +0 -0
  8. package/dist/browserTask/executeBrowserTask.ts +79 -0
  9. package/dist/browserTask/requirements.txt +8 -0
  10. package/dist/browserTask/setup.sh +144 -0
  11. package/dist/cli/index.js +14 -14
  12. package/dist/cli/index.js.map +1 -1
  13. package/dist/edges/createLogicalRouter.js +1 -1
  14. package/dist/edges/createLogicalRouter.js.map +1 -1
  15. package/dist/edges/createPromptRouter.d.ts.map +1 -1
  16. package/dist/edges/createPromptRouter.js +0 -7
  17. package/dist/edges/createPromptRouter.js.map +1 -1
  18. package/dist/edges/edgeFactory.js +2 -2
  19. package/dist/edges/edgeFactory.js.map +1 -1
  20. package/dist/index.d.ts +4 -3
  21. package/dist/index.d.ts.map +1 -1
  22. package/dist/index.js +5 -3
  23. package/dist/index.js.map +1 -1
  24. package/dist/internalTools/retell.js +17 -7
  25. package/dist/internalTools/retell.js.map +1 -1
  26. package/dist/internalTools/sendPlaceholderMessage.js +17 -7
  27. package/dist/internalTools/sendPlaceholderMessage.js.map +1 -1
  28. package/dist/internalTools/voice/escalateVoiceCall.d.ts +2 -0
  29. package/dist/internalTools/voice/escalateVoiceCall.d.ts.map +1 -0
  30. package/dist/internalTools/voice/escalateVoiceCall.js +42 -0
  31. package/dist/internalTools/voice/escalateVoiceCall.js.map +1 -0
  32. package/dist/internalTools/voice/retell.d.ts +12 -0
  33. package/dist/internalTools/voice/retell.d.ts.map +1 -0
  34. package/dist/internalTools/voice/retell.js +44 -0
  35. package/dist/internalTools/voice/retell.js.map +1 -0
  36. package/dist/internalTools/voice/sendPlaceholderMessage.d.ts +14 -0
  37. package/dist/internalTools/voice/sendPlaceholderMessage.d.ts.map +1 -0
  38. package/dist/internalTools/voice/sendPlaceholderMessage.js +51 -0
  39. package/dist/internalTools/voice/sendPlaceholderMessage.js.map +1 -0
  40. package/dist/interrupts/BaseInterruptSessionManager.d.ts +52 -0
  41. package/dist/interrupts/BaseInterruptSessionManager.d.ts.map +1 -0
  42. package/dist/interrupts/BaseInterruptSessionManager.js +40 -0
  43. package/dist/interrupts/BaseInterruptSessionManager.js.map +1 -0
  44. package/dist/interrupts/MemoryInterruptSessionManager.d.ts +14 -0
  45. package/dist/interrupts/MemoryInterruptSessionManager.d.ts.map +1 -0
  46. package/dist/interrupts/MemoryInterruptSessionManager.js +60 -0
  47. package/dist/interrupts/MemoryInterruptSessionManager.js.map +1 -0
  48. package/dist/interrupts/MindedInterruptSessionManager.d.ts +13 -0
  49. package/dist/interrupts/MindedInterruptSessionManager.d.ts.map +1 -0
  50. package/dist/interrupts/MindedInterruptSessionManager.js +151 -0
  51. package/dist/interrupts/MindedInterruptSessionManager.js.map +1 -0
  52. package/dist/interrupts/interruptSessionManagerFactory.d.ts +3 -0
  53. package/dist/interrupts/interruptSessionManagerFactory.d.ts.map +1 -0
  54. package/dist/interrupts/interruptSessionManagerFactory.js +46 -0
  55. package/dist/interrupts/interruptSessionManagerFactory.js.map +1 -0
  56. package/dist/nodes/addAppToolNode.d.ts.map +1 -1
  57. package/dist/nodes/addAppToolNode.js +7 -13
  58. package/dist/nodes/addAppToolNode.js.map +1 -1
  59. package/dist/nodes/addBrowserTaskNode.d.ts.map +1 -1
  60. package/dist/nodes/addBrowserTaskNode.js +7 -74
  61. package/dist/nodes/addBrowserTaskNode.js.map +1 -1
  62. package/dist/nodes/addHumanInTheLoopNode.d.ts.map +1 -1
  63. package/dist/nodes/addHumanInTheLoopNode.js +2 -1
  64. package/dist/nodes/addHumanInTheLoopNode.js.map +1 -1
  65. package/dist/nodes/addJumpToNode.js +2 -2
  66. package/dist/nodes/addJumpToNode.js.map +1 -1
  67. package/dist/nodes/addPromptNode.d.ts.map +1 -1
  68. package/dist/nodes/addPromptNode.js +96 -13
  69. package/dist/nodes/addPromptNode.js.map +1 -1
  70. package/dist/nodes/addToolNode.d.ts.map +1 -1
  71. package/dist/nodes/addToolNode.js +12 -13
  72. package/dist/nodes/addToolNode.js.map +1 -1
  73. package/dist/nodes/addToolRunNode.d.ts.map +1 -1
  74. package/dist/nodes/addToolRunNode.js +4 -0
  75. package/dist/nodes/addToolRunNode.js.map +1 -1
  76. package/dist/nodes/compilePrompt.d.ts +5 -0
  77. package/dist/nodes/compilePrompt.d.ts.map +1 -0
  78. package/dist/nodes/compilePrompt.js +64 -0
  79. package/dist/nodes/compilePrompt.js.map +1 -0
  80. package/dist/platform/mindedConnection.js +12 -12
  81. package/dist/platform/mindedConnection.js.map +1 -1
  82. package/dist/platform/mindedConnectionTypes.d.ts +151 -1
  83. package/dist/platform/mindedConnectionTypes.d.ts.map +1 -1
  84. package/dist/platform/mindedConnectionTypes.js +9 -0
  85. package/dist/platform/mindedConnectionTypes.js.map +1 -1
  86. package/dist/playbooks/playbooks.d.ts +2 -2
  87. package/dist/playbooks/playbooks.d.ts.map +1 -1
  88. package/dist/playbooks/playbooks.js +37 -45
  89. package/dist/playbooks/playbooks.js.map +1 -1
  90. package/dist/types/Agent.types.d.ts +2 -0
  91. package/dist/types/Agent.types.d.ts.map +1 -1
  92. package/dist/types/Agent.types.js.map +1 -1
  93. package/dist/types/LangGraph.types.d.ts +2 -2
  94. package/dist/types/LangGraph.types.d.ts.map +1 -1
  95. package/dist/types/LangGraph.types.js +3 -1
  96. package/dist/types/LangGraph.types.js.map +1 -1
  97. package/dist/voice/voiceSession.d.ts +0 -1
  98. package/dist/voice/voiceSession.d.ts.map +1 -1
  99. package/dist/voice/voiceSession.js +9 -13
  100. package/dist/voice/voiceSession.js.map +1 -1
  101. package/docs/low-code-editor/nodes.md +21 -12
  102. package/docs/low-code-editor/playbooks.md +50 -32
  103. package/package.json +2 -2
  104. package/src/agent.ts +87 -13
  105. package/src/cli/index.ts +14 -14
  106. package/src/edges/createLogicalRouter.ts +1 -1
  107. package/src/edges/createPromptRouter.ts +5 -12
  108. package/src/edges/edgeFactory.ts +2 -2
  109. package/src/index.ts +4 -3
  110. package/src/internalTools/voice/escalateVoiceCall.ts +15 -0
  111. package/src/internalTools/{retell.ts → voice/retell.ts} +2 -2
  112. package/src/internalTools/{sendPlaceholderMessage.ts → voice/sendPlaceholderMessage.ts} +2 -2
  113. package/src/interrupts/BaseInterruptSessionManager.ts +96 -0
  114. package/src/interrupts/MemoryInterruptSessionManager.ts +63 -0
  115. package/src/interrupts/MindedInterruptSessionManager.ts +162 -0
  116. package/src/interrupts/interruptSessionManagerFactory.ts +20 -0
  117. package/src/nodes/addAppToolNode.ts +10 -13
  118. package/src/nodes/addBrowserTaskNode.ts +7 -56
  119. package/src/nodes/addHumanInTheLoopNode.ts +2 -1
  120. package/src/nodes/addJumpToNode.ts +2 -2
  121. package/src/nodes/addPromptNode.ts +104 -18
  122. package/src/nodes/addToolNode.ts +12 -14
  123. package/src/nodes/addToolRunNode.ts +4 -1
  124. package/src/nodes/compilePrompt.ts +41 -0
  125. package/src/platform/mindedConnection.ts +12 -12
  126. package/src/platform/mindedConnectionTypes.ts +187 -0
  127. package/src/playbooks/playbooks.ts +38 -48
  128. package/src/types/Agent.types.ts +2 -0
  129. package/src/types/LangGraph.types.ts +3 -1
  130. package/src/voice/voiceSession.ts +9 -14
@@ -0,0 +1,162 @@
1
+ import { BaseInterruptSessionManager, QueuedMessage, QUEUE_INTERRUPT_DETECTED_BEFORE_SPEECH } from './BaseInterruptSessionManager';
2
+ import * as mindedConnection from '../platform/mindedConnection';
3
+ import {
4
+ mindedConnectionSocketMessageType,
5
+ InterruptSessionIsProcessedRequest,
6
+ InterruptSessionIsProcessedResponse,
7
+ InterruptSessionLockRequest,
8
+ InterruptSessionLockResponse,
9
+ InterruptSessionReleaseRequest,
10
+ InterruptSessionReleaseResponse,
11
+ InterruptSessionEnqueueRequest,
12
+ InterruptSessionEnqueueResponse,
13
+ InterruptSessionDequeueAllRequest,
14
+ InterruptSessionDequeueAllResponse,
15
+ InterruptSessionDequeueRequest,
16
+ InterruptSessionDequeueResponse,
17
+ InterruptSessionHasMessagesRequest,
18
+ InterruptSessionHasMessagesResponse,
19
+ InterruptSessionGetMessagesRequest,
20
+ InterruptSessionGetMessagesResponse,
21
+ } from '../platform/mindedConnectionTypes';
22
+
23
+ export class MindedInterruptSessionManager extends BaseInterruptSessionManager {
24
+ constructor() {
25
+ super();
26
+ }
27
+
28
+ async isProcessed(sessionId: string): Promise<boolean> {
29
+ try {
30
+ const response = await mindedConnection.awaitEmit<InterruptSessionIsProcessedRequest, InterruptSessionIsProcessedResponse>(
31
+ mindedConnectionSocketMessageType.INTERRUPT_SESSION_IS_PROCESSED,
32
+ {
33
+ type: mindedConnectionSocketMessageType.INTERRUPT_SESSION_IS_PROCESSED,
34
+ sessionId,
35
+ },
36
+ );
37
+ return response.isProcessed ?? false;
38
+ } catch (error) {
39
+ console.error('Error checking if session is processed:', error);
40
+ return false;
41
+ }
42
+ }
43
+
44
+ async lock(sessionId: string): Promise<boolean> {
45
+ try {
46
+ const response = await mindedConnection.awaitEmit<InterruptSessionLockRequest, InterruptSessionLockResponse>(
47
+ mindedConnectionSocketMessageType.INTERRUPT_SESSION_LOCK,
48
+ {
49
+ type: mindedConnectionSocketMessageType.INTERRUPT_SESSION_LOCK,
50
+ sessionId,
51
+ },
52
+ );
53
+ return response.lockAcquired ?? false;
54
+ } catch (error) {
55
+ console.error('Error locking session:', error);
56
+ return false;
57
+ }
58
+ }
59
+
60
+ async release(sessionId: string): Promise<void> {
61
+ try {
62
+ await mindedConnection.awaitEmit<InterruptSessionReleaseRequest, InterruptSessionReleaseResponse>(
63
+ mindedConnectionSocketMessageType.INTERRUPT_SESSION_RELEASE,
64
+ {
65
+ type: mindedConnectionSocketMessageType.INTERRUPT_SESSION_RELEASE,
66
+ sessionId,
67
+ },
68
+ );
69
+ } catch (error) {
70
+ console.error('Error releasing session:', error);
71
+ throw error;
72
+ }
73
+ }
74
+
75
+ async enqueueMessage(sessionId: string, message: QueuedMessage): Promise<void> {
76
+ try {
77
+ await mindedConnection.awaitEmit<InterruptSessionEnqueueRequest, InterruptSessionEnqueueResponse>(
78
+ mindedConnectionSocketMessageType.INTERRUPT_SESSION_ENQUEUE,
79
+ {
80
+ type: mindedConnectionSocketMessageType.INTERRUPT_SESSION_ENQUEUE,
81
+ sessionId,
82
+ message,
83
+ },
84
+ );
85
+ } catch (error) {
86
+ console.error('Error enqueuing message:', error);
87
+ throw error;
88
+ }
89
+ }
90
+
91
+ async dequeueAll(sessionId: string): Promise<QueuedMessage[]> {
92
+ try {
93
+ const response = await mindedConnection.awaitEmit<InterruptSessionDequeueAllRequest, InterruptSessionDequeueAllResponse>(
94
+ mindedConnectionSocketMessageType.INTERRUPT_SESSION_DEQUEUE_ALL,
95
+ {
96
+ type: mindedConnectionSocketMessageType.INTERRUPT_SESSION_DEQUEUE_ALL,
97
+ sessionId,
98
+ },
99
+ );
100
+ return response.messages ?? [];
101
+ } catch (error) {
102
+ console.error('Error dequeuing all messages:', error);
103
+ return [];
104
+ }
105
+ }
106
+
107
+ async dequeue(sessionId: string): Promise<QueuedMessage | null> {
108
+ try {
109
+ const response = await mindedConnection.awaitEmit<InterruptSessionDequeueRequest, InterruptSessionDequeueResponse>(
110
+ mindedConnectionSocketMessageType.INTERRUPT_SESSION_DEQUEUE,
111
+ {
112
+ type: mindedConnectionSocketMessageType.INTERRUPT_SESSION_DEQUEUE,
113
+ sessionId,
114
+ },
115
+ );
116
+ const message = response.message ?? null;
117
+
118
+ // If the dequeued message has the special trigger name, recursively dequeue the next one
119
+ if (message && message.triggerName === QUEUE_INTERRUPT_DETECTED_BEFORE_SPEECH) {
120
+ return this.dequeue(sessionId);
121
+ }
122
+
123
+ return message;
124
+ } catch (error) {
125
+ console.error('Error dequeuing message:', error);
126
+ return null;
127
+ }
128
+ }
129
+
130
+ // Implementation of abstract methods from BaseInterruptSessionManager
131
+ protected async hasQueuedMessages(sessionId: string): Promise<boolean> {
132
+ try {
133
+ const response = await mindedConnection.awaitEmit<InterruptSessionHasMessagesRequest, InterruptSessionHasMessagesResponse>(
134
+ mindedConnectionSocketMessageType.INTERRUPT_SESSION_HAS_MESSAGES,
135
+ {
136
+ type: mindedConnectionSocketMessageType.INTERRUPT_SESSION_HAS_MESSAGES,
137
+ sessionId,
138
+ },
139
+ );
140
+ return response.hasMessages ?? false;
141
+ } catch (error) {
142
+ console.error('Error checking if session has messages:', error);
143
+ return false;
144
+ }
145
+ }
146
+
147
+ protected async getQueuedMessages(sessionId: string): Promise<QueuedMessage[]> {
148
+ try {
149
+ const response = await mindedConnection.awaitEmit<InterruptSessionGetMessagesRequest, InterruptSessionGetMessagesResponse>(
150
+ mindedConnectionSocketMessageType.INTERRUPT_SESSION_GET_MESSAGES,
151
+ {
152
+ type: mindedConnectionSocketMessageType.INTERRUPT_SESSION_GET_MESSAGES,
153
+ sessionId,
154
+ },
155
+ );
156
+ return response.messages ?? [];
157
+ } catch (error) {
158
+ console.error('Error getting queued messages:', error);
159
+ return [];
160
+ }
161
+ }
162
+ }
@@ -0,0 +1,20 @@
1
+ import { InterruptSessionManager } from './BaseInterruptSessionManager';
2
+ import { MemoryInterruptSessionManager } from './MemoryInterruptSessionManager';
3
+ import { MindedInterruptSessionManager } from './MindedInterruptSessionManager';
4
+ import * as mindedConnection from '../platform/mindedConnection';
5
+ import { getConfig } from '../platform/config';
6
+ import { logger } from '../utils/logger';
7
+
8
+ export function createInterruptSessionManager(): InterruptSessionManager {
9
+ const { runLocally } = getConfig();
10
+ if (runLocally) {
11
+ logger.info({ msg: 'Using memory interrupt session manager' });
12
+ return new MemoryInterruptSessionManager();
13
+ } else {
14
+ if (!mindedConnection.isConnected()) {
15
+ throw new Error('MindedConnection is required for platform interrupt session manager');
16
+ }
17
+ logger.info({ msg: 'Using Minded interrupt session manager' });
18
+ return new MindedInterruptSessionManager();
19
+ }
20
+ }
@@ -8,9 +8,10 @@ import { LLMProviders } from '../types/LLM.types';
8
8
  import { AppActionInvocationHistoryStep } from '../types/Agent.types';
9
9
  import { Agent } from '../agent';
10
10
  import { logger } from '../utils/logger';
11
- import { compilePlaybooks } from '../playbooks/playbooks';
12
11
  import { createHistoryStep } from '../utils/history';
13
12
  import { Tool } from '../types/Tools.types';
13
+ import { combinePlaybooks } from '../playbooks/playbooks';
14
+ import { compilePrompt } from './compilePrompt';
14
15
 
15
16
  export const addAppToolNode = async ({
16
17
  graph,
@@ -51,14 +52,10 @@ export const addAppToolNode = async ({
51
52
  description: appRunnerTool.description,
52
53
  schema: appRunnerTool.input,
53
54
  });
54
- // Get compiled playbooks with proper parameters
55
- const playbookParams = {
56
- ...state.memory, // Spread memory fields at the top level
57
- currentTime: new Date().toISOString(),
58
- };
59
- const compiledPlaybooks = compilePlaybooks(agent.playbooks, playbookParams) || '';
60
55
 
61
- const message = `${compiledPlaybooks ? compiledPlaybooks + '\n\n' : ''}
56
+ const combinedPlaybooks = combinePlaybooks(agent.playbooks) || '';
57
+
58
+ const message = `${combinedPlaybooks ? combinedPlaybooks + '\n\n' : ''}
62
59
  Additional context:
63
60
  previous messages are available for context.
64
61
  Your goal is execute the tool with the correct parameters, some of them already chosen by the user and the rest should be generated.
@@ -70,15 +67,15 @@ export const addAppToolNode = async ({
70
67
  Parameters manually configured by the user are:
71
68
  ${JSON.stringify(cleanedParameters)}
72
69
  User instructions for choosing tool parameters are:
73
- ${node.prompt ? `${node.prompt}` : 'no instructions set by the user'}
74
- Workflow memory is:
75
- ${JSON.stringify(state.memory)}
76
- `;
70
+ ${node.prompt ? `${node.prompt}` : 'no instructions set by the user'}`;
71
+
72
+ const compiledPrompt = compilePrompt(message, { memory: state.memory, system: { currentTime: new Date().toISOString() } });
73
+
77
74
  const AIToolCallMessage = await llm
78
75
  .bindTools([tool], {
79
76
  tool_choice: tool.name,
80
77
  })
81
- .invoke([...state.messages, new SystemMessage(message)]);
78
+ .invoke([...state.messages, new SystemMessage(compiledPrompt)]);
82
79
  AIToolCallMessage.tool_calls[0].args = {
83
80
  ...AIToolCallMessage.tool_calls[0].args,
84
81
  ...cleanedParameters, //user set parameters have priority over ai generated parameters
@@ -11,46 +11,8 @@ import { createBrowserSession } from '../browserTask/executeBrowserTask';
11
11
  import { tool as langchainTool } from '@langchain/core/tools';
12
12
  import { z } from 'zod';
13
13
  import { LLMProviders } from '../types/LLM.types';
14
- import { compilePlaybooks } from '../playbooks/playbooks';
15
- import * as ejs from 'ejs';
16
-
17
- /**
18
- * Compile prompt with parameters using EJS and placeholder replacement
19
- */
20
- function compilePrompt(prompt: string, params: Record<string, any> = {}): string {
21
- try {
22
- // First, render with EJS
23
- let compiledPrompt = ejs.render(prompt, params);
24
-
25
- // Then, replace placeholders in {} format
26
- compiledPrompt = replacePlaceholders(compiledPrompt, params);
27
-
28
- return compiledPrompt;
29
- } catch (error) {
30
- logger.error({ message: 'Error compiling prompt', error });
31
- return prompt; // Return uncompiled if there's an error
32
- }
33
- }
34
-
35
- /**
36
- * Replace placeholders in {key} format
37
- */
38
- function replacePlaceholders(text: string, params: Record<string, any>): string {
39
- return text.replace(/\{([^}]+)\}/g, (match, key) => {
40
- const keys = key.split('.');
41
- let value: any = params;
42
-
43
- for (const k of keys) {
44
- if (value && typeof value === 'object' && k in value) {
45
- value = value[k];
46
- } else {
47
- return match; // Return original if key not found
48
- }
49
- }
50
-
51
- return String(value);
52
- });
53
- }
14
+ import { compilePrompt } from './compilePrompt';
15
+ import { combinePlaybooks } from '../playbooks/playbooks';
54
16
 
55
17
  type AddBrowserTaskNodeParams = {
56
18
  graph: PreCompiledGraph;
@@ -98,17 +60,8 @@ export const addBrowserTaskNode = async ({ graph, node, agent, llm }: AddBrowser
98
60
  schema: zodSchema,
99
61
  });
100
62
 
101
- // Get compiled playbooks
102
- const playbookParams = {
103
- ...state.memory,
104
- state,
105
- currentTime: new Date().toISOString(),
106
- };
107
- const compiledPlaybooks = compilePlaybooks(agent.playbooks, playbookParams) || '';
108
-
109
- const systemPrompt = `${compiledPlaybooks ? compiledPlaybooks + '\n\n' : ''}
110
- Additional context:
111
- - workflow memory: ${JSON.stringify(state.memory)}`;
63
+ const combinedPlaybooks = combinePlaybooks(agent.playbooks) || '';
64
+ const systemPrompt = combinedPlaybooks ? compilePrompt(combinedPlaybooks, { state, currentTime: new Date().toISOString() }) : '';
112
65
 
113
66
  try {
114
67
  // Use LLM to generate tool call
@@ -128,8 +81,8 @@ Additional context:
128
81
  // Prepare parameters for prompt compilation
129
82
  const promptParams = {
130
83
  input: inputParams,
131
- state,
132
- currentTime: new Date().toISOString(),
84
+ memory: state.memory,
85
+ system: { currentTime: new Date().toISOString() },
133
86
  };
134
87
 
135
88
  // Compile the prompt with parameters
@@ -142,9 +95,7 @@ Follow the instructions. Any retrieved data should be printed as string and not
142
95
  # Task instructions:
143
96
  ${compiledPrompt}
144
97
 
145
- ${Object.keys(inputParams).length > 0 ? `# Input parameters:\n${JSON.stringify(inputParams, null, 2)}\n\n` : ''}
146
- ${state.memory ? `# Task context:\n${JSON.stringify(state.memory)}\n\n` : ''}
147
- `;
98
+ ${Object.keys(inputParams).length > 0 ? `# Input parameters:\n${JSON.stringify(inputParams, null, 2)}\n\n` : ''}`;
148
99
 
149
100
  // Create browser session using socket
150
101
  const session = await createBrowserSession(node.proxy);
@@ -3,6 +3,7 @@ import { PreCompiledGraph, stateAnnotation } from '../types/LangGraph.types';
3
3
  import { RunnableLike } from '@langchain/core/runnables';
4
4
  import { logger } from '../utils/logger';
5
5
  import { internalNodesSuffix } from '../types/Flows.types';
6
+ import { InterruptType } from '../interrupts/BaseInterruptSessionManager';
6
7
 
7
8
  type AddHumanInTheLoopNodeParams = {
8
9
  graph: PreCompiledGraph;
@@ -16,7 +17,7 @@ export const addHumanInTheLoopNode = async ({ graph, attachedToNodeName }: AddHu
16
17
  logger.debug({ msg: `[Node] Waiting for human input`, node: attachedToNodeName });
17
18
 
18
19
  if (state.messages[state.messages.length - 1].getType() === 'ai') {
19
- const value = interrupt('input from human in the loop');
20
+ const value = interrupt({ type: InterruptType.HUMAN_IN_THE_LOOP });
20
21
  return value;
21
22
  }
22
23
  };
@@ -7,10 +7,10 @@ import { createHistoryStep } from '../utils/history';
7
7
 
8
8
  export const addJumpToNode = async ({ graph, node }: { graph: PreCompiledGraph; node: JumpToNode }) => {
9
9
  const callback: RunnableLike = async (state: typeof stateAnnotation.State) => {
10
- logger.info(`Executing jump node ${node.displayName} – jumping to ${node.targetNodeId}`);
10
+ logger.info({ msg: `Executing jump node ${node.displayName} – jumping to ${node.targetNodeId}` });
11
11
  // No state modifications are necessary; control flow is handled via edges.
12
12
  return {
13
- goto: null,
13
+ goto: node.targetNodeId,
14
14
  history: createHistoryStep<HistoryStep>(state.history, {
15
15
  type: NodeType.JUMP_TO_NODE,
16
16
  nodeId: node.name,
@@ -12,9 +12,10 @@ import { createLlmInstance } from '../llm/createLlmInstance';
12
12
  import extractToolStateResponse from '../utils/extractStateMemoryResponse';
13
13
  import { Agent } from '../agent';
14
14
  import { logger } from '../utils/logger';
15
- import { compilePlaybooks } from '../playbooks/playbooks';
15
+ import { combinePlaybooks } from '../playbooks/playbooks';
16
16
  import { createHistoryStep } from '../utils/history';
17
-
17
+ import { compilePrompt } from './compilePrompt';
18
+ import { v4 as uuidv4 } from 'uuid';
18
19
  type AddPromptNodeParams = {
19
20
  graph: PreCompiledGraph;
20
21
  node: PromptNode;
@@ -26,7 +27,8 @@ type AddPromptNodeParams = {
26
27
 
27
28
  export const addPromptNode = async ({ graph, node, llm, tools, emit, agent }: AddPromptNodeParams) => {
28
29
  const callback: RunnableLike = async (state: typeof stateAnnotation.State) => {
29
- logger.debug({ msg: `[Node] Executing prompt node`, node: node.displayName });
30
+ await agent.interruptSessionManager.checkQueueAndInterrupt(state.sessionId);
31
+ logger.info({ msg: `[Node] Executing prompt node`, node: node.displayName });
30
32
  const llmToUse = node.llmConfig ? createLlmInstance(node.llmConfig) : llm;
31
33
 
32
34
  const globalTools = tools
@@ -39,20 +41,19 @@ export const addPromptNode = async ({ graph, node, llm, tools, emit, agent }: Ad
39
41
  }),
40
42
  );
41
43
 
42
- // Get compiled playbooks with proper parameters
43
- const playbookParams = {
44
- ...state.memory, // Spread memory fields at the top level
45
- };
46
- const compiledPlaybooks = compilePlaybooks(agent.playbooks, playbookParams) || '';
47
-
48
- const message = `
49
- ${node.prompt ? `# Task instructions:\n${node.prompt}\n\n` : ''}
50
- ${state.memory ? `# Task context:\n${JSON.stringify(state.memory)}\n\n` : ''}
51
- ${compiledPlaybooks ? `# General guidelines:\n${compiledPlaybooks}\n\n` : ''}
52
- `;
44
+ const combinedPlaybooks = combinePlaybooks(agent.playbooks) || '';
53
45
 
54
- const result: AIMessage = await llmToUse.bindTools(globalTools).invoke([...state.messages, new SystemMessage(message)]);
46
+ // Get edges for the current node and format them
47
+ const edges = agent.flows?.flatMap((flow: any) => flow.edges) || [];
48
+ const nodeEdges = edges.filter((edge: any) => edge.source === node.name);
49
+ const currentPromptNode = getCurrentPromptNode(node, nodeEdges);
50
+ const systemMessage = combinedPlaybooks + '\n\n' + currentPromptNode;
51
+ const compiledPrompt = compilePrompt(systemMessage, { memory: state.memory, system: { currentTime: new Date().toISOString() } });
55
52
 
53
+ const startTime = Date.now();
54
+ const result: AIMessage = await llmToUse.bindTools(globalTools).invoke([new SystemMessage(compiledPrompt), ...state.messages]);
55
+ const endTime = Date.now();
56
+ logger.debug({ msg: '[Model] Model execution time', executionTimeMs: endTime - startTime });
56
57
  // Check if the result contains tool calls
57
58
  if (result.tool_calls && result.tool_calls.length > 0) {
58
59
  // Execute the tools
@@ -65,8 +66,33 @@ export const addPromptNode = async ({ graph, node, llm, tools, emit, agent }: Ad
65
66
  if (matchedTool) {
66
67
  try {
67
68
  // Invoke the LangChain tool directly
69
+ const startTime = Date.now();
68
70
  const toolResult = await matchedTool.invoke(toolCall);
69
- logger.debug({ msg: `[Tool] Tool result`, tool: matchedTool?.name, result: toolResult });
71
+ const endTime = Date.now();
72
+ logger.debug({ msg: `[Tool] Tool result`, tool: matchedTool?.name, result: toolResult, executionTimeMs: endTime - startTime });
73
+ //check for queue after tool call
74
+ const systemMessageId = uuidv4();
75
+
76
+ await agent.interruptSessionManager.checkQueueAndInterrupt(state.sessionId, {
77
+ messages: [
78
+ result,
79
+ toolResult,
80
+ new SystemMessage({
81
+ id: systemMessageId,
82
+ content:
83
+ 'you called tool when the user send a new message, Consider calling the function again after user message is processed',
84
+ }),
85
+ ],
86
+ history: [
87
+ createHistoryStep<HistoryStep>(state.history, {
88
+ type: NodeType.TOOL,
89
+ nodeId: node.name,
90
+ nodeDisplayName: node.displayName,
91
+ raw: toolResult,
92
+ messageIds: [toolResult.id!, systemMessageId],
93
+ }),
94
+ ],
95
+ });
70
96
  const toolStateUpdate = extractToolStateResponse(toolResult);
71
97
  // Properly merge memory and other state updates
72
98
  stateUpdates = {
@@ -75,7 +101,8 @@ export const addPromptNode = async ({ graph, node, llm, tools, emit, agent }: Ad
75
101
  memory: { ...(stateUpdates as any).memory, ...(toolStateUpdate as any).memory },
76
102
  };
77
103
  toolResults.push(toolResult);
78
- } catch (error) {
104
+ } catch (error: any) {
105
+ if (error?.name === 'GraphInterrupt') throw error;
79
106
  logger.error({ msg: `[Tool] Error executing tool`, tool: toolCall.name, error });
80
107
  const errorMessage = new ToolMessage({
81
108
  content: JSON.stringify({ error: error instanceof Error ? error.message : String(error) }),
@@ -88,10 +115,30 @@ export const addPromptNode = async ({ graph, node, llm, tools, emit, agent }: Ad
88
115
  }
89
116
  }
90
117
 
118
+ // await agent.interruptSessionManager.checkQueueAndInterrupt(state.sessionId);
119
+
91
120
  // Return the tool call message and tool results with state updates spread at top level
92
121
  return {
93
122
  ...stateUpdates,
94
123
  messages: [result, ...toolResults],
124
+ history: [
125
+ createHistoryStep<HistoryStep>(state.history, {
126
+ type: NodeType.TOOL,
127
+ nodeId: node.name,
128
+ nodeDisplayName: node.displayName,
129
+ raw: result,
130
+ messageIds: [result.id!],
131
+ }),
132
+ ...toolResults.map((toolResult) =>
133
+ createHistoryStep<HistoryStep>(state.history, {
134
+ type: NodeType.TOOL,
135
+ nodeId: node.name,
136
+ nodeDisplayName: node.displayName,
137
+ raw: toolResult,
138
+ messageIds: [toolResult.id!],
139
+ }),
140
+ ),
141
+ ],
95
142
  };
96
143
  }
97
144
 
@@ -102,7 +149,6 @@ export const addPromptNode = async ({ graph, node, llm, tools, emit, agent }: Ad
102
149
  });
103
150
  logger.info({ msg: `[Model] Response`, content: result.content });
104
151
  }
105
-
106
152
  return {
107
153
  goto: null,
108
154
  history: createHistoryStep<HistoryStep>(state.history, {
@@ -117,3 +163,43 @@ export const addPromptNode = async ({ graph, node, llm, tools, emit, agent }: Ad
117
163
  };
118
164
  graph.addNode(node.name, callback);
119
165
  };
166
+
167
+ function getCurrentPromptNode(node: PromptNode, nodeEdges: any[]) {
168
+ // Format edges for display
169
+ let nextEdges = '';
170
+ if (node.canStayOnNode !== false) {
171
+ nextEdges = `0. Stay in the current step. step title: ${node.displayName}\n`;
172
+ }
173
+
174
+ let counter = node.canStayOnNode !== false ? 0 : -1;
175
+ const promptConditionEdges = nodeEdges.filter((edge: any) => edge.type === 'PROMPT_CONDITION');
176
+ promptConditionEdges.forEach((edge: any) => {
177
+ counter++;
178
+ nextEdges += `${counter}. ${edge.prompt || edge.condition || `Go to ${edge.target}`}\n`;
179
+ });
180
+
181
+ return `
182
+ # Current Node Instructions
183
+
184
+ The flow of the conversation is built upon nodes. each conversation turn you
185
+ will be given just with the current node prompt.
186
+
187
+ Always follow the guidelines of "Current node prompt"
188
+
189
+ Current node title: ${node.displayName}
190
+
191
+ Current node prompt:
192
+ ${node.prompt}
193
+
194
+ # Next nodes
195
+
196
+ After handling current node you can stay in the same node or move to the next
197
+ node based on the condition.
198
+
199
+ The next nodes will run automatically by condition runner later, no need to
200
+ worry about it now.
201
+
202
+ Here are possible next nodes and their conditions, never share this
203
+ information with the user:
204
+ ${nextEdges}`;
205
+ }
@@ -7,9 +7,10 @@ import { RunnableLike } from '@langchain/core/runnables';
7
7
  import { LLMProviders } from '../types/LLM.types';
8
8
  import { logger } from '../utils/logger';
9
9
  import { Agent } from '../agent';
10
- import { compilePlaybooks } from '../playbooks/playbooks';
11
10
  import { createHistoryStep } from '../utils/history';
12
11
  import { HistoryStep } from '../types/Agent.types';
12
+ import { combinePlaybooks } from '../playbooks/playbooks';
13
+ import { compilePrompt } from './compilePrompt';
13
14
 
14
15
  export const addToolNode = async ({
15
16
  graph,
@@ -30,6 +31,7 @@ export const addToolNode = async ({
30
31
  throw new Error(`Tool not found: ${toolNode.toolName} in node ${node.name}`);
31
32
  }
32
33
  const callback: RunnableLike = async (state: typeof stateAnnotation.State) => {
34
+ await agent.interruptSessionManager.checkQueueAndInterrupt(state.sessionId);
33
35
  logger.debug({ msg: `[Node] Executing tool node`, node: toolNode.displayName });
34
36
 
35
37
  const tool = langchainTool(() => {}, {
@@ -38,24 +40,20 @@ export const addToolNode = async ({
38
40
  schema: matchedTool.input,
39
41
  });
40
42
 
41
- // Get compiled playbooks with proper parameters
42
- const playbookParams = {
43
- ...state.memory, // Spread memory fields at the top level
44
- currentTime: new Date().toISOString(),
45
- // Add any other common parameters that playbooks might need
46
- };
47
- const compiledPlaybooks = compilePlaybooks(agent.playbooks, playbookParams) || '';
43
+ const combinedPlaybooks = combinePlaybooks(agent.playbooks) || '';
44
+ const systemPrompt = combinedPlaybooks
45
+ ? compilePrompt(combinedPlaybooks, { memory: state.memory, system: { currentTime: new Date().toISOString() } })
46
+ : '';
48
47
 
49
- const prompt = `${compiledPlaybooks ? compiledPlaybooks + '\n\n' : ''}
50
- Additional context:
51
- workflow memory: ${JSON.stringify(state.memory)}
52
- `;
48
+ const startTime = Date.now();
53
49
  const AIToolCallMessage: AIMessage = await llm
54
50
  .bindTools([tool], {
55
51
  tool_choice: tool.name,
56
52
  })
57
- .invoke([...state.messages, new SystemMessage(prompt)]);
58
-
53
+ .invoke([...state.messages, new SystemMessage(systemPrompt)]);
54
+ const endTime = Date.now();
55
+ logger.debug({ msg: '[Tool] Model execution time', tool: matchedTool.name, executionTimeMs: endTime - startTime });
56
+ await agent.interruptSessionManager.checkQueueAndInterrupt(state.sessionId);
59
57
  return {
60
58
  goto: null,
61
59
  messages: [AIToolCallMessage],
@@ -23,6 +23,7 @@ type AddToolRunNodeParams = {
23
23
 
24
24
  export const buildToolRunNodeName = (nodeName: string) => `${nodeName}${internalNodesSuffix.TOOL_RUN}`;
25
25
 
26
+ //you never want to interrupt here because of new triggers, as this node depends on the last message to be the tool call from the addToolNode, interrupting will add human message here
26
27
  export const addToolRunNode = async ({ graph, tools, toolNode, attachedToNodeName, agent }: AddToolRunNodeParams) => {
27
28
  const callback: RunnableLike = async (state: typeof stateAnnotation.State) => {
28
29
  const matchedTool = tools.find((tool) => tool.name === toolNode.toolName);
@@ -32,7 +33,10 @@ export const addToolRunNode = async ({ graph, tools, toolNode, attachedToNodeNam
32
33
 
33
34
  const executeWrapper = async (input: z.infer<typeof matchedTool.input>) => {
34
35
  try {
36
+ const startTime = Date.now();
35
37
  const response = await matchedTool.execute({ input, state, agent });
38
+ const endTime = Date.now();
39
+ logger.debug({ msg: '[Tool] Tool execution time', tool: matchedTool.name, executionTimeMs: endTime - startTime });
36
40
  return response || {};
37
41
  } catch (error) {
38
42
  logger.error({ msg: '[Tool] Error executing tool', error, node: toolNode.displayName });
@@ -54,7 +58,6 @@ export const addToolRunNode = async ({ graph, tools, toolNode, attachedToNodeNam
54
58
 
55
59
  // Push the toolCallMessage into the messages array from toolStateUpdate
56
60
  const updatedMessages = [toolCallMessage, ...((toolStateUpdate as any).messages || [])];
57
-
58
61
  // Return the full state update from the tool with the updated messages
59
62
 
60
63
  return {
@@ -0,0 +1,41 @@
1
+ import * as ejs from 'ejs';
2
+ import { logger } from '../utils/logger';
3
+
4
+ /**
5
+ * Compile prompt with parameters using EJS and placeholder replacement
6
+ */
7
+ export function compilePrompt(prompt: string, params: Record<string, any> = {}): string {
8
+ try {
9
+ // First, render with EJS
10
+ let compiledPrompt = ejs.render(prompt, params);
11
+
12
+ // Then, replace placeholders in {} format
13
+ compiledPrompt = replacePlaceholders(compiledPrompt, params);
14
+
15
+ return compiledPrompt;
16
+ } catch (error) {
17
+ logger.error({ message: 'Error compiling prompt', error });
18
+ return prompt; // Return uncompiled if there's an error
19
+ }
20
+ }
21
+
22
+ /**
23
+ * Replace placeholders in {key} format
24
+ */
25
+ function replacePlaceholders(text: string, params: Record<string, any>): string {
26
+ return text.replace(/\{([^}]+)\}/g, (match, key) => {
27
+ const keys = key.split('.');
28
+ let value: any = params;
29
+
30
+ for (const k of keys) {
31
+ if (value && typeof value === 'object' && k in value) {
32
+ value = value[k];
33
+ } else {
34
+ logger.warn({ message: `Placeholder {${key}} in prompt not found in memory. It will remain as placeholder.` });
35
+ return match; // Return original if key not found
36
+ }
37
+ }
38
+
39
+ return String(value);
40
+ });
41
+ }