@librechat/agents 3.1.86 → 3.1.88
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +69 -0
- package/dist/cjs/events.cjs +23 -0
- package/dist/cjs/events.cjs.map +1 -1
- package/dist/cjs/graphs/Graph.cjs +133 -18
- package/dist/cjs/graphs/Graph.cjs.map +1 -1
- package/dist/cjs/graphs/MultiAgentGraph.cjs +1 -1
- package/dist/cjs/graphs/MultiAgentGraph.cjs.map +1 -1
- package/dist/cjs/llm/anthropic/index.cjs +251 -53
- package/dist/cjs/llm/anthropic/index.cjs.map +1 -1
- package/dist/cjs/llm/init.cjs +1 -5
- package/dist/cjs/llm/init.cjs.map +1 -1
- package/dist/cjs/llm/openai/index.cjs +113 -24
- package/dist/cjs/llm/openai/index.cjs.map +1 -1
- package/dist/cjs/llm/openai/utils/index.cjs.map +1 -1
- package/dist/cjs/llm/openrouter/index.cjs +3 -1
- package/dist/cjs/llm/openrouter/index.cjs.map +1 -1
- package/dist/cjs/main.cjs +18 -5
- package/dist/cjs/main.cjs.map +1 -1
- package/dist/cjs/openai/index.cjs +253 -0
- package/dist/cjs/openai/index.cjs.map +1 -0
- package/dist/cjs/responses/index.cjs +448 -0
- package/dist/cjs/responses/index.cjs.map +1 -0
- package/dist/cjs/run.cjs +108 -7
- package/dist/cjs/run.cjs.map +1 -1
- package/dist/cjs/session/AgentSession.cjs +1057 -0
- package/dist/cjs/session/AgentSession.cjs.map +1 -0
- package/dist/cjs/session/JsonlSessionStore.cjs +425 -0
- package/dist/cjs/session/JsonlSessionStore.cjs.map +1 -0
- package/dist/cjs/session/handlers.cjs +221 -0
- package/dist/cjs/session/handlers.cjs.map +1 -0
- package/dist/cjs/session/ids.cjs +22 -0
- package/dist/cjs/session/ids.cjs.map +1 -0
- package/dist/cjs/session/messageSerialization.cjs +179 -0
- package/dist/cjs/session/messageSerialization.cjs.map +1 -0
- package/dist/cjs/stream.cjs +475 -11
- package/dist/cjs/stream.cjs.map +1 -1
- package/dist/cjs/summarization/node.cjs +1 -1
- package/dist/cjs/summarization/node.cjs.map +1 -1
- package/dist/cjs/tools/ToolNode.cjs +177 -59
- package/dist/cjs/tools/ToolNode.cjs.map +1 -1
- package/dist/cjs/tools/eagerEventExecution.cjs +113 -0
- package/dist/cjs/tools/eagerEventExecution.cjs.map +1 -0
- package/dist/cjs/tools/handlers.cjs +1 -1
- package/dist/cjs/tools/handlers.cjs.map +1 -1
- package/dist/cjs/tools/streamedToolCallSeals.cjs +42 -0
- package/dist/cjs/tools/streamedToolCallSeals.cjs.map +1 -0
- package/dist/esm/events.mjs +23 -1
- package/dist/esm/events.mjs.map +1 -1
- package/dist/esm/graphs/Graph.mjs +133 -18
- package/dist/esm/graphs/Graph.mjs.map +1 -1
- package/dist/esm/graphs/MultiAgentGraph.mjs +1 -1
- package/dist/esm/graphs/MultiAgentGraph.mjs.map +1 -1
- package/dist/esm/llm/anthropic/index.mjs +251 -53
- package/dist/esm/llm/anthropic/index.mjs.map +1 -1
- package/dist/esm/llm/init.mjs +1 -5
- package/dist/esm/llm/init.mjs.map +1 -1
- package/dist/esm/llm/openai/index.mjs +113 -25
- package/dist/esm/llm/openai/index.mjs.map +1 -1
- package/dist/esm/llm/openai/utils/index.mjs.map +1 -1
- package/dist/esm/llm/openrouter/index.mjs +4 -2
- package/dist/esm/llm/openrouter/index.mjs.map +1 -1
- package/dist/esm/main.mjs +5 -1
- package/dist/esm/main.mjs.map +1 -1
- package/dist/esm/openai/index.mjs +246 -0
- package/dist/esm/openai/index.mjs.map +1 -0
- package/dist/esm/responses/index.mjs +440 -0
- package/dist/esm/responses/index.mjs.map +1 -0
- package/dist/esm/run.mjs +108 -7
- package/dist/esm/run.mjs.map +1 -1
- package/dist/esm/session/AgentSession.mjs +1054 -0
- package/dist/esm/session/AgentSession.mjs.map +1 -0
- package/dist/esm/session/JsonlSessionStore.mjs +422 -0
- package/dist/esm/session/JsonlSessionStore.mjs.map +1 -0
- package/dist/esm/session/handlers.mjs +219 -0
- package/dist/esm/session/handlers.mjs.map +1 -0
- package/dist/esm/session/ids.mjs +17 -0
- package/dist/esm/session/ids.mjs.map +1 -0
- package/dist/esm/session/messageSerialization.mjs +173 -0
- package/dist/esm/session/messageSerialization.mjs.map +1 -0
- package/dist/esm/stream.mjs +476 -12
- package/dist/esm/stream.mjs.map +1 -1
- package/dist/esm/summarization/node.mjs +1 -1
- package/dist/esm/summarization/node.mjs.map +1 -1
- package/dist/esm/tools/ToolNode.mjs +177 -59
- package/dist/esm/tools/ToolNode.mjs.map +1 -1
- package/dist/esm/tools/eagerEventExecution.mjs +107 -0
- package/dist/esm/tools/eagerEventExecution.mjs.map +1 -0
- package/dist/esm/tools/handlers.mjs +1 -1
- package/dist/esm/tools/handlers.mjs.map +1 -1
- package/dist/esm/tools/streamedToolCallSeals.mjs +36 -0
- package/dist/esm/tools/streamedToolCallSeals.mjs.map +1 -0
- package/dist/types/events.d.ts +1 -0
- package/dist/types/graphs/Graph.d.ts +24 -9
- package/dist/types/index.d.ts +1 -0
- package/dist/types/llm/openai/index.d.ts +1 -0
- package/dist/types/openai/index.d.ts +75 -0
- package/dist/types/responses/index.d.ts +97 -0
- package/dist/types/run.d.ts +2 -0
- package/dist/types/session/AgentSession.d.ts +32 -0
- package/dist/types/session/JsonlSessionStore.d.ts +67 -0
- package/dist/types/session/handlers.d.ts +8 -0
- package/dist/types/session/ids.d.ts +4 -0
- package/dist/types/session/index.d.ts +5 -0
- package/dist/types/session/messageSerialization.d.ts +7 -0
- package/dist/types/session/types.d.ts +191 -0
- package/dist/types/tools/ToolNode.d.ts +12 -1
- package/dist/types/tools/eagerEventExecution.d.ts +23 -0
- package/dist/types/tools/streamedToolCallSeals.d.ts +13 -0
- package/dist/types/types/hitl.d.ts +4 -0
- package/dist/types/types/run.d.ts +11 -1
- package/dist/types/types/tools.d.ts +36 -0
- package/package.json +19 -2
- package/src/__tests__/stream.eagerEventExecution.test.ts +2571 -0
- package/src/events.ts +29 -0
- package/src/graphs/Graph.ts +224 -50
- package/src/graphs/MultiAgentGraph.ts +1 -1
- package/src/graphs/__tests__/composition.smoke.test.ts +30 -0
- package/src/index.ts +3 -0
- package/src/llm/anthropic/index.ts +356 -84
- package/src/llm/anthropic/llm.spec.ts +64 -0
- package/src/llm/custom-chat-models.smoke.test.ts +175 -4
- package/src/llm/openai/contentBlocks.test.ts +35 -0
- package/src/llm/openai/deepseek.test.ts +201 -2
- package/src/llm/openai/index.ts +171 -26
- package/src/llm/openai/utils/index.ts +22 -0
- package/src/llm/openrouter/index.ts +4 -2
- package/src/openai/__tests__/openai.test.ts +337 -0
- package/src/openai/index.ts +404 -0
- package/src/responses/__tests__/responses.test.ts +652 -0
- package/src/responses/index.ts +677 -0
- package/src/run.ts +158 -8
- package/src/scripts/compare_pi_vs_ours.ts +592 -173
- package/src/scripts/session_live.ts +548 -0
- package/src/session/AgentSession.ts +1432 -0
- package/src/session/JsonlSessionStore.ts +572 -0
- package/src/session/__tests__/JsonlSessionStore.test.ts +1410 -0
- package/src/session/__tests__/handlers.test.ts +161 -0
- package/src/session/handlers.ts +272 -0
- package/src/session/ids.ts +17 -0
- package/src/session/index.ts +44 -0
- package/src/session/messageSerialization.ts +207 -0
- package/src/session/types.ts +275 -0
- package/src/specs/custom-event-await.test.ts +89 -0
- package/src/specs/summarization.test.ts +1 -1
- package/src/stream.ts +756 -48
- package/src/summarization/node.ts +1 -1
- package/src/tools/ToolNode.ts +299 -126
- package/src/tools/__tests__/ToolNode.eagerEventExecution.test.ts +373 -0
- package/src/tools/__tests__/handlers.test.ts +2 -1
- package/src/tools/__tests__/hitl.test.ts +206 -110
- package/src/tools/eagerEventExecution.ts +153 -0
- package/src/tools/handlers.ts +8 -4
- package/src/tools/streamedToolCallSeals.ts +57 -0
- package/src/types/hitl.ts +4 -0
- package/src/types/run.ts +11 -0
- package/src/types/tools.ts +36 -0
- package/dist/cjs/llm/text.cjs +0 -69
- package/dist/cjs/llm/text.cjs.map +0 -1
- package/dist/esm/llm/text.mjs +0 -67
- package/dist/esm/llm/text.mjs.map +0 -1
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import type * as t from '@/types';
|
|
2
|
+
|
|
3
|
+
export function coerceRecordArgs(
|
|
4
|
+
args: unknown
|
|
5
|
+
): Record<string, unknown> | undefined {
|
|
6
|
+
if (typeof args === 'string') {
|
|
7
|
+
try {
|
|
8
|
+
const parsed = JSON.parse(args) as unknown;
|
|
9
|
+
return coerceRecordArgs(parsed);
|
|
10
|
+
} catch {
|
|
11
|
+
return undefined;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
if (args === null || typeof args !== 'object' || Array.isArray(args)) {
|
|
16
|
+
return undefined;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
return args as Record<string, unknown>;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function stableStringify(value: unknown): string {
|
|
23
|
+
if (Array.isArray(value)) {
|
|
24
|
+
return `[${value.map((item) => stableStringify(item)).join(',')}]`;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (value !== null && typeof value === 'object') {
|
|
28
|
+
const record = value as Record<string, unknown>;
|
|
29
|
+
const keys = Object.keys(record).sort();
|
|
30
|
+
return `{${keys
|
|
31
|
+
.map((key) => `${JSON.stringify(key)}:${stableStringify(record[key])}`)
|
|
32
|
+
.join(',')}}`;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return JSON.stringify(value);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function recordArgsEqual(
|
|
39
|
+
left: Record<string, unknown>,
|
|
40
|
+
right: Record<string, unknown>
|
|
41
|
+
): boolean {
|
|
42
|
+
return stableStringify(left) === stableStringify(right);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function normalizeError(error: unknown): Error {
|
|
46
|
+
return error instanceof Error ? error : new Error(String(error));
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export type ToolExecutionPlanCall = {
|
|
50
|
+
id?: string;
|
|
51
|
+
name: string;
|
|
52
|
+
args: unknown;
|
|
53
|
+
stepId?: string;
|
|
54
|
+
codeSessionContext?: t.ToolCallRequest['codeSessionContext'];
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
export type ToolExecutionRequestPlan = {
|
|
58
|
+
allRequests: t.ToolCallRequest[];
|
|
59
|
+
requests: t.ToolCallRequest[];
|
|
60
|
+
rejectedResults: t.ToolExecuteResult[];
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
export function buildToolExecutionRequestPlan(args: {
|
|
64
|
+
toolCalls: ToolExecutionPlanCall[];
|
|
65
|
+
usageCount: Map<string, number>;
|
|
66
|
+
invalidArgsBehavior?: 'abort' | 'error-result';
|
|
67
|
+
recordTurn?: (toolName: string, turn: number, callId: string) => void;
|
|
68
|
+
}): ToolExecutionRequestPlan | undefined {
|
|
69
|
+
const invalidArgsBehavior = args.invalidArgsBehavior ?? 'abort';
|
|
70
|
+
const prepared: Array<{
|
|
71
|
+
id: string;
|
|
72
|
+
name: string;
|
|
73
|
+
args: Record<string, unknown>;
|
|
74
|
+
stepId?: string;
|
|
75
|
+
codeSessionContext?: t.ToolCallRequest['codeSessionContext'];
|
|
76
|
+
rejectedErrorMessage?: string;
|
|
77
|
+
}> = [];
|
|
78
|
+
|
|
79
|
+
for (const toolCall of args.toolCalls) {
|
|
80
|
+
if (
|
|
81
|
+
toolCall.id == null ||
|
|
82
|
+
toolCall.id === '' ||
|
|
83
|
+
toolCall.name === ''
|
|
84
|
+
) {
|
|
85
|
+
return undefined;
|
|
86
|
+
}
|
|
87
|
+
const coercedArgs = coerceRecordArgs(toolCall.args);
|
|
88
|
+
if (coercedArgs == null) {
|
|
89
|
+
if (invalidArgsBehavior === 'abort') {
|
|
90
|
+
return undefined;
|
|
91
|
+
}
|
|
92
|
+
prepared.push({
|
|
93
|
+
id: toolCall.id,
|
|
94
|
+
name: toolCall.name,
|
|
95
|
+
args: {},
|
|
96
|
+
stepId: toolCall.stepId,
|
|
97
|
+
codeSessionContext: toolCall.codeSessionContext,
|
|
98
|
+
rejectedErrorMessage:
|
|
99
|
+
'Invalid tool call arguments: expected a JSON object.',
|
|
100
|
+
});
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
prepared.push({
|
|
104
|
+
id: toolCall.id,
|
|
105
|
+
name: toolCall.name,
|
|
106
|
+
args: coercedArgs,
|
|
107
|
+
stepId: toolCall.stepId,
|
|
108
|
+
codeSessionContext: toolCall.codeSessionContext,
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const nextUsageCount = new Map(args.usageCount);
|
|
113
|
+
const allRequests = prepared.map((toolCall): t.ToolCallRequest => {
|
|
114
|
+
const turn = nextUsageCount.get(toolCall.name) ?? 0;
|
|
115
|
+
nextUsageCount.set(toolCall.name, turn + 1);
|
|
116
|
+
const request: t.ToolCallRequest = {
|
|
117
|
+
id: toolCall.id,
|
|
118
|
+
name: toolCall.name,
|
|
119
|
+
args: toolCall.args,
|
|
120
|
+
stepId: toolCall.stepId,
|
|
121
|
+
turn,
|
|
122
|
+
};
|
|
123
|
+
if (toolCall.codeSessionContext != null) {
|
|
124
|
+
request.codeSessionContext = toolCall.codeSessionContext;
|
|
125
|
+
}
|
|
126
|
+
return request;
|
|
127
|
+
});
|
|
128
|
+
const requests = allRequests.filter(
|
|
129
|
+
(_, index) => prepared[index].rejectedErrorMessage == null
|
|
130
|
+
);
|
|
131
|
+
const rejectedResults = prepared.flatMap((toolCall) => {
|
|
132
|
+
if (toolCall.rejectedErrorMessage == null) {
|
|
133
|
+
return [];
|
|
134
|
+
}
|
|
135
|
+
return [
|
|
136
|
+
{
|
|
137
|
+
toolCallId: toolCall.id,
|
|
138
|
+
status: 'error' as const,
|
|
139
|
+
content: '',
|
|
140
|
+
errorMessage: toolCall.rejectedErrorMessage,
|
|
141
|
+
},
|
|
142
|
+
];
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
for (const [toolName, count] of nextUsageCount) {
|
|
146
|
+
args.usageCount.set(toolName, count);
|
|
147
|
+
}
|
|
148
|
+
for (const request of allRequests) {
|
|
149
|
+
args.recordTurn?.(request.name, request.turn ?? 0, request.id);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
return { allRequests, requests, rejectedResults };
|
|
153
|
+
}
|
package/src/tools/handlers.ts
CHANGED
|
@@ -118,10 +118,14 @@ export async function handleToolCallChunks({
|
|
|
118
118
|
);
|
|
119
119
|
}
|
|
120
120
|
|
|
121
|
-
await graph.dispatchRunStepDelta(
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
121
|
+
await graph.dispatchRunStepDelta(
|
|
122
|
+
stepId,
|
|
123
|
+
{
|
|
124
|
+
type: StepTypes.TOOL_CALLS,
|
|
125
|
+
tool_calls: toolCallChunks,
|
|
126
|
+
},
|
|
127
|
+
metadata
|
|
128
|
+
);
|
|
125
129
|
}
|
|
126
130
|
|
|
127
131
|
export const handleToolCalls = async (
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
export const STREAMED_TOOL_CALL_ADAPTER_METADATA_KEY =
|
|
2
|
+
'lc_streamed_tool_call_adapter';
|
|
3
|
+
export const STREAMED_TOOL_CALL_SEAL_METADATA_KEY =
|
|
4
|
+
'lc_streamed_tool_call_seal';
|
|
5
|
+
export const OPENAI_RESPONSES_STREAMED_TOOL_CALL_ADAPTER = 'openai_responses';
|
|
6
|
+
|
|
7
|
+
export type StreamedToolCallAdapter =
|
|
8
|
+
typeof OPENAI_RESPONSES_STREAMED_TOOL_CALL_ADAPTER;
|
|
9
|
+
|
|
10
|
+
export type StreamedToolCallSeal =
|
|
11
|
+
| {
|
|
12
|
+
kind: 'single';
|
|
13
|
+
id?: string;
|
|
14
|
+
index?: number;
|
|
15
|
+
}
|
|
16
|
+
| {
|
|
17
|
+
kind: 'all';
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export function getStreamedToolCallAdapter(
|
|
21
|
+
metadata: Record<string, unknown> | undefined
|
|
22
|
+
): StreamedToolCallAdapter | undefined {
|
|
23
|
+
if (
|
|
24
|
+
metadata?.[STREAMED_TOOL_CALL_ADAPTER_METADATA_KEY] ===
|
|
25
|
+
OPENAI_RESPONSES_STREAMED_TOOL_CALL_ADAPTER
|
|
26
|
+
) {
|
|
27
|
+
return OPENAI_RESPONSES_STREAMED_TOOL_CALL_ADAPTER;
|
|
28
|
+
}
|
|
29
|
+
return undefined;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function getStreamedToolCallSeal(
|
|
33
|
+
metadata: Record<string, unknown> | undefined
|
|
34
|
+
): StreamedToolCallSeal | undefined {
|
|
35
|
+
const seal = metadata?.[STREAMED_TOOL_CALL_SEAL_METADATA_KEY];
|
|
36
|
+
if (seal == null || typeof seal !== 'object') {
|
|
37
|
+
return undefined;
|
|
38
|
+
}
|
|
39
|
+
if (!('kind' in seal)) {
|
|
40
|
+
return undefined;
|
|
41
|
+
}
|
|
42
|
+
if (seal.kind === 'all') {
|
|
43
|
+
return { kind: 'all' };
|
|
44
|
+
}
|
|
45
|
+
if (seal.kind !== 'single') {
|
|
46
|
+
return undefined;
|
|
47
|
+
}
|
|
48
|
+
const id = 'id' in seal && typeof seal.id === 'string' ? seal.id : undefined;
|
|
49
|
+
const index =
|
|
50
|
+
'index' in seal && typeof seal.index === 'number'
|
|
51
|
+
? seal.index
|
|
52
|
+
: undefined;
|
|
53
|
+
if (id == null && index == null) {
|
|
54
|
+
return undefined;
|
|
55
|
+
}
|
|
56
|
+
return { kind: 'single', id, index };
|
|
57
|
+
}
|
package/src/types/hitl.ts
CHANGED
|
@@ -327,6 +327,10 @@ export interface RunInterruptResult<TPayload = HumanInterruptPayload> {
|
|
|
327
327
|
interruptId: string;
|
|
328
328
|
/** `thread_id` the run was bound to — required to resume. */
|
|
329
329
|
threadId?: string;
|
|
330
|
+
/** LangGraph checkpoint id that contains the paused interrupt task. */
|
|
331
|
+
checkpointId?: string;
|
|
332
|
+
/** LangGraph checkpoint namespace for the paused interrupt task. */
|
|
333
|
+
checkpointNs?: string;
|
|
330
334
|
/** Structured payload describing what needs human input. */
|
|
331
335
|
payload: TPayload;
|
|
332
336
|
}
|
package/src/types/run.ts
CHANGED
|
@@ -15,6 +15,7 @@ import type {
|
|
|
15
15
|
ToolSessionMap,
|
|
16
16
|
ToolExecutionConfig,
|
|
17
17
|
ToolOutputReferencesConfig,
|
|
18
|
+
EagerEventToolExecutionConfig,
|
|
18
19
|
} from '@/types/tools';
|
|
19
20
|
import type { HumanInTheLoopConfig } from '@/types/hitl';
|
|
20
21
|
import type { HookRegistry } from '@/hooks';
|
|
@@ -161,6 +162,16 @@ export type RunConfig = {
|
|
|
161
162
|
* placeholders. Disabled by default so existing runs are unaffected.
|
|
162
163
|
*/
|
|
163
164
|
toolOutputReferences?: ToolOutputReferencesConfig;
|
|
165
|
+
/**
|
|
166
|
+
* Opt-in latency optimization for event-driven tools. When enabled,
|
|
167
|
+
* the streaming layer may start a tool call as soon as it sees a
|
|
168
|
+
* complete, parseable tool call; ToolNode still waits for the final
|
|
169
|
+
* assistant message before appending ToolMessages, preserving provider
|
|
170
|
+
* ordering. The SDK automatically falls back to normal batch dispatch
|
|
171
|
+
* when hooks, HITL, output references, or ambiguous stream shapes make
|
|
172
|
+
* eager dispatch unsafe.
|
|
173
|
+
*/
|
|
174
|
+
eagerEventToolExecution?: EagerEventToolExecutionConfig;
|
|
164
175
|
/**
|
|
165
176
|
* Selects the execution backend for built-in code tools. Omit this to keep
|
|
166
177
|
* the remote LibreChat Code API sandbox. Set `{ engine: 'local' }` to run
|
package/src/types/tools.ts
CHANGED
|
@@ -29,6 +29,36 @@ export type ToolRefs = {
|
|
|
29
29
|
|
|
30
30
|
export type ToolRefGenerator = (tool_calls: ToolCall[]) => ToolRefs;
|
|
31
31
|
|
|
32
|
+
export type EagerEventToolExecutionConfig = {
|
|
33
|
+
/**
|
|
34
|
+
* When enabled, event-driven tool calls may be started from the
|
|
35
|
+
* streaming layer as soon as the SDK observes a complete tool call.
|
|
36
|
+
* Results are still held until ToolNode materializes the final
|
|
37
|
+
* ToolMessages so provider message ordering is preserved.
|
|
38
|
+
*/
|
|
39
|
+
enabled?: boolean;
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export type EagerEventToolExecutionOutcome =
|
|
43
|
+
| { results: ToolExecuteResult[]; error?: undefined }
|
|
44
|
+
| { results?: undefined; error: Error };
|
|
45
|
+
|
|
46
|
+
export type EagerEventToolExecution = {
|
|
47
|
+
toolCallId: string;
|
|
48
|
+
toolName: string;
|
|
49
|
+
args: Record<string, unknown>;
|
|
50
|
+
request: ToolCallRequest;
|
|
51
|
+
promise: Promise<EagerEventToolExecutionOutcome>;
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
export type EagerEventToolCallChunkState = {
|
|
55
|
+
id?: string;
|
|
56
|
+
name?: string;
|
|
57
|
+
argsText: string;
|
|
58
|
+
index?: number;
|
|
59
|
+
lastArgsFragment?: string;
|
|
60
|
+
};
|
|
61
|
+
|
|
32
62
|
export type ToolNodeOptions = {
|
|
33
63
|
name?: string;
|
|
34
64
|
tags?: string[];
|
|
@@ -51,6 +81,12 @@ export type ToolNodeOptions = {
|
|
|
51
81
|
agentId?: string;
|
|
52
82
|
/** Tool names that must be executed directly (via runTool) even in event-driven mode (e.g., graph-managed handoff tools) */
|
|
53
83
|
directToolNames?: Set<string>;
|
|
84
|
+
/** Opt-in eager execution for event-driven tool calls. */
|
|
85
|
+
eagerEventToolExecution?: EagerEventToolExecutionConfig;
|
|
86
|
+
/** Shared per-run eager execution registry populated by the stream handler. */
|
|
87
|
+
eagerEventToolExecutions?: Map<string, EagerEventToolExecution>;
|
|
88
|
+
/** Shared per-run per-tool turn counter used by eager and normal event dispatch. */
|
|
89
|
+
eagerEventToolUsageCount?: Map<string, number>;
|
|
54
90
|
/**
|
|
55
91
|
* Hook registry for PreToolUse/PostToolUse/PostToolUseFailure/
|
|
56
92
|
* PermissionDenied lifecycle hooks. Fires for **every** tool the
|
package/dist/cjs/llm/text.cjs
DELETED
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
class TextStream {
|
|
4
|
-
text;
|
|
5
|
-
currentIndex;
|
|
6
|
-
minChunkSize;
|
|
7
|
-
maxChunkSize;
|
|
8
|
-
delay;
|
|
9
|
-
firstWordChunk;
|
|
10
|
-
constructor(text, options = {}) {
|
|
11
|
-
this.text = text;
|
|
12
|
-
this.currentIndex = 0;
|
|
13
|
-
this.minChunkSize = options.minChunkSize ?? 4;
|
|
14
|
-
this.maxChunkSize = options.maxChunkSize ?? 8;
|
|
15
|
-
this.delay = options.delay ?? 20;
|
|
16
|
-
this.firstWordChunk = options.firstWordChunk ?? true;
|
|
17
|
-
}
|
|
18
|
-
randomInt(min, max) {
|
|
19
|
-
return Math.floor(Math.random() * (max - min)) + min;
|
|
20
|
-
}
|
|
21
|
-
static BOUNDARIES = new Set([
|
|
22
|
-
' ',
|
|
23
|
-
'.',
|
|
24
|
-
',',
|
|
25
|
-
'!',
|
|
26
|
-
'?',
|
|
27
|
-
';',
|
|
28
|
-
':',
|
|
29
|
-
]);
|
|
30
|
-
findFirstWordBoundary(text, minSize) {
|
|
31
|
-
if (minSize >= text.length)
|
|
32
|
-
return text.length;
|
|
33
|
-
// Ensure we meet the minimum size first
|
|
34
|
-
let pos = minSize;
|
|
35
|
-
// Look forward until we find a boundary
|
|
36
|
-
while (pos < text.length) {
|
|
37
|
-
if (TextStream.BOUNDARIES.has(text[pos])) {
|
|
38
|
-
return pos + 1; // Include the boundary character
|
|
39
|
-
}
|
|
40
|
-
pos++;
|
|
41
|
-
}
|
|
42
|
-
return text.length; // If no boundary found, return entire remaining text
|
|
43
|
-
}
|
|
44
|
-
async *generateText(signal, progressCallback) {
|
|
45
|
-
const { delay, minChunkSize, maxChunkSize } = this;
|
|
46
|
-
while (this.currentIndex < this.text.length) {
|
|
47
|
-
if (signal?.aborted === true) {
|
|
48
|
-
break;
|
|
49
|
-
}
|
|
50
|
-
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
51
|
-
const remainingText = this.text.slice(this.currentIndex);
|
|
52
|
-
let chunkSize;
|
|
53
|
-
if (this.firstWordChunk) {
|
|
54
|
-
chunkSize = this.findFirstWordBoundary(remainingText, minChunkSize);
|
|
55
|
-
}
|
|
56
|
-
else {
|
|
57
|
-
const remainingChars = remainingText.length;
|
|
58
|
-
chunkSize = Math.min(this.randomInt(minChunkSize, maxChunkSize + 1), remainingChars);
|
|
59
|
-
}
|
|
60
|
-
const chunk = this.text.slice(this.currentIndex, this.currentIndex + chunkSize);
|
|
61
|
-
progressCallback?.(chunk);
|
|
62
|
-
yield chunk;
|
|
63
|
-
this.currentIndex += chunkSize;
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
exports.TextStream = TextStream;
|
|
69
|
-
//# sourceMappingURL=text.cjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"text.cjs","sources":["../../../src/llm/text.ts"],"sourcesContent":["export interface TextStreamOptions {\n minChunkSize?: number;\n maxChunkSize?: number;\n delay?: number;\n firstWordChunk?: boolean;\n}\n\nexport type ProgressCallback = (chunk: string) => void;\nexport type PostChunkCallback = (chunk: string) => void;\n\nexport class TextStream {\n private text: string;\n private currentIndex: number;\n private minChunkSize: number;\n private maxChunkSize: number;\n private delay: number;\n private firstWordChunk: boolean;\n\n constructor(text: string, options: TextStreamOptions = {}) {\n this.text = text;\n this.currentIndex = 0;\n this.minChunkSize = options.minChunkSize ?? 4;\n this.maxChunkSize = options.maxChunkSize ?? 8;\n this.delay = options.delay ?? 20;\n this.firstWordChunk = options.firstWordChunk ?? true;\n }\n\n private randomInt(min: number, max: number): number {\n return Math.floor(Math.random() * (max - min)) + min;\n }\n\n private static readonly BOUNDARIES = new Set([\n ' ',\n '.',\n ',',\n '!',\n '?',\n ';',\n ':',\n ]);\n\n private findFirstWordBoundary(text: string, minSize: number): number {\n if (minSize >= text.length) return text.length;\n\n // Ensure we meet the minimum size first\n let pos = minSize;\n\n // Look forward until we find a boundary\n while (pos < text.length) {\n if (TextStream.BOUNDARIES.has(text[pos])) {\n return pos + 1; // Include the boundary character\n }\n pos++;\n }\n\n return text.length; // If no boundary found, return entire remaining text\n }\n\n async *generateText(\n signal?: AbortSignal,\n progressCallback?: ProgressCallback\n ): AsyncGenerator<string, void, unknown> {\n const { delay, minChunkSize, maxChunkSize } = this;\n\n while (this.currentIndex < this.text.length) {\n if (signal?.aborted === true) {\n break;\n }\n await new Promise((resolve) => setTimeout(resolve, delay));\n\n const remainingText = this.text.slice(this.currentIndex);\n let chunkSize: number;\n\n if (this.firstWordChunk) {\n chunkSize = this.findFirstWordBoundary(remainingText, minChunkSize);\n } else {\n const remainingChars = remainingText.length;\n chunkSize = Math.min(\n this.randomInt(minChunkSize, maxChunkSize + 1),\n remainingChars\n );\n }\n\n const chunk = this.text.slice(\n this.currentIndex,\n this.currentIndex + chunkSize\n );\n progressCallback?.(chunk);\n\n yield chunk;\n this.currentIndex += chunkSize;\n }\n }\n}\n"],"names":[],"mappings":";;MAUa,UAAU,CAAA;AACb,IAAA,IAAI;AACJ,IAAA,YAAY;AACZ,IAAA,YAAY;AACZ,IAAA,YAAY;AACZ,IAAA,KAAK;AACL,IAAA,cAAc;IAEtB,WAAA,CAAY,IAAY,EAAE,OAAA,GAA6B,EAAE,EAAA;AACvD,QAAA,IAAI,CAAC,IAAI,GAAG,IAAI;AAChB,QAAA,IAAI,CAAC,YAAY,GAAG,CAAC;QACrB,IAAI,CAAC,YAAY,GAAG,OAAO,CAAC,YAAY,IAAI,CAAC;QAC7C,IAAI,CAAC,YAAY,GAAG,OAAO,CAAC,YAAY,IAAI,CAAC;QAC7C,IAAI,CAAC,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,EAAE;QAChC,IAAI,CAAC,cAAc,GAAG,OAAO,CAAC,cAAc,IAAI,IAAI;IACtD;IAEQ,SAAS,CAAC,GAAW,EAAE,GAAW,EAAA;AACxC,QAAA,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,GAAG,GAAG,GAAG,CAAC,CAAC,GAAG,GAAG;IACtD;AAEQ,IAAA,OAAgB,UAAU,GAAG,IAAI,GAAG,CAAC;QAC3C,GAAG;QACH,GAAG;QACH,GAAG;QACH,GAAG;QACH,GAAG;QACH,GAAG;QACH,GAAG;AACJ,KAAA,CAAC;IAEM,qBAAqB,CAAC,IAAY,EAAE,OAAe,EAAA;AACzD,QAAA,IAAI,OAAO,IAAI,IAAI,CAAC,MAAM;YAAE,OAAO,IAAI,CAAC,MAAM;;QAG9C,IAAI,GAAG,GAAG,OAAO;;AAGjB,QAAA,OAAO,GAAG,GAAG,IAAI,CAAC,MAAM,EAAE;AACxB,YAAA,IAAI,UAAU,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE;AACxC,gBAAA,OAAO,GAAG,GAAG,CAAC,CAAC;YACjB;AACA,YAAA,GAAG,EAAE;QACP;AAEA,QAAA,OAAO,IAAI,CAAC,MAAM,CAAC;IACrB;AAEA,IAAA,OAAO,YAAY,CACjB,MAAoB,EACpB,gBAAmC,EAAA;QAEnC,MAAM,EAAE,KAAK,EAAE,YAAY,EAAE,YAAY,EAAE,GAAG,IAAI;QAElD,OAAO,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE;AAC3C,YAAA,IAAI,MAAM,EAAE,OAAO,KAAK,IAAI,EAAE;gBAC5B;YACF;AACA,YAAA,MAAM,IAAI,OAAO,CAAC,CAAC,OAAO,KAAK,UAAU,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;AAE1D,YAAA,MAAM,aAAa,GAAG,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,YAAY,CAAC;AACxD,YAAA,IAAI,SAAiB;AAErB,YAAA,IAAI,IAAI,CAAC,cAAc,EAAE;gBACvB,SAAS,GAAG,IAAI,CAAC,qBAAqB,CAAC,aAAa,EAAE,YAAY,CAAC;YACrE;iBAAO;AACL,gBAAA,MAAM,cAAc,GAAG,aAAa,CAAC,MAAM;AAC3C,gBAAA,SAAS,GAAG,IAAI,CAAC,GAAG,CAClB,IAAI,CAAC,SAAS,CAAC,YAAY,EAAE,YAAY,GAAG,CAAC,CAAC,EAC9C,cAAc,CACf;YACH;AAEA,YAAA,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,KAAK,CAC3B,IAAI,CAAC,YAAY,EACjB,IAAI,CAAC,YAAY,GAAG,SAAS,CAC9B;AACD,YAAA,gBAAgB,GAAG,KAAK,CAAC;AAEzB,YAAA,MAAM,KAAK;AACX,YAAA,IAAI,CAAC,YAAY,IAAI,SAAS;QAChC;IACF;;;;;"}
|
package/dist/esm/llm/text.mjs
DELETED
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
class TextStream {
|
|
2
|
-
text;
|
|
3
|
-
currentIndex;
|
|
4
|
-
minChunkSize;
|
|
5
|
-
maxChunkSize;
|
|
6
|
-
delay;
|
|
7
|
-
firstWordChunk;
|
|
8
|
-
constructor(text, options = {}) {
|
|
9
|
-
this.text = text;
|
|
10
|
-
this.currentIndex = 0;
|
|
11
|
-
this.minChunkSize = options.minChunkSize ?? 4;
|
|
12
|
-
this.maxChunkSize = options.maxChunkSize ?? 8;
|
|
13
|
-
this.delay = options.delay ?? 20;
|
|
14
|
-
this.firstWordChunk = options.firstWordChunk ?? true;
|
|
15
|
-
}
|
|
16
|
-
randomInt(min, max) {
|
|
17
|
-
return Math.floor(Math.random() * (max - min)) + min;
|
|
18
|
-
}
|
|
19
|
-
static BOUNDARIES = new Set([
|
|
20
|
-
' ',
|
|
21
|
-
'.',
|
|
22
|
-
',',
|
|
23
|
-
'!',
|
|
24
|
-
'?',
|
|
25
|
-
';',
|
|
26
|
-
':',
|
|
27
|
-
]);
|
|
28
|
-
findFirstWordBoundary(text, minSize) {
|
|
29
|
-
if (minSize >= text.length)
|
|
30
|
-
return text.length;
|
|
31
|
-
// Ensure we meet the minimum size first
|
|
32
|
-
let pos = minSize;
|
|
33
|
-
// Look forward until we find a boundary
|
|
34
|
-
while (pos < text.length) {
|
|
35
|
-
if (TextStream.BOUNDARIES.has(text[pos])) {
|
|
36
|
-
return pos + 1; // Include the boundary character
|
|
37
|
-
}
|
|
38
|
-
pos++;
|
|
39
|
-
}
|
|
40
|
-
return text.length; // If no boundary found, return entire remaining text
|
|
41
|
-
}
|
|
42
|
-
async *generateText(signal, progressCallback) {
|
|
43
|
-
const { delay, minChunkSize, maxChunkSize } = this;
|
|
44
|
-
while (this.currentIndex < this.text.length) {
|
|
45
|
-
if (signal?.aborted === true) {
|
|
46
|
-
break;
|
|
47
|
-
}
|
|
48
|
-
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
49
|
-
const remainingText = this.text.slice(this.currentIndex);
|
|
50
|
-
let chunkSize;
|
|
51
|
-
if (this.firstWordChunk) {
|
|
52
|
-
chunkSize = this.findFirstWordBoundary(remainingText, minChunkSize);
|
|
53
|
-
}
|
|
54
|
-
else {
|
|
55
|
-
const remainingChars = remainingText.length;
|
|
56
|
-
chunkSize = Math.min(this.randomInt(minChunkSize, maxChunkSize + 1), remainingChars);
|
|
57
|
-
}
|
|
58
|
-
const chunk = this.text.slice(this.currentIndex, this.currentIndex + chunkSize);
|
|
59
|
-
progressCallback?.(chunk);
|
|
60
|
-
yield chunk;
|
|
61
|
-
this.currentIndex += chunkSize;
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
export { TextStream };
|
|
67
|
-
//# sourceMappingURL=text.mjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"text.mjs","sources":["../../../src/llm/text.ts"],"sourcesContent":["export interface TextStreamOptions {\n minChunkSize?: number;\n maxChunkSize?: number;\n delay?: number;\n firstWordChunk?: boolean;\n}\n\nexport type ProgressCallback = (chunk: string) => void;\nexport type PostChunkCallback = (chunk: string) => void;\n\nexport class TextStream {\n private text: string;\n private currentIndex: number;\n private minChunkSize: number;\n private maxChunkSize: number;\n private delay: number;\n private firstWordChunk: boolean;\n\n constructor(text: string, options: TextStreamOptions = {}) {\n this.text = text;\n this.currentIndex = 0;\n this.minChunkSize = options.minChunkSize ?? 4;\n this.maxChunkSize = options.maxChunkSize ?? 8;\n this.delay = options.delay ?? 20;\n this.firstWordChunk = options.firstWordChunk ?? true;\n }\n\n private randomInt(min: number, max: number): number {\n return Math.floor(Math.random() * (max - min)) + min;\n }\n\n private static readonly BOUNDARIES = new Set([\n ' ',\n '.',\n ',',\n '!',\n '?',\n ';',\n ':',\n ]);\n\n private findFirstWordBoundary(text: string, minSize: number): number {\n if (minSize >= text.length) return text.length;\n\n // Ensure we meet the minimum size first\n let pos = minSize;\n\n // Look forward until we find a boundary\n while (pos < text.length) {\n if (TextStream.BOUNDARIES.has(text[pos])) {\n return pos + 1; // Include the boundary character\n }\n pos++;\n }\n\n return text.length; // If no boundary found, return entire remaining text\n }\n\n async *generateText(\n signal?: AbortSignal,\n progressCallback?: ProgressCallback\n ): AsyncGenerator<string, void, unknown> {\n const { delay, minChunkSize, maxChunkSize } = this;\n\n while (this.currentIndex < this.text.length) {\n if (signal?.aborted === true) {\n break;\n }\n await new Promise((resolve) => setTimeout(resolve, delay));\n\n const remainingText = this.text.slice(this.currentIndex);\n let chunkSize: number;\n\n if (this.firstWordChunk) {\n chunkSize = this.findFirstWordBoundary(remainingText, minChunkSize);\n } else {\n const remainingChars = remainingText.length;\n chunkSize = Math.min(\n this.randomInt(minChunkSize, maxChunkSize + 1),\n remainingChars\n );\n }\n\n const chunk = this.text.slice(\n this.currentIndex,\n this.currentIndex + chunkSize\n );\n progressCallback?.(chunk);\n\n yield chunk;\n this.currentIndex += chunkSize;\n }\n }\n}\n"],"names":[],"mappings":"MAUa,UAAU,CAAA;AACb,IAAA,IAAI;AACJ,IAAA,YAAY;AACZ,IAAA,YAAY;AACZ,IAAA,YAAY;AACZ,IAAA,KAAK;AACL,IAAA,cAAc;IAEtB,WAAA,CAAY,IAAY,EAAE,OAAA,GAA6B,EAAE,EAAA;AACvD,QAAA,IAAI,CAAC,IAAI,GAAG,IAAI;AAChB,QAAA,IAAI,CAAC,YAAY,GAAG,CAAC;QACrB,IAAI,CAAC,YAAY,GAAG,OAAO,CAAC,YAAY,IAAI,CAAC;QAC7C,IAAI,CAAC,YAAY,GAAG,OAAO,CAAC,YAAY,IAAI,CAAC;QAC7C,IAAI,CAAC,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,EAAE;QAChC,IAAI,CAAC,cAAc,GAAG,OAAO,CAAC,cAAc,IAAI,IAAI;IACtD;IAEQ,SAAS,CAAC,GAAW,EAAE,GAAW,EAAA;AACxC,QAAA,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,GAAG,GAAG,GAAG,CAAC,CAAC,GAAG,GAAG;IACtD;AAEQ,IAAA,OAAgB,UAAU,GAAG,IAAI,GAAG,CAAC;QAC3C,GAAG;QACH,GAAG;QACH,GAAG;QACH,GAAG;QACH,GAAG;QACH,GAAG;QACH,GAAG;AACJ,KAAA,CAAC;IAEM,qBAAqB,CAAC,IAAY,EAAE,OAAe,EAAA;AACzD,QAAA,IAAI,OAAO,IAAI,IAAI,CAAC,MAAM;YAAE,OAAO,IAAI,CAAC,MAAM;;QAG9C,IAAI,GAAG,GAAG,OAAO;;AAGjB,QAAA,OAAO,GAAG,GAAG,IAAI,CAAC,MAAM,EAAE;AACxB,YAAA,IAAI,UAAU,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE;AACxC,gBAAA,OAAO,GAAG,GAAG,CAAC,CAAC;YACjB;AACA,YAAA,GAAG,EAAE;QACP;AAEA,QAAA,OAAO,IAAI,CAAC,MAAM,CAAC;IACrB;AAEA,IAAA,OAAO,YAAY,CACjB,MAAoB,EACpB,gBAAmC,EAAA;QAEnC,MAAM,EAAE,KAAK,EAAE,YAAY,EAAE,YAAY,EAAE,GAAG,IAAI;QAElD,OAAO,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE;AAC3C,YAAA,IAAI,MAAM,EAAE,OAAO,KAAK,IAAI,EAAE;gBAC5B;YACF;AACA,YAAA,MAAM,IAAI,OAAO,CAAC,CAAC,OAAO,KAAK,UAAU,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;AAE1D,YAAA,MAAM,aAAa,GAAG,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,YAAY,CAAC;AACxD,YAAA,IAAI,SAAiB;AAErB,YAAA,IAAI,IAAI,CAAC,cAAc,EAAE;gBACvB,SAAS,GAAG,IAAI,CAAC,qBAAqB,CAAC,aAAa,EAAE,YAAY,CAAC;YACrE;iBAAO;AACL,gBAAA,MAAM,cAAc,GAAG,aAAa,CAAC,MAAM;AAC3C,gBAAA,SAAS,GAAG,IAAI,CAAC,GAAG,CAClB,IAAI,CAAC,SAAS,CAAC,YAAY,EAAE,YAAY,GAAG,CAAC,CAAC,EAC9C,cAAc,CACf;YACH;AAEA,YAAA,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,KAAK,CAC3B,IAAI,CAAC,YAAY,EACjB,IAAI,CAAC,YAAY,GAAG,SAAS,CAC9B;AACD,YAAA,gBAAgB,GAAG,KAAK,CAAC;AAEzB,YAAA,MAAM,KAAK;AACX,YAAA,IAAI,CAAC,YAAY,IAAI,SAAS;QAChC;IACF;;;;;"}
|