@librechat/agents 3.6.10 → 3.6.12
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/agents/AgentContext.cjs +63 -10
- package/dist/cjs/agents/AgentContext.cjs.map +1 -1
- package/dist/cjs/graphs/Graph.cjs +48 -119
- package/dist/cjs/graphs/Graph.cjs.map +1 -1
- package/dist/cjs/llm/contextPressureMeter.cjs +132 -0
- package/dist/cjs/llm/contextPressureMeter.cjs.map +1 -0
- package/dist/cjs/llm/invoke.cjs +46 -75
- package/dist/cjs/llm/invoke.cjs.map +1 -1
- package/dist/cjs/llm/prepareProviderRequest.cjs +108 -0
- package/dist/cjs/llm/prepareProviderRequest.cjs.map +1 -0
- package/dist/cjs/main.cjs +4 -0
- package/dist/cjs/messages/content.cjs +5 -4
- package/dist/cjs/messages/content.cjs.map +1 -1
- package/dist/cjs/stream.cjs +1 -0
- package/dist/cjs/stream.cjs.map +1 -1
- package/dist/cjs/tools/CallerCapabilities.cjs +31 -0
- package/dist/cjs/tools/CallerCapabilities.cjs.map +1 -1
- package/dist/cjs/tools/ToolNode.cjs +23 -6
- package/dist/cjs/tools/ToolNode.cjs.map +1 -1
- package/dist/cjs/tools/local/resolveLocalExecutionTools.cjs +26 -2
- package/dist/cjs/tools/local/resolveLocalExecutionTools.cjs.map +1 -1
- package/dist/esm/agents/AgentContext.mjs +65 -12
- package/dist/esm/agents/AgentContext.mjs.map +1 -1
- package/dist/esm/graphs/Graph.mjs +51 -122
- package/dist/esm/graphs/Graph.mjs.map +1 -1
- package/dist/esm/llm/contextPressureMeter.mjs +132 -0
- package/dist/esm/llm/contextPressureMeter.mjs.map +1 -0
- package/dist/esm/llm/invoke.mjs +48 -75
- package/dist/esm/llm/invoke.mjs.map +1 -1
- package/dist/esm/llm/prepareProviderRequest.mjs +105 -0
- package/dist/esm/llm/prepareProviderRequest.mjs.map +1 -0
- package/dist/esm/main.mjs +3 -2
- package/dist/esm/messages/content.mjs +5 -4
- package/dist/esm/messages/content.mjs.map +1 -1
- package/dist/esm/stream.mjs +1 -0
- package/dist/esm/stream.mjs.map +1 -1
- package/dist/esm/tools/CallerCapabilities.mjs +29 -1
- package/dist/esm/tools/CallerCapabilities.mjs.map +1 -1
- package/dist/esm/tools/ToolNode.mjs +24 -7
- package/dist/esm/tools/ToolNode.mjs.map +1 -1
- package/dist/esm/tools/local/resolveLocalExecutionTools.mjs +26 -4
- package/dist/esm/tools/local/resolveLocalExecutionTools.mjs.map +1 -1
- package/dist/types/agents/AgentContext.d.ts +11 -0
- package/dist/types/index.d.ts +2 -0
- package/dist/types/llm/contextPressureMeter.d.ts +30 -0
- package/dist/types/llm/invoke.d.ts +31 -33
- package/dist/types/llm/prepareProviderRequest.d.ts +55 -0
- package/dist/types/tools/CallerCapabilities.d.ts +13 -0
- package/dist/types/tools/ToolNode.d.ts +9 -1
- package/dist/types/tools/local/resolveLocalExecutionTools.d.ts +6 -0
- package/dist/types/types/tools.d.ts +17 -0
- package/package.json +1 -1
- package/src/agents/AgentContext.ts +157 -15
- package/src/graphs/Graph.ts +75 -279
- package/src/index.ts +8 -0
- package/src/llm/contextPressureMeter.ts +284 -0
- package/src/llm/invoke.ts +136 -296
- package/src/llm/prepareProviderRequest.ts +357 -0
- package/src/messages/content.ts +3 -3
- package/src/stream.ts +6 -0
- package/src/tools/CallerCapabilities.ts +68 -0
- package/src/tools/ToolNode.ts +47 -4
- package/src/tools/local/resolveLocalExecutionTools.ts +77 -3
- package/src/types/tools.ts +18 -0
|
@@ -0,0 +1,357 @@
|
|
|
1
|
+
import type { RunnableConfig } from '@langchain/core/runnables';
|
|
2
|
+
import type { BaseMessage } from '@langchain/core/messages';
|
|
3
|
+
import type { ToolOutputReferenceRegistry } from '@/tools/toolOutputReferences';
|
|
4
|
+
import type * as t from '@/types';
|
|
5
|
+
import {
|
|
6
|
+
projectCacheControlledToolOutputsToText,
|
|
7
|
+
projectComputerCallOutputsToText,
|
|
8
|
+
projectOpenAIChatToolMessageContent,
|
|
9
|
+
projectOpenAIResponsesToolMessageContent,
|
|
10
|
+
projectOpenRouterToolMessageContent,
|
|
11
|
+
projectSingleTextToolOutputsToText,
|
|
12
|
+
projectStructuredToolOutputsToText,
|
|
13
|
+
projectToolStreamContentForProvider,
|
|
14
|
+
} from '@/messages/core';
|
|
15
|
+
import {
|
|
16
|
+
coalesceAdjacentUserTurns,
|
|
17
|
+
strictAlternationProviders,
|
|
18
|
+
appendPredecessorHandoffCue,
|
|
19
|
+
removePredecessorHandoffCue,
|
|
20
|
+
} from '@/messages';
|
|
21
|
+
import {
|
|
22
|
+
stripAnthropicCacheControl,
|
|
23
|
+
stripBedrockCacheControl,
|
|
24
|
+
} from '@/messages/cache';
|
|
25
|
+
import { annotateMessagesForLLM } from '@/tools/toolOutputReferences';
|
|
26
|
+
import { Providers } from '@/common';
|
|
27
|
+
import { isAnthropicLike, isOpenAILike } from '@/utils/llm';
|
|
28
|
+
|
|
29
|
+
const preparedProviderRequestBrand = Symbol('PreparedProviderRequest');
|
|
30
|
+
|
|
31
|
+
export type ProviderMessageProjectionMode =
|
|
32
|
+
| 'chat-messages'
|
|
33
|
+
| 'openai-responses';
|
|
34
|
+
|
|
35
|
+
export interface ProviderPayloadMeasurement {
|
|
36
|
+
readonly fits: boolean;
|
|
37
|
+
readonly projectedMessageTokens?: number;
|
|
38
|
+
readonly availableMessageTokens?: number;
|
|
39
|
+
readonly contextBudget?: number;
|
|
40
|
+
readonly effectiveInstructionTokens?: number;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface PreparedProviderRequest {
|
|
44
|
+
readonly model: t.ChatModel;
|
|
45
|
+
readonly modelId?: string;
|
|
46
|
+
readonly provider: Providers;
|
|
47
|
+
readonly projectionMode: ProviderMessageProjectionMode;
|
|
48
|
+
readonly messages: BaseMessage[];
|
|
49
|
+
readonly measurement?: ProviderPayloadMeasurement;
|
|
50
|
+
readonly [preparedProviderRequestBrand]: true;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
type PreparedProviderRequestData = Omit<
|
|
54
|
+
PreparedProviderRequest,
|
|
55
|
+
typeof preparedProviderRequestBrand
|
|
56
|
+
>;
|
|
57
|
+
|
|
58
|
+
export interface ProviderRequestContext {
|
|
59
|
+
getOrCreateToolOutputRegistry?(): ToolOutputReferenceRegistry | undefined;
|
|
60
|
+
isRunProducedMessage?(message: BaseMessage): boolean;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export interface PrepareProviderRequestParams {
|
|
64
|
+
model: t.ChatModel;
|
|
65
|
+
messages: BaseMessage[];
|
|
66
|
+
provider: Providers;
|
|
67
|
+
context?: ProviderRequestContext;
|
|
68
|
+
config?: RunnableConfig;
|
|
69
|
+
maxToolResultChars?: number;
|
|
70
|
+
measure?: (messages: BaseMessage[]) => ProviderPayloadMeasurement;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export function usesNativeOpenAIResponses(
|
|
74
|
+
model: t.ChatModel,
|
|
75
|
+
provider: Providers,
|
|
76
|
+
callOptions?: unknown
|
|
77
|
+
): boolean {
|
|
78
|
+
if (!isOpenAILike(provider)) {
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
let candidate: unknown = model;
|
|
82
|
+
let effectiveCallOptions = callOptions;
|
|
83
|
+
const seen = new Set<object>();
|
|
84
|
+
for (let depth = 0; depth < 20; depth++) {
|
|
85
|
+
if (candidate == null || typeof candidate !== 'object') {
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
88
|
+
if (seen.has(candidate)) {
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
seen.add(candidate);
|
|
92
|
+
const runnable = candidate as {
|
|
93
|
+
_useResponsesApi?: (options?: unknown) => boolean;
|
|
94
|
+
bound?: unknown;
|
|
95
|
+
defaultOptions?: unknown;
|
|
96
|
+
last?: unknown;
|
|
97
|
+
constructor?: { name?: unknown };
|
|
98
|
+
};
|
|
99
|
+
try {
|
|
100
|
+
if (
|
|
101
|
+
runnable.defaultOptions != null &&
|
|
102
|
+
typeof runnable.defaultOptions === 'object' &&
|
|
103
|
+
!Array.isArray(runnable.defaultOptions) &&
|
|
104
|
+
effectiveCallOptions != null &&
|
|
105
|
+
typeof effectiveCallOptions === 'object' &&
|
|
106
|
+
!Array.isArray(effectiveCallOptions)
|
|
107
|
+
) {
|
|
108
|
+
effectiveCallOptions = {
|
|
109
|
+
...(runnable.defaultOptions as Record<string, unknown>),
|
|
110
|
+
...(effectiveCallOptions as Record<string, unknown>),
|
|
111
|
+
};
|
|
112
|
+
} else if (effectiveCallOptions == null) {
|
|
113
|
+
effectiveCallOptions = runnable.defaultOptions;
|
|
114
|
+
}
|
|
115
|
+
if (
|
|
116
|
+
runnable._useResponsesApi?.(effectiveCallOptions) === true ||
|
|
117
|
+
runnable._useResponsesApi?.(undefined) === true
|
|
118
|
+
) {
|
|
119
|
+
return true;
|
|
120
|
+
}
|
|
121
|
+
} catch {
|
|
122
|
+
// Continue through RunnableSequence/RunnableBinding wrappers.
|
|
123
|
+
}
|
|
124
|
+
if (
|
|
125
|
+
typeof runnable.constructor?.name === 'string' &&
|
|
126
|
+
runnable.constructor.name.includes('Responses')
|
|
127
|
+
) {
|
|
128
|
+
return true;
|
|
129
|
+
}
|
|
130
|
+
if (runnable.last != null && typeof runnable.last === 'object') {
|
|
131
|
+
candidate = runnable.last;
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
if (runnable.bound != null && typeof runnable.bound === 'object') {
|
|
135
|
+
candidate = runnable.bound;
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
138
|
+
return false;
|
|
139
|
+
}
|
|
140
|
+
return false;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function resolveProviderMessageProjectionMode(
|
|
144
|
+
model: t.ChatModel,
|
|
145
|
+
provider: Providers,
|
|
146
|
+
callOptions?: unknown
|
|
147
|
+
): ProviderMessageProjectionMode {
|
|
148
|
+
return usesNativeOpenAIResponses(model, provider, callOptions)
|
|
149
|
+
? 'openai-responses'
|
|
150
|
+
: 'chat-messages';
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
interface ProjectMessagesForProviderParams {
|
|
154
|
+
model: t.ChatModel;
|
|
155
|
+
messages: BaseMessage[];
|
|
156
|
+
provider: Providers;
|
|
157
|
+
maxToolResultChars?: number;
|
|
158
|
+
callOptions?: unknown;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function projectMessagesForProviderMode({
|
|
162
|
+
messages,
|
|
163
|
+
provider,
|
|
164
|
+
maxToolResultChars,
|
|
165
|
+
}: ProjectMessagesForProviderParams,
|
|
166
|
+
projectionMode: ProviderMessageProjectionMode
|
|
167
|
+
): BaseMessage[] {
|
|
168
|
+
const nativeOpenAIResponses = projectionMode === 'openai-responses';
|
|
169
|
+
const providerInputMessages = projectToolStreamContentForProvider(
|
|
170
|
+
messages,
|
|
171
|
+
nativeOpenAIResponses ? 'native' : 'fallback',
|
|
172
|
+
maxToolResultChars
|
|
173
|
+
);
|
|
174
|
+
if (nativeOpenAIResponses) {
|
|
175
|
+
return projectOpenAIResponsesToolMessageContent(
|
|
176
|
+
stripAnthropicCacheControl(
|
|
177
|
+
stripBedrockCacheControl(providerInputMessages)
|
|
178
|
+
),
|
|
179
|
+
maxToolResultChars
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
if (provider === Providers.OPENROUTER) {
|
|
183
|
+
return projectComputerCallOutputsToText(
|
|
184
|
+
projectOpenRouterToolMessageContent(
|
|
185
|
+
stripBedrockCacheControl(providerInputMessages),
|
|
186
|
+
maxToolResultChars
|
|
187
|
+
)
|
|
188
|
+
);
|
|
189
|
+
}
|
|
190
|
+
if (isOpenAILike(provider)) {
|
|
191
|
+
return projectComputerCallOutputsToText(
|
|
192
|
+
projectOpenAIChatToolMessageContent(
|
|
193
|
+
stripAnthropicCacheControl(
|
|
194
|
+
stripBedrockCacheControl(providerInputMessages)
|
|
195
|
+
),
|
|
196
|
+
maxToolResultChars
|
|
197
|
+
)
|
|
198
|
+
);
|
|
199
|
+
}
|
|
200
|
+
if (provider === Providers.ANTHROPIC) {
|
|
201
|
+
return projectComputerCallOutputsToText(
|
|
202
|
+
projectSingleTextToolOutputsToText(
|
|
203
|
+
stripBedrockCacheControl(providerInputMessages),
|
|
204
|
+
maxToolResultChars
|
|
205
|
+
)
|
|
206
|
+
);
|
|
207
|
+
}
|
|
208
|
+
if (provider === Providers.BEDROCK) {
|
|
209
|
+
return stripAnthropicCacheControl(
|
|
210
|
+
projectComputerCallOutputsToText(
|
|
211
|
+
projectCacheControlledToolOutputsToText(
|
|
212
|
+
providerInputMessages,
|
|
213
|
+
maxToolResultChars
|
|
214
|
+
)
|
|
215
|
+
)
|
|
216
|
+
);
|
|
217
|
+
}
|
|
218
|
+
return projectComputerCallOutputsToText(
|
|
219
|
+
projectStructuredToolOutputsToText(
|
|
220
|
+
projectSingleTextToolOutputsToText(
|
|
221
|
+
stripAnthropicCacheControl(
|
|
222
|
+
stripBedrockCacheControl(providerInputMessages)
|
|
223
|
+
),
|
|
224
|
+
maxToolResultChars
|
|
225
|
+
),
|
|
226
|
+
maxToolResultChars
|
|
227
|
+
)
|
|
228
|
+
);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/** Produces the provider-facing representation before adapter serialization. */
|
|
232
|
+
export function projectMessagesForProvider(
|
|
233
|
+
params: ProjectMessagesForProviderParams
|
|
234
|
+
): BaseMessage[] {
|
|
235
|
+
return projectMessagesForProviderMode(
|
|
236
|
+
params,
|
|
237
|
+
resolveProviderMessageProjectionMode(
|
|
238
|
+
params.model,
|
|
239
|
+
params.provider,
|
|
240
|
+
params.callOptions
|
|
241
|
+
)
|
|
242
|
+
);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/** Reads the serving model id through LangChain binding/sequence wrappers. */
|
|
246
|
+
export function resolveServingModelId(model: unknown): string | undefined {
|
|
247
|
+
const seen = new Set<unknown>();
|
|
248
|
+
let current: unknown = model;
|
|
249
|
+
while (current != null && typeof current === 'object' && !seen.has(current)) {
|
|
250
|
+
seen.add(current);
|
|
251
|
+
const wrapper = current as {
|
|
252
|
+
model?: unknown;
|
|
253
|
+
bound?: unknown;
|
|
254
|
+
last?: unknown;
|
|
255
|
+
steps?: unknown[];
|
|
256
|
+
};
|
|
257
|
+
if (typeof wrapper.model === 'string' && wrapper.model !== '') {
|
|
258
|
+
return wrapper.model;
|
|
259
|
+
}
|
|
260
|
+
current =
|
|
261
|
+
wrapper.bound ??
|
|
262
|
+
wrapper.last ??
|
|
263
|
+
(Array.isArray(wrapper.steps)
|
|
264
|
+
? wrapper.steps[wrapper.steps.length - 1]
|
|
265
|
+
: undefined);
|
|
266
|
+
}
|
|
267
|
+
return undefined;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* Finalizes one provider request and measures the exact message array that
|
|
272
|
+
* will be passed to LangChain. Source messages remain untouched.
|
|
273
|
+
*/
|
|
274
|
+
export function prepareProviderRequest({
|
|
275
|
+
model,
|
|
276
|
+
messages,
|
|
277
|
+
provider,
|
|
278
|
+
context,
|
|
279
|
+
config,
|
|
280
|
+
maxToolResultChars,
|
|
281
|
+
measure,
|
|
282
|
+
}: PrepareProviderRequestParams): PreparedProviderRequest {
|
|
283
|
+
const projectionMode = resolveProviderMessageProjectionMode(
|
|
284
|
+
model,
|
|
285
|
+
provider,
|
|
286
|
+
config
|
|
287
|
+
);
|
|
288
|
+
const projected = projectMessagesForProviderMode(
|
|
289
|
+
{
|
|
290
|
+
model,
|
|
291
|
+
messages,
|
|
292
|
+
provider,
|
|
293
|
+
maxToolResultChars,
|
|
294
|
+
callOptions: config,
|
|
295
|
+
},
|
|
296
|
+
projectionMode
|
|
297
|
+
);
|
|
298
|
+
const registry = context?.getOrCreateToolOutputRegistry?.();
|
|
299
|
+
const runId = config?.configurable?.run_id as string | undefined;
|
|
300
|
+
const annotated = annotateMessagesForLLM(projected, registry, runId);
|
|
301
|
+
const isRunProduced = context?.isRunProducedMessage;
|
|
302
|
+
const modelId = resolveServingModelId(model);
|
|
303
|
+
const cued = isAnthropicLike(provider, {
|
|
304
|
+
model: modelId,
|
|
305
|
+
})
|
|
306
|
+
? appendPredecessorHandoffCue(
|
|
307
|
+
annotated,
|
|
308
|
+
isRunProduced == null
|
|
309
|
+
? undefined
|
|
310
|
+
: (message): boolean => isRunProduced.call(context, message)
|
|
311
|
+
)
|
|
312
|
+
: removePredecessorHandoffCue(annotated);
|
|
313
|
+
const preparedMessages = strictAlternationProviders.has(provider)
|
|
314
|
+
? coalesceAdjacentUserTurns(cued)
|
|
315
|
+
: cued;
|
|
316
|
+
|
|
317
|
+
const request: PreparedProviderRequestData = {
|
|
318
|
+
model,
|
|
319
|
+
modelId,
|
|
320
|
+
provider,
|
|
321
|
+
projectionMode,
|
|
322
|
+
messages: preparedMessages,
|
|
323
|
+
measurement: measure?.(preparedMessages),
|
|
324
|
+
};
|
|
325
|
+
Object.defineProperty(request, preparedProviderRequestBrand, {
|
|
326
|
+
value: true,
|
|
327
|
+
enumerable: false,
|
|
328
|
+
});
|
|
329
|
+
return Object.freeze(request) as PreparedProviderRequest;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
export function assertPreparedProviderRequestFor(
|
|
333
|
+
request: PreparedProviderRequest,
|
|
334
|
+
model: t.ChatModel,
|
|
335
|
+
provider: Providers,
|
|
336
|
+
config?: RunnableConfig
|
|
337
|
+
): void {
|
|
338
|
+
if (
|
|
339
|
+
!Object.prototype.hasOwnProperty.call(request, preparedProviderRequestBrand)
|
|
340
|
+
) {
|
|
341
|
+
throw new Error('Invalid prepared provider request');
|
|
342
|
+
}
|
|
343
|
+
if (request.model !== model) {
|
|
344
|
+
throw new Error('Prepared provider request does not match serving model');
|
|
345
|
+
}
|
|
346
|
+
if (request.provider !== provider) {
|
|
347
|
+
throw new Error('Prepared provider request does not match serving provider');
|
|
348
|
+
}
|
|
349
|
+
if (
|
|
350
|
+
request.projectionMode !==
|
|
351
|
+
resolveProviderMessageProjectionMode(model, provider, config)
|
|
352
|
+
) {
|
|
353
|
+
throw new Error(
|
|
354
|
+
'Prepared provider request does not match invocation options'
|
|
355
|
+
);
|
|
356
|
+
}
|
|
357
|
+
}
|
package/src/messages/content.ts
CHANGED
|
@@ -3,6 +3,7 @@ import type {
|
|
|
3
3
|
MessageContentComplex,
|
|
4
4
|
} from '@langchain/core/messages';
|
|
5
5
|
import { ContentTypes } from '@/common';
|
|
6
|
+
import { cloneMessage } from './cache';
|
|
6
7
|
|
|
7
8
|
/**
|
|
8
9
|
* Whether {@link formatContentStrings} will flatten this message's content:
|
|
@@ -42,13 +43,12 @@ export const formatContentStrings = (
|
|
|
42
43
|
const blocks = message.content as MessageContentComplex[];
|
|
43
44
|
const content = blocks.reduce((acc, curr) => {
|
|
44
45
|
if (curr.type === ContentTypes.TEXT) {
|
|
45
|
-
return `${acc}${curr[ContentTypes.TEXT]
|
|
46
|
+
return `${acc}${curr[ContentTypes.TEXT] ?? ''}\n`;
|
|
46
47
|
}
|
|
47
48
|
return acc;
|
|
48
49
|
}, '');
|
|
49
50
|
|
|
50
|
-
message
|
|
51
|
-
result.push(message);
|
|
51
|
+
result.push(cloneMessage(message, content.trim()));
|
|
52
52
|
}
|
|
53
53
|
|
|
54
54
|
return result;
|
package/src/stream.ts
CHANGED
|
@@ -820,6 +820,12 @@ function startEagerToolExecutions(args: {
|
|
|
820
820
|
toolCalls: entries.map((entry) => entry.request),
|
|
821
821
|
userId: graph.config?.configurable?.user_id as string | undefined,
|
|
822
822
|
agentId: agentContext?.agentId,
|
|
823
|
+
callerCapabilityProjection:
|
|
824
|
+
(
|
|
825
|
+
agentContext as
|
|
826
|
+
| Partial<Pick<AgentContext, 'getCallerCapabilityProjectionSnapshot'>>
|
|
827
|
+
| undefined
|
|
828
|
+
)?.getCallerCapabilityProjectionSnapshot?.(),
|
|
823
829
|
configurable: graph.config?.configurable as
|
|
824
830
|
| Record<string, unknown>
|
|
825
831
|
| undefined,
|
|
@@ -14,6 +14,74 @@ export type CallerCapabilityProjection = {
|
|
|
14
14
|
codeExecutionOnlyTools: t.LCTool[];
|
|
15
15
|
};
|
|
16
16
|
|
|
17
|
+
/**
|
|
18
|
+
* Combines event schemas with runtime capability metadata. Matching runtime
|
|
19
|
+
* entries override caller/defer policy without replacing the event schema;
|
|
20
|
+
* runtime-only definitions are appended intact.
|
|
21
|
+
*/
|
|
22
|
+
export function mergeCallerCapabilityDefinitions(
|
|
23
|
+
toolDefs: Iterable<t.LCTool> | null | undefined,
|
|
24
|
+
overrides: Iterable<t.LCTool> | null | undefined
|
|
25
|
+
): t.LCTool[] {
|
|
26
|
+
const runtimeDefinitions = Array.from(overrides ?? []);
|
|
27
|
+
const merged = new Map(
|
|
28
|
+
applyCallerCapabilityDefinitionOverrides(
|
|
29
|
+
toolDefs ?? [],
|
|
30
|
+
runtimeDefinitions
|
|
31
|
+
).map((toolDef) => [toolDef.name, toolDef])
|
|
32
|
+
);
|
|
33
|
+
for (const override of runtimeDefinitions) {
|
|
34
|
+
if (!merged.has(override.name)) {
|
|
35
|
+
merged.set(override.name, override);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return Array.from(merged.values());
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Applies runtime caller metadata to schema-only event definitions without
|
|
43
|
+
* adding registry-only tools or replacing their model-facing schemas.
|
|
44
|
+
*/
|
|
45
|
+
export function applyCallerCapabilityDefinitionOverrides(
|
|
46
|
+
toolDefs: Iterable<t.LCTool>,
|
|
47
|
+
overrides: Iterable<t.LCTool> | null | undefined
|
|
48
|
+
): t.LCTool[] {
|
|
49
|
+
const overridesByName = new Map<string, t.LCTool>();
|
|
50
|
+
for (const override of overrides ?? []) {
|
|
51
|
+
overridesByName.set(override.name, override);
|
|
52
|
+
}
|
|
53
|
+
return Array.from(toolDefs, (toolDef) => {
|
|
54
|
+
const override = overridesByName.get(toolDef.name);
|
|
55
|
+
if (override == null) {
|
|
56
|
+
return toolDef;
|
|
57
|
+
}
|
|
58
|
+
return {
|
|
59
|
+
...toolDef,
|
|
60
|
+
allowed_callers: override.allowed_callers,
|
|
61
|
+
defer_loading: override.defer_loading,
|
|
62
|
+
};
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** Converts the live projection into the versioned event transport shape. */
|
|
67
|
+
export function createCallerCapabilityProjectionSnapshot(
|
|
68
|
+
projection: CallerCapabilityProjection
|
|
69
|
+
): t.CallerCapabilityProjectionSnapshot {
|
|
70
|
+
return {
|
|
71
|
+
version: 1,
|
|
72
|
+
directToolNames: projection.directTools.map((toolDef) => toolDef.name),
|
|
73
|
+
codeExecutionToolNames: projection.codeExecutionTools.map(
|
|
74
|
+
(toolDef) => toolDef.name
|
|
75
|
+
),
|
|
76
|
+
directOnlyToolNames: projection.directOnlyTools.map(
|
|
77
|
+
(toolDef) => toolDef.name
|
|
78
|
+
),
|
|
79
|
+
codeExecutionOnlyToolNames: projection.codeExecutionOnlyTools.map(
|
|
80
|
+
(toolDef) => toolDef.name
|
|
81
|
+
),
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
|
|
17
85
|
export function getAllowedCallers(
|
|
18
86
|
toolDef: t.LCTool
|
|
19
87
|
): readonly t.AllowedCaller[] {
|
package/src/tools/ToolNode.ts
CHANGED
|
@@ -95,8 +95,11 @@ import {
|
|
|
95
95
|
resolveLocalExecutionTools,
|
|
96
96
|
} from '@/tools/local';
|
|
97
97
|
import {
|
|
98
|
+
type CallerCapabilityProjection,
|
|
99
|
+
createCallerCapabilityProjectionSnapshot,
|
|
98
100
|
isToolDefinitionActive,
|
|
99
101
|
isProgrammaticControlTool,
|
|
102
|
+
mergeCallerCapabilityDefinitions,
|
|
100
103
|
resolveCallerCapabilityProjection,
|
|
101
104
|
} from '@/tools/CallerCapabilities';
|
|
102
105
|
import { stripCodeSessionFileSummary } from '@/tools/CodeSessionFileSummary';
|
|
@@ -698,6 +701,10 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
698
701
|
>();
|
|
699
702
|
/** Tool registry for filtering (lazy computation of programmatic maps) */
|
|
700
703
|
private toolRegistry?: t.LCToolRegistry;
|
|
704
|
+
/** Schema-only definitions used when event mode has no runtime registry. */
|
|
705
|
+
private toolDefinitions?: t.LCToolRegistry;
|
|
706
|
+
/** Tool-map entries created or replaced by the local execution resolver. */
|
|
707
|
+
private localImplementationNames = new Set<string>();
|
|
701
708
|
/** Reads deferred-tool discovery state from the owning agent context. */
|
|
702
709
|
private getDiscoveredToolNames?: () => readonly string[];
|
|
703
710
|
/** Reference to Graph's sessions map for automatic session injection */
|
|
@@ -803,6 +810,7 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
803
810
|
handleToolErrors,
|
|
804
811
|
loadRuntimeTools,
|
|
805
812
|
toolRegistry,
|
|
813
|
+
toolDefinitions,
|
|
806
814
|
getDiscoveredToolNames,
|
|
807
815
|
sessions,
|
|
808
816
|
codeSessionKey,
|
|
@@ -879,6 +887,7 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
879
887
|
toolRegistry,
|
|
880
888
|
toolExecution,
|
|
881
889
|
});
|
|
890
|
+
this.toolDefinitions = toolDefinitions;
|
|
882
891
|
this.getDiscoveredToolNames = getDiscoveredToolNames;
|
|
883
892
|
this.sessions = sessions;
|
|
884
893
|
this.codeSessionKey = codeSessionKey ?? Constants.EXECUTE_CODE;
|
|
@@ -999,6 +1008,7 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
999
1008
|
});
|
|
1000
1009
|
|
|
1001
1010
|
this.toolMap = resolved.toolMap;
|
|
1011
|
+
this.localImplementationNames = resolved.localImplementationNames;
|
|
1002
1012
|
if (resolved.fileCheckpointer != null) {
|
|
1003
1013
|
this.fileCheckpointer = resolved.fileCheckpointer;
|
|
1004
1014
|
}
|
|
@@ -1105,27 +1115,58 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
1105
1115
|
this.settledInterruptingResults.clear();
|
|
1106
1116
|
}
|
|
1107
1117
|
|
|
1118
|
+
/** Returns the live caller projection used by direct and event execution. */
|
|
1119
|
+
private getCallerCapabilityProjection(): CallerCapabilityProjection {
|
|
1120
|
+
const discoveredToolNames = new Set(
|
|
1121
|
+
this.getDiscoveredToolNames?.() ?? []
|
|
1122
|
+
);
|
|
1123
|
+
return resolveCallerCapabilityProjection(
|
|
1124
|
+
mergeCallerCapabilityDefinitions(
|
|
1125
|
+
this.toolDefinitions?.values(),
|
|
1126
|
+
this.toolRegistry?.values()
|
|
1127
|
+
),
|
|
1128
|
+
(toolDef) => isToolDefinitionActive(toolDef, discoveredToolNames)
|
|
1129
|
+
);
|
|
1130
|
+
}
|
|
1131
|
+
|
|
1132
|
+
/** Serializes the live caller projection for event-driven hosts. */
|
|
1133
|
+
private getCallerCapabilityProjectionSnapshot(): t.CallerCapabilityProjectionSnapshot {
|
|
1134
|
+
return createCallerCapabilityProjectionSnapshot(
|
|
1135
|
+
this.getCallerCapabilityProjection()
|
|
1136
|
+
);
|
|
1137
|
+
}
|
|
1138
|
+
|
|
1108
1139
|
/** Returns active tools projected by their effective caller capabilities. */
|
|
1109
1140
|
private getProgrammaticTools(): t.ProgrammaticCache {
|
|
1110
1141
|
const toolMap: t.ToolMap = new Map();
|
|
1142
|
+
const toolDefs: t.LCTool[] = [];
|
|
1111
1143
|
const discoveredToolNames = new Set(
|
|
1112
1144
|
this.getDiscoveredToolNames?.() ?? []
|
|
1113
1145
|
);
|
|
1114
|
-
const
|
|
1146
|
+
const executableCapabilities = resolveCallerCapabilityProjection(
|
|
1115
1147
|
this.toolRegistry?.values() ?? [],
|
|
1116
1148
|
(toolDef) => isToolDefinitionActive(toolDef, discoveredToolNames)
|
|
1117
1149
|
);
|
|
1118
|
-
for (const toolDef of
|
|
1150
|
+
for (const toolDef of executableCapabilities.codeExecutionTools) {
|
|
1151
|
+
if (
|
|
1152
|
+
this.eventDrivenMode &&
|
|
1153
|
+
this.directToolNames?.has(toolDef.name) !== true &&
|
|
1154
|
+
!this.localImplementationNames.has(toolDef.name)
|
|
1155
|
+
) {
|
|
1156
|
+
continue;
|
|
1157
|
+
}
|
|
1119
1158
|
const tool = this.toolMap.get(toolDef.name);
|
|
1120
1159
|
if (tool != null) {
|
|
1121
1160
|
toolMap.set(toolDef.name, tool);
|
|
1161
|
+
toolDefs.push(toolDef);
|
|
1122
1162
|
}
|
|
1123
1163
|
}
|
|
1164
|
+
const activeCapabilities = this.getCallerCapabilityProjection();
|
|
1124
1165
|
|
|
1125
1166
|
return {
|
|
1126
1167
|
toolMap,
|
|
1127
|
-
toolDefs
|
|
1128
|
-
disallowedToolDefs:
|
|
1168
|
+
toolDefs,
|
|
1169
|
+
disallowedToolDefs: activeCapabilities.directOnlyTools
|
|
1129
1170
|
.filter((toolDef) => !isProgrammaticControlTool(toolDef.name))
|
|
1130
1171
|
.map((toolDef) => ({ name: toolDef.name })),
|
|
1131
1172
|
};
|
|
@@ -3455,6 +3496,8 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
3455
3496
|
// the eager path sends `agentContext.agentId` — this must
|
|
3456
3497
|
// match it at the top level too.
|
|
3457
3498
|
agentId: this.executingAgentId,
|
|
3499
|
+
callerCapabilityProjection:
|
|
3500
|
+
this.getCallerCapabilityProjectionSnapshot(),
|
|
3458
3501
|
configurable: stripRunBreakerScope(
|
|
3459
3502
|
config.configurable as Record<string, unknown> | undefined
|
|
3460
3503
|
),
|