illuma-agents 1.0.23 → 1.0.25
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/cjs/graphs/Graph.cjs +3 -3
- package/dist/cjs/graphs/Graph.cjs.map +1 -1
- package/dist/cjs/main.cjs +12 -34
- package/dist/cjs/main.cjs.map +1 -1
- package/dist/cjs/run.cjs +3 -209
- package/dist/cjs/run.cjs.map +1 -1
- package/dist/esm/graphs/Graph.mjs +3 -3
- package/dist/esm/graphs/Graph.mjs.map +1 -1
- package/dist/esm/main.mjs +5 -4
- package/dist/esm/main.mjs.map +1 -1
- package/dist/esm/run.mjs +4 -208
- package/dist/esm/run.mjs.map +1 -1
- package/dist/types/index.d.ts +4 -3
- package/dist/types/run.d.ts +0 -48
- package/dist/types/types/run.d.ts +0 -8
- package/package.json +1 -1
- package/src/graphs/Graph.ts +3 -3
- package/src/index.ts +5 -4
- package/src/run.ts +3 -260
- package/src/types/run.ts +0 -8
- package/dist/cjs/tools/BrowserInterruptTools.cjs +0 -431
- package/dist/cjs/tools/BrowserInterruptTools.cjs.map +0 -1
- package/dist/cjs/tools/BrowserTools.cjs +0 -533
- package/dist/cjs/tools/BrowserTools.cjs.map +0 -1
- package/dist/esm/tools/BrowserInterruptTools.mjs +0 -415
- package/dist/esm/tools/BrowserInterruptTools.mjs.map +0 -1
- package/dist/esm/tools/BrowserTools.mjs +0 -517
- package/dist/esm/tools/BrowserTools.mjs.map +0 -1
- package/dist/types/tools/BrowserInterruptTools.d.ts +0 -282
- package/dist/types/tools/BrowserTools.d.ts +0 -259
- package/src/specs/browser-interrupt-tools.test.ts +0 -235
- package/src/tools/BrowserInterruptTools.ts +0 -571
- package/src/tools/BrowserTools.test.ts +0 -563
- package/src/tools/BrowserTools.ts +0 -650
package/src/run.ts
CHANGED
|
@@ -4,8 +4,6 @@ import { CallbackHandler } from '@langfuse/langchain';
|
|
|
4
4
|
import { PromptTemplate } from '@langchain/core/prompts';
|
|
5
5
|
import { RunnableLambda } from '@langchain/core/runnables';
|
|
6
6
|
import { AzureChatOpenAI, ChatOpenAI } from '@langchain/openai';
|
|
7
|
-
import { MemorySaver } from '@langchain/langgraph-checkpoint';
|
|
8
|
-
import { Command } from '@langchain/langgraph';
|
|
9
7
|
import type {
|
|
10
8
|
MessageContentComplex,
|
|
11
9
|
BaseMessage,
|
|
@@ -24,7 +22,6 @@ import { StandardGraph } from '@/graphs/Graph';
|
|
|
24
22
|
import { HandlerRegistry } from '@/events';
|
|
25
23
|
import { isOpenAILike } from '@/utils/llm';
|
|
26
24
|
import { isPresent } from '@/utils/misc';
|
|
27
|
-
import type { BrowserInterrupt, BrowserActionResult } from '@/tools/BrowserInterruptTools';
|
|
28
25
|
|
|
29
26
|
export const defaultOmitOptions = new Set([
|
|
30
27
|
'stream',
|
|
@@ -39,35 +36,11 @@ export const defaultOmitOptions = new Set([
|
|
|
39
36
|
'additionalModelRequestFields',
|
|
40
37
|
]);
|
|
41
38
|
|
|
42
|
-
/** Global checkpointer store for browser mode (keyed by runId) */
|
|
43
|
-
const browserCheckpointers = new Map<string, MemorySaver>();
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* Get or create a checkpointer for browser mode
|
|
47
|
-
*/
|
|
48
|
-
export function getBrowserCheckpointer(runId: string): MemorySaver {
|
|
49
|
-
let checkpointer = browserCheckpointers.get(runId);
|
|
50
|
-
if (!checkpointer) {
|
|
51
|
-
checkpointer = new MemorySaver();
|
|
52
|
-
browserCheckpointers.set(runId, checkpointer);
|
|
53
|
-
}
|
|
54
|
-
return checkpointer;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* Clean up a browser checkpointer when done
|
|
59
|
-
*/
|
|
60
|
-
export function cleanupBrowserCheckpointer(runId: string): void {
|
|
61
|
-
browserCheckpointers.delete(runId);
|
|
62
|
-
}
|
|
63
|
-
|
|
64
39
|
export class Run<_T extends t.BaseGraphState> {
|
|
65
40
|
id: string;
|
|
66
41
|
private tokenCounter?: t.TokenCounter;
|
|
67
42
|
private handlerRegistry?: HandlerRegistry;
|
|
68
43
|
private indexTokenCountMap?: Record<string, number>;
|
|
69
|
-
/** Whether this run is in browser extension mode */
|
|
70
|
-
browserMode: boolean = false;
|
|
71
44
|
graphRunnable?: t.CompiledStateWorkflow;
|
|
72
45
|
Graph: StandardGraph | MultiAgentGraph | undefined;
|
|
73
46
|
returnContent: boolean = false;
|
|
@@ -81,7 +54,6 @@ export class Run<_T extends t.BaseGraphState> {
|
|
|
81
54
|
this.id = runId;
|
|
82
55
|
this.tokenCounter = config.tokenCounter;
|
|
83
56
|
this.indexTokenCountMap = config.indexTokenCountMap;
|
|
84
|
-
this.browserMode = config.browserMode ?? false;
|
|
85
57
|
|
|
86
58
|
const handlerRegistry = new HandlerRegistry();
|
|
87
59
|
|
|
@@ -101,24 +73,16 @@ export class Run<_T extends t.BaseGraphState> {
|
|
|
101
73
|
|
|
102
74
|
/** Handle different graph types */
|
|
103
75
|
if (config.graphConfig.type === 'multi-agent') {
|
|
104
|
-
// For multi-agent, browser mode checkpointer support not yet implemented
|
|
105
76
|
this.graphRunnable = this.createMultiAgentGraph(config.graphConfig);
|
|
106
77
|
if (this.Graph) {
|
|
107
78
|
this.Graph.handlerRegistry = handlerRegistry;
|
|
108
79
|
}
|
|
109
80
|
} else {
|
|
110
81
|
/** Default to legacy graph for 'standard' or undefined type */
|
|
111
|
-
|
|
112
|
-
const graphConfig = { ...config.graphConfig };
|
|
113
|
-
if (this.browserMode) {
|
|
114
|
-
const checkpointer = getBrowserCheckpointer(runId);
|
|
115
|
-
graphConfig.compileOptions = {
|
|
116
|
-
...graphConfig.compileOptions,
|
|
117
|
-
checkpointer,
|
|
118
|
-
};
|
|
119
|
-
}
|
|
120
|
-
this.graphRunnable = this.createLegacyGraph(graphConfig);
|
|
82
|
+
this.graphRunnable = this.createLegacyGraph(config.graphConfig);
|
|
121
83
|
if (this.Graph) {
|
|
84
|
+
this.Graph.compileOptions =
|
|
85
|
+
config.graphConfig.compileOptions ?? this.Graph.compileOptions;
|
|
122
86
|
this.Graph.handlerRegistry = handlerRegistry;
|
|
123
87
|
}
|
|
124
88
|
}
|
|
@@ -489,225 +453,4 @@ export class Run<_T extends t.BaseGraphState> {
|
|
|
489
453
|
);
|
|
490
454
|
}
|
|
491
455
|
}
|
|
492
|
-
|
|
493
|
-
/**
|
|
494
|
-
* Process stream with browser interrupt support.
|
|
495
|
-
* Uses streamEvents() like processStream to emit SSE events,
|
|
496
|
-
* then checks for interrupts after stream completes.
|
|
497
|
-
*/
|
|
498
|
-
async *processBrowserStream(
|
|
499
|
-
inputs: t.IState,
|
|
500
|
-
config: Partial<RunnableConfig> & { version: 'v1' | 'v2'; run_id?: string },
|
|
501
|
-
streamOptions?: t.EventStreamOptions
|
|
502
|
-
): AsyncGenerator<
|
|
503
|
-
| { type: 'event'; data: unknown }
|
|
504
|
-
| { type: 'interrupt'; data: BrowserInterrupt }
|
|
505
|
-
| { type: 'done'; data?: MessageContentComplex[] }
|
|
506
|
-
> {
|
|
507
|
-
if (this.graphRunnable == null) {
|
|
508
|
-
throw new Error(
|
|
509
|
-
'Run not initialized. Make sure to use Run.create() to instantiate the Run.'
|
|
510
|
-
);
|
|
511
|
-
}
|
|
512
|
-
if (!this.Graph) {
|
|
513
|
-
throw new Error(
|
|
514
|
-
'Graph not initialized. Make sure to use Run.create() to instantiate the Run.'
|
|
515
|
-
);
|
|
516
|
-
}
|
|
517
|
-
|
|
518
|
-
this.Graph.resetValues(streamOptions?.keepContent);
|
|
519
|
-
|
|
520
|
-
if (!this.id) {
|
|
521
|
-
throw new Error('Run ID not provided');
|
|
522
|
-
}
|
|
523
|
-
|
|
524
|
-
config.run_id = this.id;
|
|
525
|
-
// Set up thread_id for checkpointing (required for interrupt/resume)
|
|
526
|
-
config.configurable = Object.assign(config.configurable ?? {}, {
|
|
527
|
-
run_id: this.id,
|
|
528
|
-
thread_id: this.id, // Use run ID as thread ID for browser sessions
|
|
529
|
-
});
|
|
530
|
-
|
|
531
|
-
// Set up callbacks for SSE event emission (same as processStream)
|
|
532
|
-
const customEventCallback = this.createCustomEventCallback();
|
|
533
|
-
|
|
534
|
-
const baseCallbacks = (config.callbacks as t.ProvidedCallbacks) ?? [];
|
|
535
|
-
const streamCallbacks = streamOptions?.callbacks
|
|
536
|
-
? this.getCallbacks(streamOptions.callbacks)
|
|
537
|
-
: [];
|
|
538
|
-
|
|
539
|
-
config.callbacks = baseCallbacks.concat(streamCallbacks).concat({
|
|
540
|
-
[Callback.CUSTOM_EVENT]: customEventCallback,
|
|
541
|
-
});
|
|
542
|
-
|
|
543
|
-
// Use streamEvents like processStream to emit proper SSE events
|
|
544
|
-
const stream = this.graphRunnable.streamEvents(inputs, config, {
|
|
545
|
-
raiseError: true,
|
|
546
|
-
ignoreCustomEvent: true,
|
|
547
|
-
});
|
|
548
|
-
|
|
549
|
-
let wasInterrupted = false;
|
|
550
|
-
|
|
551
|
-
try {
|
|
552
|
-
for await (const event of stream) {
|
|
553
|
-
const { data, metadata, ...info } = event;
|
|
554
|
-
const eventName: t.EventName = info.event;
|
|
555
|
-
|
|
556
|
-
// Skip custom events as they're handled by our callback
|
|
557
|
-
if (eventName === GraphEvents.ON_CUSTOM_EVENT) {
|
|
558
|
-
continue;
|
|
559
|
-
}
|
|
560
|
-
|
|
561
|
-
const handler = this.handlerRegistry?.getHandler(eventName);
|
|
562
|
-
if (handler) {
|
|
563
|
-
await handler.handle(eventName, data, metadata, this.Graph);
|
|
564
|
-
}
|
|
565
|
-
|
|
566
|
-
// Also yield the event for the caller to process
|
|
567
|
-
yield { type: 'event', data: event };
|
|
568
|
-
}
|
|
569
|
-
} catch (error) {
|
|
570
|
-
// streamEvents may throw when interrupt happens
|
|
571
|
-
// Check if this is an interrupt-related termination
|
|
572
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
573
|
-
if (errorMessage.includes('interrupt') || errorMessage.includes('GraphInterrupt')) {
|
|
574
|
-
wasInterrupted = true;
|
|
575
|
-
} else {
|
|
576
|
-
throw error;
|
|
577
|
-
}
|
|
578
|
-
}
|
|
579
|
-
|
|
580
|
-
// After stream completes, check for pending interrupts using getState
|
|
581
|
-
// The checkpointer stores the interrupt state
|
|
582
|
-
try {
|
|
583
|
-
const state = await this.graphRunnable.getState(config);
|
|
584
|
-
if (state?.tasks) {
|
|
585
|
-
for (const task of state.tasks) {
|
|
586
|
-
if (task.interrupts && task.interrupts.length > 0) {
|
|
587
|
-
for (const interruptInfo of task.interrupts) {
|
|
588
|
-
if (interruptInfo.value?.type === 'browser_interrupt') {
|
|
589
|
-
yield { type: 'interrupt', data: interruptInfo.value as BrowserInterrupt };
|
|
590
|
-
return; // Stop after yielding interrupt
|
|
591
|
-
}
|
|
592
|
-
}
|
|
593
|
-
}
|
|
594
|
-
}
|
|
595
|
-
}
|
|
596
|
-
} catch (stateError) {
|
|
597
|
-
// getState may fail if no checkpointer - that's ok for non-browser flows
|
|
598
|
-
console.debug('[processBrowserStream] Could not get state:', stateError);
|
|
599
|
-
}
|
|
600
|
-
|
|
601
|
-
// Stream completed without interrupt
|
|
602
|
-
if (this.returnContent) {
|
|
603
|
-
yield { type: 'done', data: this.Graph.getContentParts() };
|
|
604
|
-
} else {
|
|
605
|
-
yield { type: 'done' };
|
|
606
|
-
}
|
|
607
|
-
}
|
|
608
|
-
|
|
609
|
-
/**
|
|
610
|
-
* Resume a browser stream after interrupt.
|
|
611
|
-
* Call this with the result from the browser extension.
|
|
612
|
-
* Uses streamEvents() to emit proper SSE events during resume.
|
|
613
|
-
*/
|
|
614
|
-
async *resumeBrowserStream(
|
|
615
|
-
result: BrowserActionResult,
|
|
616
|
-
config: Partial<RunnableConfig> & { version: 'v1' | 'v2'; run_id?: string },
|
|
617
|
-
streamOptions?: t.EventStreamOptions
|
|
618
|
-
): AsyncGenerator<
|
|
619
|
-
| { type: 'event'; data: unknown }
|
|
620
|
-
| { type: 'interrupt'; data: BrowserInterrupt }
|
|
621
|
-
| { type: 'done'; data?: MessageContentComplex[] }
|
|
622
|
-
> {
|
|
623
|
-
if (this.graphRunnable == null) {
|
|
624
|
-
throw new Error(
|
|
625
|
-
'Run not initialized. Make sure to use Run.create() to instantiate the Run.'
|
|
626
|
-
);
|
|
627
|
-
}
|
|
628
|
-
if (!this.Graph) {
|
|
629
|
-
throw new Error(
|
|
630
|
-
'Graph not initialized. Make sure to use Run.create() to instantiate the Run.'
|
|
631
|
-
);
|
|
632
|
-
}
|
|
633
|
-
|
|
634
|
-
if (!this.id) {
|
|
635
|
-
throw new Error('Run ID not provided');
|
|
636
|
-
}
|
|
637
|
-
|
|
638
|
-
config.run_id = this.id;
|
|
639
|
-
config.configurable = Object.assign(config.configurable ?? {}, {
|
|
640
|
-
run_id: this.id,
|
|
641
|
-
thread_id: this.id,
|
|
642
|
-
});
|
|
643
|
-
|
|
644
|
-
// Set up callbacks for SSE event emission (same as processStream)
|
|
645
|
-
const customEventCallback = this.createCustomEventCallback();
|
|
646
|
-
|
|
647
|
-
const baseCallbacks = (config.callbacks as t.ProvidedCallbacks) ?? [];
|
|
648
|
-
const streamCallbacks = streamOptions?.callbacks
|
|
649
|
-
? this.getCallbacks(streamOptions.callbacks)
|
|
650
|
-
: [];
|
|
651
|
-
|
|
652
|
-
config.callbacks = baseCallbacks.concat(streamCallbacks).concat({
|
|
653
|
-
[Callback.CUSTOM_EVENT]: customEventCallback,
|
|
654
|
-
});
|
|
655
|
-
|
|
656
|
-
// Use Command to resume with the browser result
|
|
657
|
-
const resumeCommand = new Command({ resume: result });
|
|
658
|
-
|
|
659
|
-
// Use streamEvents for proper SSE event emission
|
|
660
|
-
const stream = this.graphRunnable.streamEvents(resumeCommand, config, {
|
|
661
|
-
raiseError: true,
|
|
662
|
-
ignoreCustomEvent: true,
|
|
663
|
-
});
|
|
664
|
-
|
|
665
|
-
try {
|
|
666
|
-
for await (const event of stream) {
|
|
667
|
-
const { data, metadata, ...info } = event;
|
|
668
|
-
const eventName: t.EventName = info.event;
|
|
669
|
-
|
|
670
|
-
if (eventName === GraphEvents.ON_CUSTOM_EVENT) {
|
|
671
|
-
continue;
|
|
672
|
-
}
|
|
673
|
-
|
|
674
|
-
const handler = this.handlerRegistry?.getHandler(eventName);
|
|
675
|
-
if (handler) {
|
|
676
|
-
await handler.handle(eventName, data, metadata, this.Graph);
|
|
677
|
-
}
|
|
678
|
-
|
|
679
|
-
yield { type: 'event', data: event };
|
|
680
|
-
}
|
|
681
|
-
} catch (error) {
|
|
682
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
683
|
-
if (!errorMessage.includes('interrupt') && !errorMessage.includes('GraphInterrupt')) {
|
|
684
|
-
throw error;
|
|
685
|
-
}
|
|
686
|
-
}
|
|
687
|
-
|
|
688
|
-
// Check for additional interrupts after resume
|
|
689
|
-
try {
|
|
690
|
-
const state = await this.graphRunnable.getState(config);
|
|
691
|
-
if (state?.tasks) {
|
|
692
|
-
for (const task of state.tasks) {
|
|
693
|
-
if (task.interrupts && task.interrupts.length > 0) {
|
|
694
|
-
for (const interruptInfo of task.interrupts) {
|
|
695
|
-
if (interruptInfo.value?.type === 'browser_interrupt') {
|
|
696
|
-
yield { type: 'interrupt', data: interruptInfo.value as BrowserInterrupt };
|
|
697
|
-
return;
|
|
698
|
-
}
|
|
699
|
-
}
|
|
700
|
-
}
|
|
701
|
-
}
|
|
702
|
-
}
|
|
703
|
-
} catch (stateError) {
|
|
704
|
-
console.debug('[resumeBrowserStream] Could not get state:', stateError);
|
|
705
|
-
}
|
|
706
|
-
|
|
707
|
-
if (this.returnContent) {
|
|
708
|
-
yield { type: 'done', data: this.Graph.getContentParts() };
|
|
709
|
-
} else {
|
|
710
|
-
yield { type: 'done' };
|
|
711
|
-
}
|
|
712
|
-
}
|
|
713
456
|
}
|
package/src/types/run.ts
CHANGED
|
@@ -115,14 +115,6 @@ export type RunConfig = {
|
|
|
115
115
|
returnContent?: boolean;
|
|
116
116
|
tokenCounter?: TokenCounter;
|
|
117
117
|
indexTokenCountMap?: Record<string, number>;
|
|
118
|
-
/**
|
|
119
|
-
* Enable browser extension mode with interrupt-based tool execution.
|
|
120
|
-
* When true:
|
|
121
|
-
* - Uses MemorySaver checkpointer for pause/resume
|
|
122
|
-
* - Browser tools will interrupt execution and wait for extension results
|
|
123
|
-
* - Extension must call resume endpoint with Command to continue
|
|
124
|
-
*/
|
|
125
|
-
browserMode?: boolean;
|
|
126
118
|
};
|
|
127
119
|
|
|
128
120
|
export type ProvidedCallbacks =
|