@minded-ai/mindedjs 1.0.91 → 1.0.92-beta-3
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/dist/agent.d.ts +4 -1
- package/dist/agent.d.ts.map +1 -1
- package/dist/agent.js +96 -15
- package/dist/agent.js.map +1 -1
- package/dist/cli/index.js +12 -12
- package/dist/cli/index.js.map +1 -1
- package/dist/edges/createDirectEdge.js +1 -1
- package/dist/edges/createDirectEdge.js.map +1 -1
- package/dist/edges/createLogicalRouter.d.ts.map +1 -1
- package/dist/edges/createLogicalRouter.js +7 -7
- package/dist/edges/createLogicalRouter.js.map +1 -1
- package/dist/edges/createPromptRouter.d.ts.map +1 -1
- package/dist/edges/createPromptRouter.js +4 -3
- package/dist/edges/createPromptRouter.js.map +1 -1
- package/dist/edges/edgeFactory.js +2 -2
- package/dist/edges/edgeFactory.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -2
- package/dist/index.js.map +1 -1
- package/dist/interrupts/InterruptSessionManager.types.d.ts +36 -0
- package/dist/interrupts/InterruptSessionManager.types.d.ts.map +1 -0
- package/dist/interrupts/InterruptSessionManager.types.js +40 -0
- package/dist/interrupts/InterruptSessionManager.types.js.map +1 -0
- package/dist/interrupts/MemoryInterruptSessionManager.d.ts +14 -0
- package/dist/interrupts/MemoryInterruptSessionManager.d.ts.map +1 -0
- package/dist/interrupts/MemoryInterruptSessionManager.js +51 -0
- package/dist/interrupts/MemoryInterruptSessionManager.js.map +1 -0
- package/dist/interrupts/MindedInterruptSessionManager.d.ts +15 -0
- package/dist/interrupts/MindedInterruptSessionManager.d.ts.map +1 -0
- package/dist/interrupts/MindedInterruptSessionManager.js +121 -0
- package/dist/interrupts/MindedInterruptSessionManager.js.map +1 -0
- package/dist/interrupts/interruptSessionManagerFactory.d.ts +4 -0
- package/dist/interrupts/interruptSessionManagerFactory.d.ts.map +1 -0
- package/dist/interrupts/interruptSessionManagerFactory.js +22 -0
- package/dist/interrupts/interruptSessionManagerFactory.js.map +1 -0
- package/dist/nodes/addAppToolNode.d.ts.map +1 -1
- package/dist/nodes/addAppToolNode.js +3 -3
- package/dist/nodes/addAppToolNode.js.map +1 -1
- package/dist/nodes/addHumanInTheLoopNode.d.ts.map +1 -1
- package/dist/nodes/addHumanInTheLoopNode.js +3 -2
- package/dist/nodes/addHumanInTheLoopNode.js.map +1 -1
- package/dist/nodes/addJumpToNode.d.ts.map +1 -1
- package/dist/nodes/addJumpToNode.js +3 -3
- package/dist/nodes/addJumpToNode.js.map +1 -1
- package/dist/nodes/addJunctionNode.d.ts +7 -0
- package/dist/nodes/addJunctionNode.d.ts.map +1 -0
- package/dist/nodes/addJunctionNode.js +20 -0
- package/dist/nodes/addJunctionNode.js.map +1 -0
- package/dist/nodes/addPromptNode.d.ts.map +1 -1
- package/dist/nodes/addPromptNode.js +48 -3
- package/dist/nodes/addPromptNode.js.map +1 -1
- package/dist/nodes/addToolNode.d.ts.map +1 -1
- package/dist/nodes/addToolNode.js +3 -3
- package/dist/nodes/addToolNode.js.map +1 -1
- package/dist/nodes/addToolRunNode.d.ts.map +1 -1
- package/dist/nodes/addToolRunNode.js +1 -2
- package/dist/nodes/addToolRunNode.js.map +1 -1
- package/dist/nodes/addTriggerNode.d.ts.map +1 -1
- package/dist/nodes/addTriggerNode.js +2 -3
- package/dist/nodes/addTriggerNode.js.map +1 -1
- package/dist/nodes/nodeFactory.d.ts.map +1 -1
- package/dist/nodes/nodeFactory.js +2 -5
- package/dist/nodes/nodeFactory.js.map +1 -1
- package/dist/platform/mindedConnection.d.ts.map +1 -1
- package/dist/platform/mindedConnection.js +13 -13
- package/dist/platform/mindedConnection.js.map +1 -1
- package/dist/platform/mindedConnectionTypes.d.ts +82 -1
- package/dist/platform/mindedConnectionTypes.d.ts.map +1 -1
- package/dist/platform/mindedConnectionTypes.js +9 -0
- package/dist/platform/mindedConnectionTypes.js.map +1 -1
- package/dist/playbooks/playbooks.js +4 -4
- package/dist/playbooks/playbooks.js.map +1 -1
- package/dist/types/Agent.types.d.ts +11 -13
- package/dist/types/Agent.types.d.ts.map +1 -1
- package/dist/types/Agent.types.js +1 -10
- package/dist/types/Agent.types.js.map +1 -1
- package/dist/types/LangGraph.types.d.ts +2 -2
- package/dist/types/LangGraph.types.d.ts.map +1 -1
- package/dist/types/LangGraph.types.js +3 -1
- package/dist/types/LangGraph.types.js.map +1 -1
- package/dist/utils/logger.d.ts.map +1 -1
- package/dist/utils/logger.js +1 -0
- package/dist/utils/logger.js.map +1 -1
- package/dist/voice/voiceSession.d.ts.map +1 -1
- package/dist/voice/voiceSession.js +10 -2
- package/dist/voice/voiceSession.js.map +1 -1
- package/package.json +2 -2
- package/src/agent.ts +122 -24
- package/src/cli/index.ts +12 -12
- package/src/edges/createDirectEdge.ts +1 -1
- package/src/edges/createLogicalRouter.ts +7 -8
- package/src/edges/createPromptRouter.ts +4 -3
- package/src/edges/edgeFactory.ts +2 -2
- package/src/index.ts +0 -1
- package/src/interrupts/InterruptSessionManager.types.ts +80 -0
- package/src/interrupts/MemoryInterruptSessionManager.ts +56 -0
- package/src/interrupts/MindedInterruptSessionManager.ts +157 -0
- package/src/interrupts/interruptSessionManagerFactory.ts +20 -0
- package/src/nodes/addAppToolNode.ts +4 -4
- package/src/nodes/addHumanInTheLoopNode.ts +3 -2
- package/src/nodes/addJumpToNode.ts +4 -4
- package/src/nodes/addJunctionNode.ts +19 -0
- package/src/nodes/addPromptNode.ts +54 -8
- package/src/nodes/addToolNode.ts +3 -4
- package/src/nodes/addToolRunNode.ts +2 -3
- package/src/nodes/addTriggerNode.ts +4 -4
- package/src/nodes/nodeFactory.ts +2 -7
- package/src/platform/mindedConnection.ts +13 -13
- package/src/platform/mindedConnectionTypes.ts +100 -0
- package/src/playbooks/playbooks.ts +4 -4
- package/src/types/Agent.types.ts +12 -15
- package/src/types/LangGraph.types.ts +3 -1
- package/src/utils/logger.ts +1 -0
- package/src/voice/voiceSession.ts +14 -6
|
@@ -11,8 +11,8 @@ export const createLogicalRouter = ({ edges }: { edges: LogicalConditionEdge[] }
|
|
|
11
11
|
logger.debug(`Evaluating logical conditions for ${edges.length} edges`);
|
|
12
12
|
|
|
13
13
|
// Separate regular conditions from "else" conditions
|
|
14
|
-
const regularEdges = edges.filter(edge => edge.condition.trim() !== 'else');
|
|
15
|
-
const elseEdges = edges.filter(edge => edge.condition.trim() === 'else');
|
|
14
|
+
const regularEdges = edges.filter((edge) => edge.condition.trim() !== 'else');
|
|
15
|
+
const elseEdges = edges.filter((edge) => edge.condition.trim() === 'else');
|
|
16
16
|
|
|
17
17
|
// First, evaluate all regular conditions
|
|
18
18
|
for (const edge of regularEdges) {
|
|
@@ -59,17 +59,16 @@ export const createLogicalRouter = ({ edges }: { edges: LogicalConditionEdge[] }
|
|
|
59
59
|
]);
|
|
60
60
|
|
|
61
61
|
if (result === true) {
|
|
62
|
-
logger.info(`Condition matched for edge ${edge.source} → ${edge.target}`);
|
|
62
|
+
logger.info({ message: `Condition matched for edge ${edge.source} → ${edge.target}` });
|
|
63
63
|
return edge.target;
|
|
64
64
|
}
|
|
65
|
-
|
|
66
65
|
} catch (error) {
|
|
67
66
|
// Provide detailed error information back to the customer
|
|
68
67
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
69
68
|
const conditionPreview = edge.condition.length > 100 ? `${edge.condition.substring(0, 100)}...` : edge.condition;
|
|
70
69
|
|
|
71
70
|
logger.error({
|
|
72
|
-
|
|
71
|
+
message: `Error evaluating condition for edge ${edge.source} → ${edge.target}`,
|
|
73
72
|
condition: conditionPreview,
|
|
74
73
|
error: errorMessage,
|
|
75
74
|
edgeIndex: edges.indexOf(edge),
|
|
@@ -84,15 +83,15 @@ export const createLogicalRouter = ({ edges }: { edges: LogicalConditionEdge[] }
|
|
|
84
83
|
|
|
85
84
|
// If no regular conditions matched, check for "else" conditions
|
|
86
85
|
if (elseEdges.length > 0) {
|
|
87
|
-
logger.info(`No regular conditions matched, evaluating ${elseEdges.length} else condition(s)`);
|
|
86
|
+
logger.info({ message: `No regular conditions matched, evaluating ${elseEdges.length} else condition(s)` });
|
|
88
87
|
// Return the first "else" condition's target
|
|
89
88
|
const elseEdge = elseEdges[0];
|
|
90
|
-
logger.info(`Else condition matched for edge ${elseEdge.source} → ${elseEdge.target}
|
|
89
|
+
logger.info({ message: `Else condition matched for edge ${elseEdge.source} → ${elseEdge.target}`, edge: elseEdge });
|
|
91
90
|
return elseEdge.target;
|
|
92
91
|
}
|
|
93
92
|
|
|
94
93
|
// If no conditions matched or all failed, return to the source node
|
|
95
|
-
logger.info('No conditions matched');
|
|
94
|
+
logger.info({ message: 'No conditions matched' });
|
|
96
95
|
return null;
|
|
97
96
|
};
|
|
98
97
|
};
|
|
@@ -70,11 +70,11 @@ export const createPromptRouter = ({
|
|
|
70
70
|
currentNodeName?: string;
|
|
71
71
|
}) => {
|
|
72
72
|
return async (state: typeof stateAnnotation.State) => {
|
|
73
|
-
logger.info(`Executing prompt router. Edges: ${JSON.stringify(edges)}`);
|
|
73
|
+
logger.info({ message: `Executing prompt router. Edges: ${JSON.stringify(edges)}` });
|
|
74
74
|
|
|
75
75
|
// If canStayInCurrentNode is true and there are no edges, return current node immediately
|
|
76
76
|
if (canStayInCurrentNode && edges.length === 0 && currentNodeName) {
|
|
77
|
-
logger.info(`No edges available and canStayInCurrentNode is true, staying in current node: ${currentNodeName}`);
|
|
77
|
+
logger.info({ message: `No edges available and canStayInCurrentNode is true, staying in current node: ${currentNodeName}` });
|
|
78
78
|
return currentNodeName;
|
|
79
79
|
}
|
|
80
80
|
|
|
@@ -174,13 +174,14 @@ export const createPromptRouter = ({
|
|
|
174
174
|
|
|
175
175
|
const decision = validatedResponse.nextNodeId === currentNodeName ? 'stay in current node' : validatedResponse.nextNodeId;
|
|
176
176
|
const reasoning = includeReasoning && 'reasoning' in validatedResponse ? ` - Reasoning: ${validatedResponse.reasoning}` : '';
|
|
177
|
-
logger.info({
|
|
177
|
+
logger.info({ message: `Router decision: ${decision}`, reasoning });
|
|
178
178
|
|
|
179
179
|
return validatedResponse.nextNodeId;
|
|
180
180
|
} catch (error) {
|
|
181
181
|
lastError = error instanceof Error ? error : new Error(String(error));
|
|
182
182
|
logger.warn({
|
|
183
183
|
message: `Prompt router attempt ${attempts} failed`,
|
|
184
|
+
edge: edges,
|
|
184
185
|
error: lastError.message,
|
|
185
186
|
attempt: attempts,
|
|
186
187
|
maxRetries,
|
package/src/edges/edgeFactory.ts
CHANGED
|
@@ -49,7 +49,7 @@ export const edgeFactory = ({
|
|
|
49
49
|
if (result) {
|
|
50
50
|
return result;
|
|
51
51
|
} else {
|
|
52
|
-
logger.debug('No logical conditions matched, continuing to prompt conditions');
|
|
52
|
+
logger.debug({ message: 'No logical conditions matched, continuing to prompt conditions' });
|
|
53
53
|
}
|
|
54
54
|
}
|
|
55
55
|
|
|
@@ -69,7 +69,7 @@ export const edgeFactory = ({
|
|
|
69
69
|
|
|
70
70
|
// Fallback: stay at current source node
|
|
71
71
|
const source = originalNode?.name || sourceNode;
|
|
72
|
-
logger.info(`No conditions matched, returning to source: ${source}`);
|
|
72
|
+
logger.info({ message: `No conditions matched, returning to source: ${source}` });
|
|
73
73
|
return source;
|
|
74
74
|
};
|
|
75
75
|
};
|
package/src/index.ts
CHANGED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { State } from '../types/LangGraph.types';
|
|
2
|
+
import { GraphInterrupt } from '@langchain/langgraph';
|
|
3
|
+
import { logger } from '../utils/logger';
|
|
4
|
+
|
|
5
|
+
export enum InterruptType {
|
|
6
|
+
NEW_TRIGGERS = 'NEW_TRIGGERS',
|
|
7
|
+
HUMAN_IN_THE_LOOP = 'HUMAN_IN_THE_LOOP',
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface QueuedMessage {
|
|
11
|
+
triggerBody: any;
|
|
12
|
+
triggerName: string;
|
|
13
|
+
appName?: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// export interface UpdateStateObject {
|
|
17
|
+
// memory: any;
|
|
18
|
+
// messages: any[];
|
|
19
|
+
// history: any[];
|
|
20
|
+
// sessionId: string;
|
|
21
|
+
// sessionType: SessionType;
|
|
22
|
+
// }
|
|
23
|
+
export interface InterruptPayload {
|
|
24
|
+
type: InterruptType.NEW_TRIGGERS;
|
|
25
|
+
updateStateObject?: Partial<State>;
|
|
26
|
+
id?: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface InterruptSessionManager {
|
|
30
|
+
isProcessed(sessionId: string): boolean | Promise<boolean>;
|
|
31
|
+
lock(sessionId: string): void | Promise<void>;
|
|
32
|
+
release(sessionId: string): void | Promise<void>;
|
|
33
|
+
enqueueMessage(sessionId: string, message: QueuedMessage): void | Promise<void>;
|
|
34
|
+
dequeueAll(sessionId: string): QueuedMessage[] | Promise<QueuedMessage[]>;
|
|
35
|
+
dequeue(sessionId: string): QueuedMessage | null | Promise<QueuedMessage | null>;
|
|
36
|
+
checkQueueAndInterrupt(sessionId: string, updateStateObject?: Partial<State>): Promise<boolean>;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export abstract class BaseInterruptSessionManager implements InterruptSessionManager {
|
|
40
|
+
// Abstract methods that subclasses must implement for queue management
|
|
41
|
+
abstract isProcessed(sessionId: string): boolean | Promise<boolean>;
|
|
42
|
+
abstract lock(sessionId: string): void | Promise<void>;
|
|
43
|
+
abstract release(sessionId: string): void | Promise<void>;
|
|
44
|
+
abstract enqueueMessage(sessionId: string, message: QueuedMessage): void | Promise<void>;
|
|
45
|
+
abstract dequeueAll(sessionId: string): QueuedMessage[] | Promise<QueuedMessage[]>;
|
|
46
|
+
abstract dequeue(sessionId: string): QueuedMessage | null | Promise<QueuedMessage | null>;
|
|
47
|
+
|
|
48
|
+
// Abstract method to check if queue has messages - this is the only queue-specific part
|
|
49
|
+
protected abstract hasQueuedMessages(sessionId: string): boolean | Promise<boolean>;
|
|
50
|
+
protected abstract getQueuedMessages(sessionId: string): QueuedMessage[] | Promise<QueuedMessage[]>;
|
|
51
|
+
|
|
52
|
+
// Common implementation of checkQueueAndInterrupt
|
|
53
|
+
async checkQueueAndInterrupt(sessionId: string, updateStateObject?: Partial<State>): Promise<boolean> {
|
|
54
|
+
if (await this.hasQueuedMessages(sessionId)) {
|
|
55
|
+
const queue = await this.getQueuedMessages(sessionId);
|
|
56
|
+
logger.trace({ message: 'graph has queued messagess, interrupting graph', sessionId, queue });
|
|
57
|
+
|
|
58
|
+
// Interrupt the graph with NEW_TRIGGERS flag and optional updateStateObject
|
|
59
|
+
const interruptPayload: InterruptPayload = { type: InterruptType.NEW_TRIGGERS };
|
|
60
|
+
if (updateStateObject) {
|
|
61
|
+
interruptPayload.updateStateObject = {
|
|
62
|
+
messages: updateStateObject.messages,
|
|
63
|
+
memory: updateStateObject.memory,
|
|
64
|
+
history: updateStateObject.history,
|
|
65
|
+
sessionId: updateStateObject.sessionId,
|
|
66
|
+
sessionType: updateStateObject.sessionType,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
throw new GraphInterrupt([
|
|
71
|
+
{
|
|
72
|
+
value: interruptPayload,
|
|
73
|
+
when: 'during',
|
|
74
|
+
resumable: true,
|
|
75
|
+
},
|
|
76
|
+
]);
|
|
77
|
+
}
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { State } from '../types/LangGraph.types';
|
|
2
|
+
import { logger } from '../utils/logger';
|
|
3
|
+
import { BaseInterruptSessionManager, QueuedMessage, InterruptType, InterruptPayload } from './InterruptSessionManager.types';
|
|
4
|
+
import { GraphInterrupt, interrupt } from '@langchain/langgraph';
|
|
5
|
+
import { v4 as uuidv4 } from 'uuid';
|
|
6
|
+
|
|
7
|
+
export class MemoryInterruptSessionManager extends BaseInterruptSessionManager {
|
|
8
|
+
private sessionProcessing: Map<string, boolean> = new Map();
|
|
9
|
+
private sessionMessageQueues: Map<string, QueuedMessage[]> = new Map();
|
|
10
|
+
|
|
11
|
+
isProcessed(sessionId: string): boolean {
|
|
12
|
+
return this.sessionProcessing.get(sessionId) || false;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
lock(sessionId: string): void {
|
|
16
|
+
this.sessionProcessing.set(sessionId, true);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
release(sessionId: string): void {
|
|
20
|
+
this.sessionProcessing.set(sessionId, false);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
enqueueMessage(sessionId: string, message: QueuedMessage): void {
|
|
24
|
+
if (!this.sessionMessageQueues.has(sessionId)) {
|
|
25
|
+
this.sessionMessageQueues.set(sessionId, []);
|
|
26
|
+
}
|
|
27
|
+
this.sessionMessageQueues.get(sessionId)!.push(message);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
dequeueAll(sessionId: string): QueuedMessage[] {
|
|
31
|
+
const messages = this.sessionMessageQueues.get(sessionId) || [];
|
|
32
|
+
const result = [...messages];
|
|
33
|
+
this.sessionMessageQueues.set(sessionId, []);
|
|
34
|
+
return result;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
dequeue(sessionId: string): QueuedMessage | null {
|
|
38
|
+
const messages = this.sessionMessageQueues.get(sessionId) || [];
|
|
39
|
+
if (messages.length === 0) {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
const firstMessage = messages.shift()!;
|
|
43
|
+
this.sessionMessageQueues.set(sessionId, messages);
|
|
44
|
+
return firstMessage;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Implementation of abstract methods from BaseInterruptSessionManager
|
|
48
|
+
protected hasQueuedMessages(sessionId: string): boolean {
|
|
49
|
+
const queue = this.sessionMessageQueues.get(sessionId) || [];
|
|
50
|
+
return queue.length > 0;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
protected getQueuedMessages(sessionId: string): QueuedMessage[] {
|
|
54
|
+
return this.sessionMessageQueues.get(sessionId) || [];
|
|
55
|
+
}
|
|
56
|
+
}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import { BaseInterruptSessionManager, QueuedMessage } from './InterruptSessionManager.types';
|
|
2
|
+
import { 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
|
+
private mindedConnection: MindedConnection;
|
|
25
|
+
|
|
26
|
+
constructor(mindedConnection: MindedConnection) {
|
|
27
|
+
super();
|
|
28
|
+
this.mindedConnection = mindedConnection;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
async isProcessed(sessionId: string): Promise<boolean> {
|
|
32
|
+
try {
|
|
33
|
+
const response = await this.mindedConnection.awaitEmit<InterruptSessionIsProcessedRequest, InterruptSessionIsProcessedResponse>(
|
|
34
|
+
MindedConnectionSocketMessageType.INTERRUPT_SESSION_IS_PROCESSED,
|
|
35
|
+
{
|
|
36
|
+
type: MindedConnectionSocketMessageType.INTERRUPT_SESSION_IS_PROCESSED,
|
|
37
|
+
sessionId,
|
|
38
|
+
},
|
|
39
|
+
);
|
|
40
|
+
return response.isProcessed ?? false;
|
|
41
|
+
} catch (error) {
|
|
42
|
+
console.error('Error checking if session is processed:', error);
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
async lock(sessionId: string): Promise<void> {
|
|
48
|
+
try {
|
|
49
|
+
await this.mindedConnection.awaitEmit<InterruptSessionLockRequest, InterruptSessionLockResponse>(
|
|
50
|
+
MindedConnectionSocketMessageType.INTERRUPT_SESSION_LOCK,
|
|
51
|
+
{
|
|
52
|
+
type: MindedConnectionSocketMessageType.INTERRUPT_SESSION_LOCK,
|
|
53
|
+
sessionId,
|
|
54
|
+
},
|
|
55
|
+
);
|
|
56
|
+
} catch (error) {
|
|
57
|
+
console.error('Error locking session:', error);
|
|
58
|
+
throw error;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
async release(sessionId: string): Promise<void> {
|
|
63
|
+
try {
|
|
64
|
+
await this.mindedConnection.awaitEmit<InterruptSessionReleaseRequest, InterruptSessionReleaseResponse>(
|
|
65
|
+
MindedConnectionSocketMessageType.INTERRUPT_SESSION_RELEASE,
|
|
66
|
+
{
|
|
67
|
+
type: MindedConnectionSocketMessageType.INTERRUPT_SESSION_RELEASE,
|
|
68
|
+
sessionId,
|
|
69
|
+
},
|
|
70
|
+
);
|
|
71
|
+
} catch (error) {
|
|
72
|
+
console.error('Error releasing session:', error);
|
|
73
|
+
throw error;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
async enqueueMessage(sessionId: string, message: QueuedMessage): Promise<void> {
|
|
78
|
+
try {
|
|
79
|
+
await this.mindedConnection.awaitEmit<InterruptSessionEnqueueRequest, InterruptSessionEnqueueResponse>(
|
|
80
|
+
MindedConnectionSocketMessageType.INTERRUPT_SESSION_ENQUEUE,
|
|
81
|
+
{
|
|
82
|
+
type: MindedConnectionSocketMessageType.INTERRUPT_SESSION_ENQUEUE,
|
|
83
|
+
sessionId,
|
|
84
|
+
message,
|
|
85
|
+
},
|
|
86
|
+
);
|
|
87
|
+
} catch (error) {
|
|
88
|
+
console.error('Error enqueuing message:', error);
|
|
89
|
+
throw error;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
async dequeueAll(sessionId: string): Promise<QueuedMessage[]> {
|
|
94
|
+
try {
|
|
95
|
+
const response = await this.mindedConnection.awaitEmit<InterruptSessionDequeueAllRequest, InterruptSessionDequeueAllResponse>(
|
|
96
|
+
MindedConnectionSocketMessageType.INTERRUPT_SESSION_DEQUEUE_ALL,
|
|
97
|
+
{
|
|
98
|
+
type: MindedConnectionSocketMessageType.INTERRUPT_SESSION_DEQUEUE_ALL,
|
|
99
|
+
sessionId,
|
|
100
|
+
},
|
|
101
|
+
);
|
|
102
|
+
return response.messages ?? [];
|
|
103
|
+
} catch (error) {
|
|
104
|
+
console.error('Error dequeuing all messages:', error);
|
|
105
|
+
return [];
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
async dequeue(sessionId: string): Promise<QueuedMessage | null> {
|
|
110
|
+
try {
|
|
111
|
+
const response = await this.mindedConnection.awaitEmit<InterruptSessionDequeueRequest, InterruptSessionDequeueResponse>(
|
|
112
|
+
MindedConnectionSocketMessageType.INTERRUPT_SESSION_DEQUEUE,
|
|
113
|
+
{
|
|
114
|
+
type: MindedConnectionSocketMessageType.INTERRUPT_SESSION_DEQUEUE,
|
|
115
|
+
sessionId,
|
|
116
|
+
},
|
|
117
|
+
);
|
|
118
|
+
return response.message ?? null;
|
|
119
|
+
} catch (error) {
|
|
120
|
+
console.error('Error dequeuing message:', error);
|
|
121
|
+
return null;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Implementation of abstract methods from BaseInterruptSessionManager
|
|
126
|
+
protected async hasQueuedMessages(sessionId: string): Promise<boolean> {
|
|
127
|
+
try {
|
|
128
|
+
const response = await this.mindedConnection.awaitEmit<InterruptSessionHasMessagesRequest, InterruptSessionHasMessagesResponse>(
|
|
129
|
+
MindedConnectionSocketMessageType.INTERRUPT_SESSION_HAS_MESSAGES,
|
|
130
|
+
{
|
|
131
|
+
type: MindedConnectionSocketMessageType.INTERRUPT_SESSION_HAS_MESSAGES,
|
|
132
|
+
sessionId,
|
|
133
|
+
},
|
|
134
|
+
);
|
|
135
|
+
return response.hasMessages ?? false;
|
|
136
|
+
} catch (error) {
|
|
137
|
+
console.error('Error checking if session has messages:', error);
|
|
138
|
+
return false;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
protected async getQueuedMessages(sessionId: string): Promise<QueuedMessage[]> {
|
|
143
|
+
try {
|
|
144
|
+
const response = await this.mindedConnection.awaitEmit<InterruptSessionGetMessagesRequest, InterruptSessionGetMessagesResponse>(
|
|
145
|
+
MindedConnectionSocketMessageType.INTERRUPT_SESSION_GET_MESSAGES,
|
|
146
|
+
{
|
|
147
|
+
type: MindedConnectionSocketMessageType.INTERRUPT_SESSION_GET_MESSAGES,
|
|
148
|
+
sessionId,
|
|
149
|
+
},
|
|
150
|
+
);
|
|
151
|
+
return response.messages ?? [];
|
|
152
|
+
} catch (error) {
|
|
153
|
+
console.error('Error getting queued messages:', error);
|
|
154
|
+
return [];
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { InterruptSessionManager } from './InterruptSessionManager.types';
|
|
2
|
+
import { MemoryInterruptSessionManager } from './MemoryInterruptSessionManager';
|
|
3
|
+
import { MindedInterruptSessionManager } from './MindedInterruptSessionManager';
|
|
4
|
+
import { MindedConnection } from '../platform/mindedConnection';
|
|
5
|
+
import { getConfig } from '../platform/config';
|
|
6
|
+
import { logger } from '../utils/logger';
|
|
7
|
+
|
|
8
|
+
export function createInterruptSessionManager(mindedConnection: MindedConnection | null): InterruptSessionManager {
|
|
9
|
+
const { runLocally } = getConfig();
|
|
10
|
+
if (runLocally) {
|
|
11
|
+
logger.info({ message: 'Using memory interrupt session manager' });
|
|
12
|
+
return new MemoryInterruptSessionManager();
|
|
13
|
+
} else {
|
|
14
|
+
if (!mindedConnection) {
|
|
15
|
+
throw new Error('MindedConnection is required for platform interrupt session manager');
|
|
16
|
+
}
|
|
17
|
+
logger.info({ message: 'Using Minded interrupt session manager' });
|
|
18
|
+
return new MindedInterruptSessionManager(mindedConnection);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AppToolNode } from '../types/Flows.types';
|
|
1
|
+
import { AppToolNode, NodeType } from '../types/Flows.types';
|
|
2
2
|
import { tool as langchainTool } from '@langchain/core/tools';
|
|
3
3
|
import { PreCompiledGraph, stateAnnotation } from '../types/LangGraph.types';
|
|
4
4
|
import { SystemMessage } from '@langchain/core/messages';
|
|
@@ -6,7 +6,7 @@ import { RunnableLike } from '@langchain/core/runnables';
|
|
|
6
6
|
import { z } from 'zod';
|
|
7
7
|
import { LLMProviders } from '../types/LLM.types';
|
|
8
8
|
import { getAppActionRunnerTool } from '../internalTools/appActionRunnerTool';
|
|
9
|
-
import { AppActionInvocationHistoryStep
|
|
9
|
+
import { AppActionInvocationHistoryStep } from '../types/Agent.types';
|
|
10
10
|
import { Agent } from '../agent';
|
|
11
11
|
import { logger } from '../utils/logger';
|
|
12
12
|
import { compilePlaybooks } from '../playbooks/playbooks';
|
|
@@ -26,7 +26,7 @@ export const addAppToolNode = async ({
|
|
|
26
26
|
const cleanedParameters = Object.fromEntries(Object.entries(node.parameters || {}).filter(([, value]) => value !== ''));
|
|
27
27
|
const appRunnerTool = getAppActionRunnerTool(node.displayName!);
|
|
28
28
|
const callback: RunnableLike = async (state: typeof stateAnnotation.State) => {
|
|
29
|
-
logger.info(`Executing tool node ${appRunnerTool.name}`);
|
|
29
|
+
logger.info({ message: `Executing tool node ${appRunnerTool.name}` });
|
|
30
30
|
|
|
31
31
|
const executeWrapper = async (input: z.infer<typeof appRunnerTool.input>) => {
|
|
32
32
|
try {
|
|
@@ -82,7 +82,7 @@ export const addAppToolNode = async ({
|
|
|
82
82
|
return {
|
|
83
83
|
messages: [AIToolCallMessage, toolCallMessage],
|
|
84
84
|
history: createHistoryStep<AppActionInvocationHistoryStep>(state.history, {
|
|
85
|
-
type:
|
|
85
|
+
type: NodeType.APP_TOOL,
|
|
86
86
|
nodeId: node.name,
|
|
87
87
|
nodeDisplayName: node.displayName!,
|
|
88
88
|
raw: AIToolCallMessage.tool_calls[0],
|
|
@@ -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/InterruptSessionManager.types';
|
|
6
7
|
|
|
7
8
|
type AddHumanInTheLoopNodeParams = {
|
|
8
9
|
graph: PreCompiledGraph;
|
|
@@ -13,10 +14,10 @@ export const buildHumanInTheLoopNodeName = (nodeName: string) => `${nodeName}${i
|
|
|
13
14
|
|
|
14
15
|
export const addHumanInTheLoopNode = async ({ graph, attachedToNodeName }: AddHumanInTheLoopNodeParams) => {
|
|
15
16
|
const callback: RunnableLike = async (state: typeof stateAnnotation.State) => {
|
|
16
|
-
logger.info(`Executing "human in the loop" node for the attached node${attachedToNodeName}`);
|
|
17
|
+
logger.info({ message: `Executing "human in the loop" node for the attached node${attachedToNodeName}` });
|
|
17
18
|
|
|
18
19
|
if (state.messages[state.messages.length - 1].getType() === 'ai') {
|
|
19
|
-
const value = interrupt(
|
|
20
|
+
const value = interrupt({ type: InterruptType.HUMAN_IN_THE_LOOP });
|
|
20
21
|
return value;
|
|
21
22
|
}
|
|
22
23
|
};
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
import { RunnableLike } from '@langchain/core/runnables';
|
|
2
2
|
import { PreCompiledGraph, stateAnnotation } from '../types/LangGraph.types';
|
|
3
|
-
import { JumpToNode } from '../types/Flows.types';
|
|
3
|
+
import { JumpToNode, NodeType } from '../types/Flows.types';
|
|
4
4
|
import { logger } from '../utils/logger';
|
|
5
|
-
import {
|
|
5
|
+
import { HistoryStep } from '../types/Agent.types';
|
|
6
6
|
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({ message: `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
13
|
history: createHistoryStep<HistoryStep>(state.history, {
|
|
14
|
-
type:
|
|
14
|
+
type: NodeType.JUMP_TO_NODE,
|
|
15
15
|
nodeId: node.name,
|
|
16
16
|
nodeDisplayName: node.displayName,
|
|
17
17
|
raw: '',
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { JunctionNode, NodeType } from '../types/Flows.types';
|
|
2
|
+
import { PreCompiledGraph, stateAnnotation } from '../types/LangGraph.types';
|
|
3
|
+
import { RunnableLike } from '@langchain/core/runnables';
|
|
4
|
+
import { createHistoryStep } from '../utils/history';
|
|
5
|
+
import { HistoryStep } from '../types/Agent.types';
|
|
6
|
+
|
|
7
|
+
export const addJunctionNode = ({ graph, node }: { graph: PreCompiledGraph; node: JunctionNode }) => {
|
|
8
|
+
const callback: RunnableLike = async (state: typeof stateAnnotation.State) => {
|
|
9
|
+
return {
|
|
10
|
+
history: createHistoryStep<HistoryStep>(state.history, {
|
|
11
|
+
type: NodeType.JUNCTION,
|
|
12
|
+
nodeId: node.name,
|
|
13
|
+
nodeDisplayName: node.displayName,
|
|
14
|
+
raw: '',
|
|
15
|
+
}),
|
|
16
|
+
};
|
|
17
|
+
};
|
|
18
|
+
graph.addNode(node.name, callback);
|
|
19
|
+
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { RunnableLike } from '@langchain/core/runnables';
|
|
2
|
-
import { PromptNode } from '../types/Flows.types';
|
|
2
|
+
import { NodeType, PromptNode } from '../types/Flows.types';
|
|
3
3
|
import { PreCompiledGraph, stateAnnotation } from '../types/LangGraph.types';
|
|
4
4
|
import { z } from 'zod';
|
|
5
5
|
import { BaseLanguageModel } from '@langchain/core/language_models/base';
|
|
@@ -7,14 +7,15 @@ import { SystemMessage, AIMessage, ToolMessage } from '@langchain/core/messages'
|
|
|
7
7
|
import { Tool } from '../types/Tools.types';
|
|
8
8
|
import { tool as langchainTool } from '@langchain/core/tools';
|
|
9
9
|
import { AgentEventRequestPayloads, AgentEvents } from '../events/AgentEvents';
|
|
10
|
-
import { EmitSignature,
|
|
10
|
+
import { EmitSignature, HistoryStep } from '../types/Agent.types';
|
|
11
11
|
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
15
|
import { compilePlaybooks } from '../playbooks/playbooks';
|
|
16
16
|
import { createHistoryStep } from '../utils/history';
|
|
17
|
-
|
|
17
|
+
import { v4 as uuidv4 } from 'uuid';
|
|
18
|
+
const wait = (ms: number) => new Promise((r) => setTimeout(r, ms));
|
|
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
|
-
|
|
30
|
+
await agent.interruptSessionManager.checkQueueAndInterrupt(state.sessionId);
|
|
31
|
+
logger.info({ message: `Executing prompt node ${node.displayName}` });
|
|
30
32
|
const llmToUse = node.llmConfig ? createLlmInstance(node.llmConfig) : llm;
|
|
31
33
|
|
|
32
34
|
const globalTools = tools
|
|
@@ -52,7 +54,7 @@ export const addPromptNode = async ({ graph, node, llm, tools, emit, agent }: Ad
|
|
|
52
54
|
`;
|
|
53
55
|
|
|
54
56
|
const result: AIMessage = await llmToUse.bindTools(globalTools).invoke([...state.messages, new SystemMessage(message)]);
|
|
55
|
-
|
|
57
|
+
await agent.interruptSessionManager.checkQueueAndInterrupt(state.sessionId);
|
|
56
58
|
// Check if the result contains tool calls
|
|
57
59
|
if (result.tool_calls && result.tool_calls.length > 0) {
|
|
58
60
|
// Execute the tools
|
|
@@ -66,6 +68,30 @@ export const addPromptNode = async ({ graph, node, llm, tools, emit, agent }: Ad
|
|
|
66
68
|
try {
|
|
67
69
|
// Invoke the LangChain tool directly
|
|
68
70
|
const toolResult = await matchedTool.invoke(toolCall);
|
|
71
|
+
console.log('after invoke?');
|
|
72
|
+
//check for queue after tool call
|
|
73
|
+
const systemMessageId = uuidv4();
|
|
74
|
+
|
|
75
|
+
await agent.interruptSessionManager.checkQueueAndInterrupt(state.sessionId, {
|
|
76
|
+
messages: [
|
|
77
|
+
result,
|
|
78
|
+
toolResult,
|
|
79
|
+
new SystemMessage({
|
|
80
|
+
id: systemMessageId,
|
|
81
|
+
content:
|
|
82
|
+
'you called tool when the user send a new message, Consider calling the function again after user message is processed',
|
|
83
|
+
}),
|
|
84
|
+
],
|
|
85
|
+
history: [
|
|
86
|
+
createHistoryStep<HistoryStep>(state.history, {
|
|
87
|
+
type: NodeType.TOOL,
|
|
88
|
+
nodeId: node.name,
|
|
89
|
+
nodeDisplayName: node.displayName,
|
|
90
|
+
raw: toolResult,
|
|
91
|
+
messageIds: [toolResult.id!, systemMessageId],
|
|
92
|
+
}),
|
|
93
|
+
],
|
|
94
|
+
});
|
|
69
95
|
const toolStateUpdate = extractToolStateResponse(toolResult);
|
|
70
96
|
// Properly merge memory and other state updates
|
|
71
97
|
stateUpdates = {
|
|
@@ -74,7 +100,8 @@ export const addPromptNode = async ({ graph, node, llm, tools, emit, agent }: Ad
|
|
|
74
100
|
memory: { ...(stateUpdates as any).memory, ...(toolStateUpdate as any).memory },
|
|
75
101
|
};
|
|
76
102
|
toolResults.push(toolResult);
|
|
77
|
-
} catch (error) {
|
|
103
|
+
} catch (error: any) {
|
|
104
|
+
if (error?.name === 'GraphInterrupt') throw error;
|
|
78
105
|
logger.error({ msg: `Error executing tool ${toolCall.name}:`, error });
|
|
79
106
|
const errorMessage = new ToolMessage({
|
|
80
107
|
content: JSON.stringify({ error: error instanceof Error ? error.message : String(error) }),
|
|
@@ -87,10 +114,30 @@ export const addPromptNode = async ({ graph, node, llm, tools, emit, agent }: Ad
|
|
|
87
114
|
}
|
|
88
115
|
}
|
|
89
116
|
|
|
117
|
+
// await agent.interruptSessionManager.checkQueueAndInterrupt(state.sessionId);
|
|
118
|
+
|
|
90
119
|
// Return the tool call message and tool results with state updates spread at top level
|
|
91
120
|
return {
|
|
92
121
|
...stateUpdates,
|
|
93
122
|
messages: [result, ...toolResults],
|
|
123
|
+
history: [
|
|
124
|
+
createHistoryStep<HistoryStep>(state.history, {
|
|
125
|
+
type: NodeType.TOOL,
|
|
126
|
+
nodeId: node.name,
|
|
127
|
+
nodeDisplayName: node.displayName,
|
|
128
|
+
raw: result,
|
|
129
|
+
messageIds: [result.id!],
|
|
130
|
+
}),
|
|
131
|
+
...toolResults.map((toolResult) =>
|
|
132
|
+
createHistoryStep<HistoryStep>(state.history, {
|
|
133
|
+
type: NodeType.TOOL,
|
|
134
|
+
nodeId: node.name,
|
|
135
|
+
nodeDisplayName: node.displayName,
|
|
136
|
+
raw: toolResult,
|
|
137
|
+
messageIds: [toolResult.id!],
|
|
138
|
+
}),
|
|
139
|
+
),
|
|
140
|
+
],
|
|
94
141
|
};
|
|
95
142
|
}
|
|
96
143
|
|
|
@@ -101,10 +148,9 @@ export const addPromptNode = async ({ graph, node, llm, tools, emit, agent }: Ad
|
|
|
101
148
|
});
|
|
102
149
|
console.log('AI Message', result.content);
|
|
103
150
|
}
|
|
104
|
-
|
|
105
151
|
return {
|
|
106
152
|
history: createHistoryStep<HistoryStep>(state.history, {
|
|
107
|
-
type:
|
|
153
|
+
type: NodeType.PROMPT_NODE,
|
|
108
154
|
nodeId: node.name,
|
|
109
155
|
nodeDisplayName: node.displayName,
|
|
110
156
|
raw: result.content,
|