@minded-ai/mindedjs 1.0.121 → 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.
- package/dist/agent.d.ts +4 -1
- package/dist/agent.d.ts.map +1 -1
- package/dist/agent.js +69 -9
- package/dist/agent.js.map +1 -1
- package/dist/browserTask/README.md +419 -0
- package/dist/browserTask/browserAgent.py +632 -0
- package/dist/browserTask/captcha_isolated.png +0 -0
- package/dist/browserTask/executeBrowserTask.ts +79 -0
- package/dist/browserTask/requirements.txt +8 -0
- package/dist/browserTask/setup.sh +144 -0
- package/dist/cli/index.js +14 -14
- package/dist/cli/index.js.map +1 -1
- package/dist/edges/edgeFactory.js +2 -2
- package/dist/edges/edgeFactory.js.map +1 -1
- package/dist/internalTools/retell.d.ts +12 -0
- package/dist/internalTools/retell.d.ts.map +1 -0
- package/dist/internalTools/retell.js +54 -0
- package/dist/internalTools/retell.js.map +1 -0
- package/dist/internalTools/sendPlaceholderMessage.d.ts +14 -0
- package/dist/internalTools/sendPlaceholderMessage.d.ts.map +1 -0
- package/dist/internalTools/sendPlaceholderMessage.js +61 -0
- package/dist/internalTools/sendPlaceholderMessage.js.map +1 -0
- package/dist/interrupts/BaseInterruptSessionManager.d.ts +52 -0
- package/dist/interrupts/BaseInterruptSessionManager.d.ts.map +1 -0
- package/dist/interrupts/BaseInterruptSessionManager.js +40 -0
- package/dist/interrupts/BaseInterruptSessionManager.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 +60 -0
- package/dist/interrupts/MemoryInterruptSessionManager.js.map +1 -0
- package/dist/interrupts/MindedInterruptSessionManager.d.ts +13 -0
- package/dist/interrupts/MindedInterruptSessionManager.d.ts.map +1 -0
- package/dist/interrupts/MindedInterruptSessionManager.js +151 -0
- package/dist/interrupts/MindedInterruptSessionManager.js.map +1 -0
- package/dist/interrupts/interruptSessionManagerFactory.d.ts +3 -0
- package/dist/interrupts/interruptSessionManagerFactory.d.ts.map +1 -0
- package/dist/interrupts/interruptSessionManagerFactory.js +46 -0
- package/dist/interrupts/interruptSessionManagerFactory.js.map +1 -0
- package/dist/nodes/addAppToolNode.js +1 -1
- package/dist/nodes/addAppToolNode.js.map +1 -1
- package/dist/nodes/addBrowserTaskNode.js +2 -2
- package/dist/nodes/addBrowserTaskNode.js.map +1 -1
- package/dist/nodes/addHumanInTheLoopNode.d.ts.map +1 -1
- package/dist/nodes/addHumanInTheLoopNode.js +2 -1
- package/dist/nodes/addHumanInTheLoopNode.js.map +1 -1
- package/dist/nodes/addJumpToNode.js +1 -1
- package/dist/nodes/addJumpToNode.js.map +1 -1
- package/dist/nodes/addPromptNode.d.ts.map +1 -1
- package/dist/nodes/addPromptNode.js +88 -7
- package/dist/nodes/addPromptNode.js.map +1 -1
- package/dist/nodes/addToolNode.d.ts.map +1 -1
- package/dist/nodes/addToolNode.js +5 -1
- package/dist/nodes/addToolNode.js.map +1 -1
- package/dist/nodes/addToolRunNode.d.ts.map +1 -1
- package/dist/nodes/addToolRunNode.js +1 -0
- package/dist/nodes/addToolRunNode.js.map +1 -1
- package/dist/nodes/compilePrompt.d.ts.map +1 -1
- package/dist/nodes/compilePrompt.js +1 -0
- package/dist/nodes/compilePrompt.js.map +1 -1
- package/dist/platform/mindedConnection.js +12 -12
- package/dist/platform/mindedConnection.js.map +1 -1
- package/dist/platform/mindedConnectionTypes.d.ts +151 -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.d.ts.map +1 -1
- package/dist/playbooks/playbooks.js +33 -12
- package/dist/playbooks/playbooks.js.map +1 -1
- package/dist/types/Agent.types.d.ts +2 -0
- package/dist/types/Agent.types.d.ts.map +1 -1
- 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/voice/voiceSession.d.ts.map +1 -1
- package/dist/voice/voiceSession.js +9 -1
- package/dist/voice/voiceSession.js.map +1 -1
- package/docs/low-code-editor/nodes.md +21 -12
- package/docs/low-code-editor/playbooks.md +50 -32
- package/package.json +1 -1
- package/src/agent.ts +93 -19
- package/src/cli/index.ts +14 -14
- package/src/edges/edgeFactory.ts +2 -2
- package/src/interrupts/BaseInterruptSessionManager.ts +96 -0
- package/src/interrupts/MemoryInterruptSessionManager.ts +63 -0
- package/src/interrupts/MindedInterruptSessionManager.ts +162 -0
- package/src/interrupts/interruptSessionManagerFactory.ts +20 -0
- package/src/nodes/addAppToolNode.ts +1 -1
- package/src/nodes/addBrowserTaskNode.ts +3 -3
- package/src/nodes/addHumanInTheLoopNode.ts +2 -1
- package/src/nodes/addJumpToNode.ts +1 -1
- package/src/nodes/addPromptNode.ts +95 -11
- package/src/nodes/addToolNode.ts +6 -2
- package/src/nodes/addToolRunNode.ts +1 -1
- package/src/nodes/compilePrompt.ts +2 -2
- package/src/platform/mindedConnection.ts +12 -12
- package/src/platform/mindedConnectionTypes.ts +187 -0
- package/src/playbooks/playbooks.ts +36 -13
- package/src/types/Agent.types.ts +2 -0
- package/src/types/LangGraph.types.ts +3 -1
- package/src/voice/voiceSession.ts +9 -1
package/src/agent.ts
CHANGED
|
@@ -36,6 +36,8 @@ import {
|
|
|
36
36
|
import { createLlmInstance } from './llm/createLlmInstance';
|
|
37
37
|
import { createCheckpointSaver } from './checkpointer/checkpointSaverFactory';
|
|
38
38
|
import { getConfig } from './platform/config';
|
|
39
|
+
import { InterruptSessionManager, InterruptType } from './interrupts/BaseInterruptSessionManager';
|
|
40
|
+
import { createInterruptSessionManager } from './interrupts/interruptSessionManagerFactory';
|
|
39
41
|
import { BaseMessage, HumanMessage } from '@langchain/core/messages';
|
|
40
42
|
import triggerTypeToDefaultMessage from './triggers/triggerTypeToDefaultMessage';
|
|
41
43
|
import appActionRunnerToolCreator from './internalTools/appActionRunnerTool';
|
|
@@ -55,6 +57,7 @@ type CreateAgentParams<Memory> = {
|
|
|
55
57
|
tools: Tool<any, Memory>[];
|
|
56
58
|
platformToken?: string;
|
|
57
59
|
memorySaver?: BaseCheckpointSaver;
|
|
60
|
+
interruptSessionManager?: InterruptSessionManager;
|
|
58
61
|
};
|
|
59
62
|
|
|
60
63
|
/**
|
|
@@ -87,6 +90,8 @@ export class Agent {
|
|
|
87
90
|
|
|
88
91
|
// Langgraph memory saver. In memory for local development, Custom for Platform
|
|
89
92
|
private checkpointer!: BaseCheckpointSaver;
|
|
93
|
+
// Interrupt session manager. In memory for local development, Custom for Platform
|
|
94
|
+
public interruptSessionManager!: InterruptSessionManager;
|
|
90
95
|
// Langgraph compiled graph
|
|
91
96
|
public compiledGraph!: CompiledGraph;
|
|
92
97
|
// Cache for secrets to avoid repeated API calls
|
|
@@ -157,7 +162,7 @@ export class Agent {
|
|
|
157
162
|
|
|
158
163
|
private async init(params: CreateAgentParams<z.infer<typeof this.memorySchema>>): Promise<void> {
|
|
159
164
|
try {
|
|
160
|
-
const { config, tools, memorySaver } = params;
|
|
165
|
+
const { config, tools, memorySaver, interruptSessionManager } = params;
|
|
161
166
|
const { runLocally } = getConfig();
|
|
162
167
|
if (!runLocally) {
|
|
163
168
|
await mindedConnection.start();
|
|
@@ -196,6 +201,7 @@ export class Agent {
|
|
|
196
201
|
const libraryActionsRunnerTools = this.initLibraryActionsRunnerTools();
|
|
197
202
|
this.tools = [...tools, ...appActionsRunnerTools, ...libraryActionsRunnerTools];
|
|
198
203
|
this.checkpointer = memorySaver || createCheckpointSaver();
|
|
204
|
+
this.interruptSessionManager = interruptSessionManager || createInterruptSessionManager();
|
|
199
205
|
|
|
200
206
|
// call here methods that needs environment variables to be loaded
|
|
201
207
|
this.llm = createLlmInstance(config.llm);
|
|
@@ -387,6 +393,10 @@ export class Agent {
|
|
|
387
393
|
// Try to parse empty object through the schema to apply default values
|
|
388
394
|
// If parsing fails due to required fields without defaults, use empty object as fallback
|
|
389
395
|
const parseResult = this.memorySchema.safeParse({});
|
|
396
|
+
if (!parseResult.success) {
|
|
397
|
+
logger.error({ msg: 'Failed to parse memory schema', error: parseResult.error });
|
|
398
|
+
throw new Error('Failed to parse memory schema');
|
|
399
|
+
}
|
|
390
400
|
const initialMemory = parseResult.success ? parseResult.data : {};
|
|
391
401
|
|
|
392
402
|
const initialState = {
|
|
@@ -426,14 +436,14 @@ export class Agent {
|
|
|
426
436
|
|
|
427
437
|
return appName
|
|
428
438
|
? createHistoryStep<AppTriggerHistoryStep>(currentHistory, {
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
439
|
+
...baseStep,
|
|
440
|
+
type: NodeType.TRIGGER,
|
|
441
|
+
appName,
|
|
442
|
+
})
|
|
433
443
|
: createHistoryStep<TriggerHistoryStep>(currentHistory, {
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
444
|
+
...baseStep,
|
|
445
|
+
type: NodeType.TRIGGER,
|
|
446
|
+
});
|
|
437
447
|
}
|
|
438
448
|
|
|
439
449
|
/**
|
|
@@ -464,10 +474,24 @@ export class Agent {
|
|
|
464
474
|
* });
|
|
465
475
|
* ```
|
|
466
476
|
*/
|
|
467
|
-
public async invoke({ triggerBody, triggerName, sessionId, appName }: AgentInvokeParams) {
|
|
477
|
+
public async invoke({ triggerBody, triggerName, sessionId, appName, bypassSessionCheck }: AgentInvokeParams): Promise<any> {
|
|
468
478
|
sessionId = sessionId ?? uuidv4();
|
|
469
479
|
try {
|
|
470
480
|
await this.waitForInitialization();
|
|
481
|
+
|
|
482
|
+
// Try to acquire lock atomically (unless bypassing session check)
|
|
483
|
+
if (!bypassSessionCheck && !(await this.interruptSessionManager.lock(sessionId))) {
|
|
484
|
+
// Could not acquire lock, session is being processed - enqueue the message
|
|
485
|
+
logger.info({ msg: 'Enqueuing message', sessionId, triggerBody, triggerName, appName });
|
|
486
|
+
await this.interruptSessionManager.enqueueMessage(sessionId, {
|
|
487
|
+
triggerBody,
|
|
488
|
+
triggerName,
|
|
489
|
+
appName,
|
|
490
|
+
});
|
|
491
|
+
return;
|
|
492
|
+
}
|
|
493
|
+
// Session lock acquired, proceed with processing
|
|
494
|
+
|
|
471
495
|
let messages: Array<BaseMessage> = [];
|
|
472
496
|
let memoryUpdate = {};
|
|
473
497
|
let sessionType: SessionType = SessionType.TEXT;
|
|
@@ -503,7 +527,8 @@ export class Agent {
|
|
|
503
527
|
const handlerResult = results.find((r) => r !== undefined);
|
|
504
528
|
if (handlerResult) {
|
|
505
529
|
if (!handlerResult.isQualified) {
|
|
506
|
-
logger.
|
|
530
|
+
logger.info({ message: '[Trigger] Disqualified', triggerName, triggerBody, sessionId });
|
|
531
|
+
await this.interruptSessionManager.release(sessionId);
|
|
507
532
|
return;
|
|
508
533
|
}
|
|
509
534
|
memoryUpdate = handlerResult.memory || {};
|
|
@@ -536,13 +561,37 @@ export class Agent {
|
|
|
536
561
|
|
|
537
562
|
let res;
|
|
538
563
|
// Resume interruption
|
|
539
|
-
if (state.tasks?.[
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
564
|
+
if (state.tasks?.[state.tasks.length - 1]?.interrupts?.length > 0) {
|
|
565
|
+
const lastTask = state.tasks[state.tasks.length - 1];
|
|
566
|
+
const interrupt = lastTask.interrupts[0];
|
|
567
|
+
const interruptValue = (interrupt as any).value;
|
|
568
|
+
|
|
569
|
+
if (interruptValue?.type === InterruptType.HUMAN_IN_THE_LOOP) {
|
|
570
|
+
// For HUMAN_IN_THE_LOOP, use resume with the full object
|
|
571
|
+
res = await this.compiledGraph.invoke(
|
|
572
|
+
new Command({
|
|
573
|
+
resume: { memory: memoryUpdate, messages, history: historyStep, sessionId, sessionType, overrideStartFromNodeId: null },
|
|
574
|
+
}),
|
|
575
|
+
langraphConfig,
|
|
576
|
+
);
|
|
577
|
+
} else if (interruptValue?.type === InterruptType.NEW_TRIGGERS) {
|
|
578
|
+
// For NEW_TRIGGERS, check if there's an updateStateObject to apply first
|
|
579
|
+
const finalState = { memory: memoryUpdate, messages, history: [historyStep], sessionId, sessionType };
|
|
580
|
+
if (interruptValue.updateStateObject) {
|
|
581
|
+
finalState.messages = [...interruptValue.updateStateObject.messages, ...messages];
|
|
582
|
+
finalState.history = [...(interruptValue.updateStateObject.history || []), historyStep];
|
|
583
|
+
//add handlers for other state fields as needed
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
// Then use update with the full object and empty resume
|
|
587
|
+
res = await this.compiledGraph.invoke(
|
|
588
|
+
new Command({
|
|
589
|
+
update: finalState,
|
|
590
|
+
resume: '',
|
|
591
|
+
}),
|
|
592
|
+
langraphConfig,
|
|
593
|
+
);
|
|
594
|
+
}
|
|
546
595
|
} else if (state.values.overrideStartFromNodeId) {
|
|
547
596
|
res = await this.compiledGraph.invoke(
|
|
548
597
|
new Command({
|
|
@@ -564,6 +613,26 @@ export class Agent {
|
|
|
564
613
|
langraphConfig,
|
|
565
614
|
);
|
|
566
615
|
}
|
|
616
|
+
const nextMessage = await this.interruptSessionManager.dequeue(sessionId);
|
|
617
|
+
if (nextMessage) {
|
|
618
|
+
// Dequeue the first message and process it recursively
|
|
619
|
+
// Recursively process the next message with bypass flag
|
|
620
|
+
const invokeParams: AgentInvokeParams = {
|
|
621
|
+
triggerBody: nextMessage.triggerBody,
|
|
622
|
+
triggerName: nextMessage.triggerName,
|
|
623
|
+
sessionId: sessionId,
|
|
624
|
+
bypassSessionCheck: true,
|
|
625
|
+
};
|
|
626
|
+
if (nextMessage.appName) {
|
|
627
|
+
invokeParams.appName = nextMessage.appName;
|
|
628
|
+
}
|
|
629
|
+
logger.info({ msg: 'Invoking next message in the queue', invokeParams });
|
|
630
|
+
return await this.invoke(invokeParams);
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
// Release the session lock
|
|
634
|
+
await this.interruptSessionManager.release(sessionId);
|
|
635
|
+
|
|
567
636
|
return res;
|
|
568
637
|
} catch (error: any) {
|
|
569
638
|
logger.error({
|
|
@@ -572,6 +641,10 @@ export class Agent {
|
|
|
572
641
|
stack: error.stack,
|
|
573
642
|
sessionId,
|
|
574
643
|
});
|
|
644
|
+
|
|
645
|
+
// Release the session lock on error
|
|
646
|
+
await this.interruptSessionManager.release(sessionId);
|
|
647
|
+
|
|
575
648
|
const state = await this.compiledGraph.getState(this.getLangraphConfig(sessionId));
|
|
576
649
|
this.emit(AgentEvents.ERROR, { error: error instanceof Error ? error : new Error(JSON.stringify(error)), state: state.values });
|
|
577
650
|
throw error;
|
|
@@ -763,7 +836,7 @@ export class Agent {
|
|
|
763
836
|
// Hangup / end session handler
|
|
764
837
|
connection.on(mindedConnectionSocketMessageType.VOICE_SESSION_END, (message) => {
|
|
765
838
|
const hangup = message as BaseVoiceMessage;
|
|
766
|
-
logger.debug({
|
|
839
|
+
logger.debug({ msg: '[Voice] Dashboard eneded voice session', sessionId: hangup.sessionId });
|
|
767
840
|
const voiceSession = this.voiceSessions.get(hangup.sessionId);
|
|
768
841
|
if (voiceSession) {
|
|
769
842
|
voiceSession.hangup();
|
|
@@ -812,7 +885,8 @@ export class Agent {
|
|
|
812
885
|
voiceId: voiceTrigger.voiceId,
|
|
813
886
|
});
|
|
814
887
|
await voiceSession.init();
|
|
815
|
-
logger.debug({
|
|
888
|
+
logger.debug({ msg: '[Voice] Voice session initialized', sessionId: params.sessionId });
|
|
889
|
+
|
|
816
890
|
this.voiceSessions.set(params.sessionId, voiceSession);
|
|
817
891
|
|
|
818
892
|
// Emit voice session start event
|
package/src/cli/index.ts
CHANGED
|
@@ -19,7 +19,7 @@ function setToken(token: string): void {
|
|
|
19
19
|
if (!fs.existsSync(envPath)) {
|
|
20
20
|
// Create .env file with the token
|
|
21
21
|
fs.writeFileSync(envPath, tokenLine + '\n');
|
|
22
|
-
logger.info(`Created ${envPath} and added token`);
|
|
22
|
+
logger.info({ msg: `Created ${envPath} and added token` });
|
|
23
23
|
} else {
|
|
24
24
|
// Read existing .env file
|
|
25
25
|
const envContent = fs.readFileSync(envPath, 'utf8');
|
|
@@ -29,12 +29,12 @@ function setToken(token: string): void {
|
|
|
29
29
|
// Replace existing token
|
|
30
30
|
const updatedContent = envContent.replace(/MINDED_CONNECTION_TOKEN=.*/, tokenLine);
|
|
31
31
|
fs.writeFileSync(envPath, updatedContent);
|
|
32
|
-
logger.info(`Updated MINDED_CONNECTION_TOKEN in ${envPath}`);
|
|
32
|
+
logger.info({ msg: `Updated MINDED_CONNECTION_TOKEN in ${envPath}` });
|
|
33
33
|
} else {
|
|
34
34
|
// Append token to existing file
|
|
35
35
|
const newContent = envContent.endsWith('\n') ? envContent + tokenLine + '\n' : envContent + '\n' + tokenLine + '\n';
|
|
36
36
|
fs.writeFileSync(envPath, newContent);
|
|
37
|
-
logger.info(`Added MINDED_CONNECTION_TOKEN to ${envPath}`);
|
|
37
|
+
logger.info({ msg: `Added MINDED_CONNECTION_TOKEN to ${envPath}` });
|
|
38
38
|
}
|
|
39
39
|
}
|
|
40
40
|
}
|
|
@@ -44,7 +44,7 @@ function generateLambdaHandler(): void {
|
|
|
44
44
|
|
|
45
45
|
// Check if minded.json exists
|
|
46
46
|
if (!fs.existsSync(mindedConfigPath)) {
|
|
47
|
-
logger.error('minded.json not found in the current directory');
|
|
47
|
+
logger.error({ msg: 'minded.json not found in the current directory' });
|
|
48
48
|
process.exit(1);
|
|
49
49
|
}
|
|
50
50
|
|
|
@@ -54,14 +54,14 @@ function generateLambdaHandler(): void {
|
|
|
54
54
|
const configContent = fs.readFileSync(mindedConfigPath, 'utf8');
|
|
55
55
|
mindedConfig = JSON.parse(configContent);
|
|
56
56
|
} catch (error) {
|
|
57
|
-
logger.error('Failed to read or parse minded.json:', error);
|
|
57
|
+
logger.error({ msg: 'Failed to read or parse minded.json:', error });
|
|
58
58
|
process.exit(1);
|
|
59
59
|
}
|
|
60
60
|
|
|
61
61
|
// Get the agent path
|
|
62
62
|
const agentPath = mindedConfig.agent;
|
|
63
63
|
if (!agentPath) {
|
|
64
|
-
logger.error('No agent path found in minded.json');
|
|
64
|
+
logger.error({ msg: 'No agent path found in minded.json' });
|
|
65
65
|
process.exit(1);
|
|
66
66
|
}
|
|
67
67
|
|
|
@@ -91,7 +91,7 @@ function generateLambdaHandler(): void {
|
|
|
91
91
|
}
|
|
92
92
|
|
|
93
93
|
if (!templateContent) {
|
|
94
|
-
logger.error('Could not find Lambda handler template file');
|
|
94
|
+
logger.error({ msg: 'Could not find Lambda handler template file' });
|
|
95
95
|
process.exit(1);
|
|
96
96
|
}
|
|
97
97
|
|
|
@@ -102,10 +102,10 @@ function generateLambdaHandler(): void {
|
|
|
102
102
|
// Write the Lambda handler to index.ts at the root
|
|
103
103
|
const outputPath = path.join(process.cwd(), 'index.ts');
|
|
104
104
|
fs.writeFileSync(outputPath, templateContent);
|
|
105
|
-
logger.info(`Generated Lambda handler at ${outputPath}`);
|
|
105
|
+
logger.info({ msg: `Generated Lambda handler at ${outputPath}` });
|
|
106
106
|
|
|
107
107
|
// Compile the index.ts file using TypeScript
|
|
108
|
-
logger.info('Compiling the Lambda handler...');
|
|
108
|
+
logger.info({ msg: 'Compiling the Lambda handler...' });
|
|
109
109
|
|
|
110
110
|
try {
|
|
111
111
|
// Read tsconfig.json to get the outDir
|
|
@@ -138,12 +138,12 @@ function generateLambdaHandler(): void {
|
|
|
138
138
|
cwd: process.cwd(),
|
|
139
139
|
});
|
|
140
140
|
|
|
141
|
-
logger.info(`Successfully compiled Lambda handler to ${outDir}/index.js`);
|
|
141
|
+
logger.info({ msg: `Successfully compiled Lambda handler to ${outDir}/index.js` });
|
|
142
142
|
} catch (compileError) {
|
|
143
|
-
logger.error({
|
|
143
|
+
logger.error({ msg: 'Failed to compile Lambda handler', error: compileError });
|
|
144
144
|
}
|
|
145
145
|
} catch (error) {
|
|
146
|
-
logger.error({
|
|
146
|
+
logger.error({ msg: 'Failed to generate Lambda handler', error: error });
|
|
147
147
|
process.exit(1);
|
|
148
148
|
}
|
|
149
149
|
}
|
|
@@ -155,14 +155,14 @@ function main() {
|
|
|
155
155
|
if (command === 'token') {
|
|
156
156
|
const token = args[1];
|
|
157
157
|
if (!token) {
|
|
158
|
-
logger.error('Please provide a token');
|
|
158
|
+
logger.error({ msg: 'Please provide a token' });
|
|
159
159
|
process.exit(1);
|
|
160
160
|
}
|
|
161
161
|
setToken(token);
|
|
162
162
|
} else if (command === 'generate-lambda-ts-handler') {
|
|
163
163
|
generateLambdaHandler();
|
|
164
164
|
} else {
|
|
165
|
-
logger.error('Unknown command. Available commands: token, generate-lambda-ts-handler');
|
|
165
|
+
logger.error({ msg: 'Unknown command. Available commands: token, generate-lambda-ts-handler' });
|
|
166
166
|
process.exit(1);
|
|
167
167
|
}
|
|
168
168
|
}
|
package/src/edges/edgeFactory.ts
CHANGED
|
@@ -60,7 +60,7 @@ export const edgeFactory = ({
|
|
|
60
60
|
if (result) {
|
|
61
61
|
return result;
|
|
62
62
|
} else {
|
|
63
|
-
logger.debug('No logical conditions matched, continuing to prompt conditions');
|
|
63
|
+
logger.debug({ msg: 'No logical conditions matched, continuing to prompt conditions' });
|
|
64
64
|
}
|
|
65
65
|
}
|
|
66
66
|
|
|
@@ -85,7 +85,7 @@ export const edgeFactory = ({
|
|
|
85
85
|
|
|
86
86
|
// Fallback: stay at current source node
|
|
87
87
|
const source = originalNode?.name || sourceNode;
|
|
88
|
-
logger.info(`No conditions matched, returning to source: ${source}`);
|
|
88
|
+
logger.info({ msg: `No conditions matched, returning to source: ${source}` });
|
|
89
89
|
return source;
|
|
90
90
|
};
|
|
91
91
|
};
|
|
@@ -0,0 +1,96 @@
|
|
|
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 const QUEUE_INTERRUPT_DETECTED_BEFORE_SPEECH = 'QUEUE_INTERRUPT_DETECTED_BEFORE_SPEECH';
|
|
11
|
+
|
|
12
|
+
export interface QueuedMessage {
|
|
13
|
+
triggerBody: any;
|
|
14
|
+
triggerName: string;
|
|
15
|
+
appName?: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// export interface UpdateStateObject {
|
|
19
|
+
// memory: any;
|
|
20
|
+
// messages: any[];
|
|
21
|
+
// history: any[];
|
|
22
|
+
// sessionId: string;
|
|
23
|
+
// sessionType: SessionType;
|
|
24
|
+
// }
|
|
25
|
+
export interface InterruptPayload {
|
|
26
|
+
type: InterruptType.NEW_TRIGGERS;
|
|
27
|
+
updateStateObject?: Partial<State>;
|
|
28
|
+
id?: string;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface InterruptSessionManager {
|
|
32
|
+
isProcessed(sessionId: string): boolean | Promise<boolean>;
|
|
33
|
+
/**
|
|
34
|
+
* Atomically attempts to acquire a lock for the given session.
|
|
35
|
+
* This method should only succeed if the session is not already being processed.
|
|
36
|
+
*
|
|
37
|
+
* @param sessionId - The session ID to lock
|
|
38
|
+
* @returns true if the lock was successfully acquired, false if the session is already locked
|
|
39
|
+
*/
|
|
40
|
+
lock(sessionId: string): boolean | Promise<boolean>;
|
|
41
|
+
release(sessionId: string): void | Promise<void>;
|
|
42
|
+
enqueueMessage(sessionId: string, message: QueuedMessage): void | Promise<void>;
|
|
43
|
+
dequeueAll(sessionId: string): QueuedMessage[] | Promise<QueuedMessage[]>;
|
|
44
|
+
dequeue(sessionId: string): QueuedMessage | null | Promise<QueuedMessage | null>;
|
|
45
|
+
checkQueueAndInterrupt(sessionId: string, updateStateObject?: Partial<State>): Promise<boolean>;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export abstract class BaseInterruptSessionManager implements InterruptSessionManager {
|
|
49
|
+
// Abstract methods that subclasses must implement for queue management
|
|
50
|
+
abstract isProcessed(sessionId: string): boolean | Promise<boolean>;
|
|
51
|
+
/**
|
|
52
|
+
* Atomically attempts to acquire a lock for the given session.
|
|
53
|
+
* This method should only succeed if the session is not already being processed.
|
|
54
|
+
* Implementations must ensure this operation is atomic to prevent race conditions.
|
|
55
|
+
*
|
|
56
|
+
* @param sessionId - The session ID to lock
|
|
57
|
+
* @returns true if the lock was successfully acquired, false if the session is already locked
|
|
58
|
+
*/
|
|
59
|
+
abstract lock(sessionId: string): boolean | Promise<boolean>;
|
|
60
|
+
abstract release(sessionId: string): void | Promise<void>;
|
|
61
|
+
abstract enqueueMessage(sessionId: string, message: QueuedMessage): void | Promise<void>;
|
|
62
|
+
abstract dequeueAll(sessionId: string): QueuedMessage[] | Promise<QueuedMessage[]>;
|
|
63
|
+
abstract dequeue(sessionId: string): QueuedMessage | null | Promise<QueuedMessage | null>;
|
|
64
|
+
|
|
65
|
+
// Abstract method to check if queue has messages - this is the only queue-specific part
|
|
66
|
+
protected abstract hasQueuedMessages(sessionId: string): boolean | Promise<boolean>;
|
|
67
|
+
protected abstract getQueuedMessages(sessionId: string): QueuedMessage[] | Promise<QueuedMessage[]>;
|
|
68
|
+
|
|
69
|
+
// Common implementation of checkQueueAndInterrupt
|
|
70
|
+
async checkQueueAndInterrupt(sessionId: string, updateStateObject?: Partial<State>): Promise<boolean> {
|
|
71
|
+
if (await this.hasQueuedMessages(sessionId)) {
|
|
72
|
+
logger.trace({ msg: 'graph has queued messagess, interrupting graph', sessionId });
|
|
73
|
+
|
|
74
|
+
// Interrupt the graph with NEW_TRIGGERS flag and optional updateStateObject
|
|
75
|
+
const interruptPayload: InterruptPayload = { type: InterruptType.NEW_TRIGGERS };
|
|
76
|
+
if (updateStateObject) {
|
|
77
|
+
interruptPayload.updateStateObject = {
|
|
78
|
+
messages: updateStateObject.messages,
|
|
79
|
+
memory: updateStateObject.memory,
|
|
80
|
+
history: updateStateObject.history,
|
|
81
|
+
sessionId: updateStateObject.sessionId,
|
|
82
|
+
sessionType: updateStateObject.sessionType,
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
throw new GraphInterrupt([
|
|
87
|
+
{
|
|
88
|
+
value: interruptPayload,
|
|
89
|
+
when: 'during',
|
|
90
|
+
resumable: true,
|
|
91
|
+
},
|
|
92
|
+
]);
|
|
93
|
+
}
|
|
94
|
+
return false;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { BaseInterruptSessionManager, QueuedMessage, QUEUE_INTERRUPT_DETECTED_BEFORE_SPEECH } from './BaseInterruptSessionManager';
|
|
2
|
+
|
|
3
|
+
export class MemoryInterruptSessionManager extends BaseInterruptSessionManager {
|
|
4
|
+
private sessionProcessing: Map<string, boolean> = new Map();
|
|
5
|
+
private sessionMessageQueues: Map<string, QueuedMessage[]> = new Map();
|
|
6
|
+
|
|
7
|
+
isProcessed(sessionId: string): boolean {
|
|
8
|
+
return this.sessionProcessing.get(sessionId) || false;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
lock(sessionId: string): boolean {
|
|
12
|
+
const isCurrentlyProcessed = this.sessionProcessing.get(sessionId) || false;
|
|
13
|
+
if (isCurrentlyProcessed) {
|
|
14
|
+
return false; // Could not acquire lock
|
|
15
|
+
}
|
|
16
|
+
this.sessionProcessing.set(sessionId, true);
|
|
17
|
+
return true; // Successfully acquired lock
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
release(sessionId: string): void {
|
|
21
|
+
this.sessionProcessing.delete(sessionId);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
enqueueMessage(sessionId: string, message: QueuedMessage): void {
|
|
25
|
+
if (!this.sessionMessageQueues.has(sessionId)) {
|
|
26
|
+
this.sessionMessageQueues.set(sessionId, []);
|
|
27
|
+
}
|
|
28
|
+
this.sessionMessageQueues.get(sessionId)!.push(message);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
dequeueAll(sessionId: string): QueuedMessage[] {
|
|
32
|
+
const messages = this.sessionMessageQueues.get(sessionId) || [];
|
|
33
|
+
const result = [...messages];
|
|
34
|
+
this.sessionMessageQueues.set(sessionId, []);
|
|
35
|
+
return result;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
dequeue(sessionId: string): QueuedMessage | null {
|
|
39
|
+
const messages = this.sessionMessageQueues.get(sessionId) || [];
|
|
40
|
+
if (messages.length === 0) {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
const firstMessage = messages.shift()!;
|
|
44
|
+
this.sessionMessageQueues.set(sessionId, messages);
|
|
45
|
+
|
|
46
|
+
// If the dequeued message has the special trigger name, recursively dequeue the next one
|
|
47
|
+
if (firstMessage.triggerName === QUEUE_INTERRUPT_DETECTED_BEFORE_SPEECH) {
|
|
48
|
+
return this.dequeue(sessionId);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return firstMessage;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Implementation of abstract methods from BaseInterruptSessionManager
|
|
55
|
+
protected hasQueuedMessages(sessionId: string): boolean {
|
|
56
|
+
const queue = this.sessionMessageQueues.get(sessionId) || [];
|
|
57
|
+
return queue.length > 0;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
protected getQueuedMessages(sessionId: string): QueuedMessage[] {
|
|
61
|
+
return this.sessionMessageQueues.get(sessionId) || [];
|
|
62
|
+
}
|
|
63
|
+
}
|
|
@@ -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
|
+
}
|
|
@@ -69,7 +69,7 @@ export const addAppToolNode = async ({
|
|
|
69
69
|
User instructions for choosing tool parameters are:
|
|
70
70
|
${node.prompt ? `${node.prompt}` : 'no instructions set by the user'}`;
|
|
71
71
|
|
|
72
|
-
const compiledPrompt = compilePrompt(message, { state, currentTime: new Date().toISOString() });
|
|
72
|
+
const compiledPrompt = compilePrompt(message, { memory: state.memory, system: { currentTime: new Date().toISOString() } });
|
|
73
73
|
|
|
74
74
|
const AIToolCallMessage = await llm
|
|
75
75
|
.bindTools([tool], {
|