@librechat/agents 3.2.33 → 3.2.34

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.
Files changed (57) hide show
  1. package/dist/cjs/llm/bedrock/index.cjs +21 -2
  2. package/dist/cjs/llm/bedrock/index.cjs.map +1 -1
  3. package/dist/cjs/llm/bedrock/utils/message_outputs.cjs +38 -2
  4. package/dist/cjs/llm/bedrock/utils/message_outputs.cjs.map +1 -1
  5. package/dist/cjs/llm/google/utils/common.cjs +6 -0
  6. package/dist/cjs/llm/google/utils/common.cjs.map +1 -1
  7. package/dist/cjs/llm/openai/index.cjs +48 -1
  8. package/dist/cjs/llm/openai/index.cjs.map +1 -1
  9. package/dist/cjs/llm/vertexai/index.cjs +19 -0
  10. package/dist/cjs/llm/vertexai/index.cjs.map +1 -1
  11. package/dist/cjs/stream.cjs +20 -2
  12. package/dist/cjs/stream.cjs.map +1 -1
  13. package/dist/cjs/tools/ToolNode.cjs +41 -4
  14. package/dist/cjs/tools/ToolNode.cjs.map +1 -1
  15. package/dist/cjs/tools/streamedToolCallSeals.cjs +30 -1
  16. package/dist/cjs/tools/streamedToolCallSeals.cjs.map +1 -1
  17. package/dist/esm/llm/bedrock/index.mjs +22 -3
  18. package/dist/esm/llm/bedrock/index.mjs.map +1 -1
  19. package/dist/esm/llm/bedrock/utils/message_outputs.mjs +38 -3
  20. package/dist/esm/llm/bedrock/utils/message_outputs.mjs.map +1 -1
  21. package/dist/esm/llm/google/utils/common.mjs +6 -0
  22. package/dist/esm/llm/google/utils/common.mjs.map +1 -1
  23. package/dist/esm/llm/openai/index.mjs +48 -1
  24. package/dist/esm/llm/openai/index.mjs.map +1 -1
  25. package/dist/esm/llm/vertexai/index.mjs +19 -0
  26. package/dist/esm/llm/vertexai/index.mjs.map +1 -1
  27. package/dist/esm/stream.mjs +21 -3
  28. package/dist/esm/stream.mjs.map +1 -1
  29. package/dist/esm/tools/ToolNode.mjs +41 -4
  30. package/dist/esm/tools/ToolNode.mjs.map +1 -1
  31. package/dist/esm/tools/streamedToolCallSeals.mjs +25 -2
  32. package/dist/esm/tools/streamedToolCallSeals.mjs.map +1 -1
  33. package/dist/types/llm/bedrock/utils/index.d.ts +1 -1
  34. package/dist/types/llm/bedrock/utils/message_outputs.d.ts +9 -0
  35. package/dist/types/llm/vertexai/index.d.ts +10 -0
  36. package/dist/types/tools/ToolNode.d.ts +8 -0
  37. package/dist/types/tools/streamedToolCallSeals.d.ts +5 -1
  38. package/dist/types/types/tools.d.ts +10 -0
  39. package/package.json +1 -1
  40. package/src/__tests__/stream.eagerEventExecution.test.ts +703 -0
  41. package/src/llm/bedrock/index.ts +40 -0
  42. package/src/llm/bedrock/streamSealDispatch.test.ts +158 -0
  43. package/src/llm/bedrock/utils/index.ts +1 -0
  44. package/src/llm/bedrock/utils/message_outputs.test.ts +85 -0
  45. package/src/llm/bedrock/utils/message_outputs.ts +43 -0
  46. package/src/llm/google/utils/common.test.ts +64 -0
  47. package/src/llm/google/utils/common.ts +18 -0
  48. package/src/llm/openai/index.ts +95 -1
  49. package/src/llm/openai/sequentialToolCallSeals.test.ts +199 -0
  50. package/src/llm/vertexai/index.ts +31 -0
  51. package/src/llm/vertexai/sealStreamedToolCalls.test.ts +88 -0
  52. package/src/llm/vertexai/streamSealDispatch.test.ts +148 -0
  53. package/src/stream.ts +40 -6
  54. package/src/tools/ToolNode.ts +85 -3
  55. package/src/tools/__tests__/ToolNode.onResultCompletion.test.ts +368 -0
  56. package/src/tools/streamedToolCallSeals.ts +37 -9
  57. package/src/types/tools.ts +10 -0
@@ -1,7 +1,30 @@
1
+ //#region src/tools/streamedToolCallSeals.ts
2
+ const STREAMED_TOOL_CALL_ADAPTER_METADATA_KEY = "lc_streamed_tool_call_adapter";
1
3
  const STREAMED_TOOL_CALL_SEAL_METADATA_KEY = "lc_streamed_tool_call_seal";
2
4
  const OPENAI_RESPONSES_STREAMED_TOOL_CALL_ADAPTER = "openai_responses";
5
+ const BEDROCK_CONVERSE_STREAMED_TOOL_CALL_ADAPTER = "bedrock_converse";
6
+ const GOOGLE_STREAMED_TOOL_CALL_ADAPTER = "google_genai";
7
+ const OPENAI_CHAT_SEQUENTIAL_STREAMED_TOOL_CALL_ADAPTER = "openai_chat_sequential";
8
+ const STREAMED_TOOL_CALL_ADAPTERS = new Set([
9
+ OPENAI_RESPONSES_STREAMED_TOOL_CALL_ADAPTER,
10
+ BEDROCK_CONVERSE_STREAMED_TOOL_CALL_ADAPTER,
11
+ GOOGLE_STREAMED_TOOL_CALL_ADAPTER,
12
+ OPENAI_CHAT_SEQUENTIAL_STREAMED_TOOL_CALL_ADAPTER
13
+ ]);
14
+ /**
15
+ * Adapters whose wire protocol streams tool calls strictly sequentially by
16
+ * index, so a prior call is sealed the moment a later index begins. Used by
17
+ * the stream handler to extend next-index sealing beyond the provider-keyed
18
+ * Anthropic allowlist.
19
+ */
20
+ const SEQUENTIAL_SEAL_STREAMED_TOOL_CALL_ADAPTERS = new Set([OPENAI_CHAT_SEQUENTIAL_STREAMED_TOOL_CALL_ADAPTER]);
21
+ function streamedToolCallAdapterAllowsSequentialSeal(metadata) {
22
+ const adapter = getStreamedToolCallAdapter(metadata);
23
+ return adapter != null && SEQUENTIAL_SEAL_STREAMED_TOOL_CALL_ADAPTERS.has(adapter);
24
+ }
3
25
  function getStreamedToolCallAdapter(metadata) {
4
- if (metadata?.["lc_streamed_tool_call_adapter"] === "openai_responses") return OPENAI_RESPONSES_STREAMED_TOOL_CALL_ADAPTER;
26
+ const adapter = metadata?.[STREAMED_TOOL_CALL_ADAPTER_METADATA_KEY];
27
+ if (typeof adapter === "string" && STREAMED_TOOL_CALL_ADAPTERS.has(adapter)) return adapter;
5
28
  }
6
29
  function getStreamedToolCallSeal(metadata) {
7
30
  const seal = metadata?.[STREAMED_TOOL_CALL_SEAL_METADATA_KEY];
@@ -19,6 +42,6 @@ function getStreamedToolCallSeal(metadata) {
19
42
  };
20
43
  }
21
44
  //#endregion
22
- export { getStreamedToolCallAdapter, getStreamedToolCallSeal };
45
+ export { BEDROCK_CONVERSE_STREAMED_TOOL_CALL_ADAPTER, GOOGLE_STREAMED_TOOL_CALL_ADAPTER, OPENAI_CHAT_SEQUENTIAL_STREAMED_TOOL_CALL_ADAPTER, STREAMED_TOOL_CALL_ADAPTER_METADATA_KEY, STREAMED_TOOL_CALL_SEAL_METADATA_KEY, getStreamedToolCallAdapter, getStreamedToolCallSeal, streamedToolCallAdapterAllowsSequentialSeal };
23
46
 
24
47
  //# sourceMappingURL=streamedToolCallSeals.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"streamedToolCallSeals.mjs","names":[],"sources":["../../../src/tools/streamedToolCallSeals.ts"],"sourcesContent":["export const STREAMED_TOOL_CALL_ADAPTER_METADATA_KEY =\n 'lc_streamed_tool_call_adapter';\nexport const STREAMED_TOOL_CALL_SEAL_METADATA_KEY =\n 'lc_streamed_tool_call_seal';\nexport const OPENAI_RESPONSES_STREAMED_TOOL_CALL_ADAPTER = 'openai_responses';\n\nexport type StreamedToolCallAdapter =\n typeof OPENAI_RESPONSES_STREAMED_TOOL_CALL_ADAPTER;\n\nexport type StreamedToolCallSeal =\n | {\n kind: 'single';\n id?: string;\n index?: number;\n }\n | {\n kind: 'all';\n };\n\nexport function getStreamedToolCallAdapter(\n metadata: Record<string, unknown> | undefined\n): StreamedToolCallAdapter | undefined {\n if (\n metadata?.[STREAMED_TOOL_CALL_ADAPTER_METADATA_KEY] ===\n OPENAI_RESPONSES_STREAMED_TOOL_CALL_ADAPTER\n ) {\n return OPENAI_RESPONSES_STREAMED_TOOL_CALL_ADAPTER;\n }\n return undefined;\n}\n\nexport function getStreamedToolCallSeal(\n metadata: Record<string, unknown> | undefined\n): StreamedToolCallSeal | undefined {\n const seal = metadata?.[STREAMED_TOOL_CALL_SEAL_METADATA_KEY];\n if (seal == null || typeof seal !== 'object') {\n return undefined;\n }\n if (!('kind' in seal)) {\n return undefined;\n }\n if (seal.kind === 'all') {\n return { kind: 'all' };\n }\n if (seal.kind !== 'single') {\n return undefined;\n }\n const id = 'id' in seal && typeof seal.id === 'string' ? seal.id : undefined;\n const index =\n 'index' in seal && typeof seal.index === 'number'\n ? seal.index\n : undefined;\n if (id == null && index == null) {\n return undefined;\n }\n return { kind: 'single', id, index };\n}\n"],"mappings":"AAEA,MAAa,uCACX;AACF,MAAa,8CAA8C;AAe3D,SAAgB,2BACd,UACqC;CACrC,IACE,WAAA,qCAAA,oBAGA,OAAO;AAGX;AAEA,SAAgB,wBACd,UACkC;CAClC,MAAM,OAAO,WAAW;CACxB,IAAI,QAAQ,QAAQ,OAAO,SAAS,UAClC;CAEF,IAAI,EAAE,UAAU,OACd;CAEF,IAAI,KAAK,SAAS,OAChB,OAAO,EAAE,MAAM,MAAM;CAEvB,IAAI,KAAK,SAAS,UAChB;CAEF,MAAM,KAAK,QAAQ,QAAQ,OAAO,KAAK,OAAO,WAAW,KAAK,KAAK,KAAA;CACnE,MAAM,QACJ,WAAW,QAAQ,OAAO,KAAK,UAAU,WACrC,KAAK,QACL,KAAA;CACN,IAAI,MAAM,QAAQ,SAAS,MACzB;CAEF,OAAO;EAAE,MAAM;EAAU;EAAI;CAAM;AACrC"}
1
+ {"version":3,"file":"streamedToolCallSeals.mjs","names":[],"sources":["../../../src/tools/streamedToolCallSeals.ts"],"sourcesContent":["export const STREAMED_TOOL_CALL_ADAPTER_METADATA_KEY =\n 'lc_streamed_tool_call_adapter';\nexport const STREAMED_TOOL_CALL_SEAL_METADATA_KEY =\n 'lc_streamed_tool_call_seal';\nexport const OPENAI_RESPONSES_STREAMED_TOOL_CALL_ADAPTER = 'openai_responses';\nexport const BEDROCK_CONVERSE_STREAMED_TOOL_CALL_ADAPTER = 'bedrock_converse';\nexport const GOOGLE_STREAMED_TOOL_CALL_ADAPTER = 'google_genai';\nexport const OPENAI_CHAT_SEQUENTIAL_STREAMED_TOOL_CALL_ADAPTER =\n 'openai_chat_sequential';\n\nexport type StreamedToolCallAdapter =\n | typeof OPENAI_RESPONSES_STREAMED_TOOL_CALL_ADAPTER\n | typeof BEDROCK_CONVERSE_STREAMED_TOOL_CALL_ADAPTER\n | typeof GOOGLE_STREAMED_TOOL_CALL_ADAPTER\n | typeof OPENAI_CHAT_SEQUENTIAL_STREAMED_TOOL_CALL_ADAPTER;\n\nconst STREAMED_TOOL_CALL_ADAPTERS: ReadonlySet<string> = new Set([\n OPENAI_RESPONSES_STREAMED_TOOL_CALL_ADAPTER,\n BEDROCK_CONVERSE_STREAMED_TOOL_CALL_ADAPTER,\n GOOGLE_STREAMED_TOOL_CALL_ADAPTER,\n OPENAI_CHAT_SEQUENTIAL_STREAMED_TOOL_CALL_ADAPTER,\n]);\n\n/**\n * Adapters whose wire protocol streams tool calls strictly sequentially by\n * index, so a prior call is sealed the moment a later index begins. Used by\n * the stream handler to extend next-index sealing beyond the provider-keyed\n * Anthropic allowlist.\n */\nconst SEQUENTIAL_SEAL_STREAMED_TOOL_CALL_ADAPTERS: ReadonlySet<string> =\n new Set([OPENAI_CHAT_SEQUENTIAL_STREAMED_TOOL_CALL_ADAPTER]);\n\nexport function streamedToolCallAdapterAllowsSequentialSeal(\n metadata: Record<string, unknown> | undefined\n): boolean {\n const adapter = getStreamedToolCallAdapter(metadata);\n return (\n adapter != null && SEQUENTIAL_SEAL_STREAMED_TOOL_CALL_ADAPTERS.has(adapter)\n );\n}\n\nexport type StreamedToolCallSeal =\n | {\n kind: 'single';\n id?: string;\n index?: number;\n }\n | {\n kind: 'all';\n };\n\nexport function getStreamedToolCallAdapter(\n metadata: Record<string, unknown> | undefined\n): StreamedToolCallAdapter | undefined {\n const adapter = metadata?.[STREAMED_TOOL_CALL_ADAPTER_METADATA_KEY];\n if (typeof adapter === 'string' && STREAMED_TOOL_CALL_ADAPTERS.has(adapter)) {\n return adapter as StreamedToolCallAdapter;\n }\n return undefined;\n}\n\nexport function getStreamedToolCallSeal(\n metadata: Record<string, unknown> | undefined\n): StreamedToolCallSeal | undefined {\n const seal = metadata?.[STREAMED_TOOL_CALL_SEAL_METADATA_KEY];\n if (seal == null || typeof seal !== 'object') {\n return undefined;\n }\n if (!('kind' in seal)) {\n return undefined;\n }\n if (seal.kind === 'all') {\n return { kind: 'all' };\n }\n if (seal.kind !== 'single') {\n return undefined;\n }\n const id = 'id' in seal && typeof seal.id === 'string' ? seal.id : undefined;\n const index =\n 'index' in seal && typeof seal.index === 'number' ? seal.index : undefined;\n if (id == null && index == null) {\n return undefined;\n }\n return { kind: 'single', id, index };\n}\n"],"mappings":";AAAA,MAAa,0CACX;AACF,MAAa,uCACX;AACF,MAAa,8CAA8C;AAC3D,MAAa,8CAA8C;AAC3D,MAAa,oCAAoC;AACjD,MAAa,oDACX;AAQF,MAAM,8BAAmD,IAAI,IAAI;CAC/D;CACA;CACA;CACA;AACF,CAAC;;;;;;;AAQD,MAAM,8CACJ,IAAI,IAAI,CAAC,iDAAiD,CAAC;AAE7D,SAAgB,4CACd,UACS;CACT,MAAM,UAAU,2BAA2B,QAAQ;CACnD,OACE,WAAW,QAAQ,4CAA4C,IAAI,OAAO;AAE9E;AAYA,SAAgB,2BACd,UACqC;CACrC,MAAM,UAAU,WAAW;CAC3B,IAAI,OAAO,YAAY,YAAY,4BAA4B,IAAI,OAAO,GACxE,OAAO;AAGX;AAEA,SAAgB,wBACd,UACkC;CAClC,MAAM,OAAO,WAAW;CACxB,IAAI,QAAQ,QAAQ,OAAO,SAAS,UAClC;CAEF,IAAI,EAAE,UAAU,OACd;CAEF,IAAI,KAAK,SAAS,OAChB,OAAO,EAAE,MAAM,MAAM;CAEvB,IAAI,KAAK,SAAS,UAChB;CAEF,MAAM,KAAK,QAAQ,QAAQ,OAAO,KAAK,OAAO,WAAW,KAAK,KAAK,KAAA;CACnE,MAAM,QACJ,WAAW,QAAQ,OAAO,KAAK,UAAU,WAAW,KAAK,QAAQ,KAAA;CACnE,IAAI,MAAM,QAAQ,SAAS,MACzB;CAEF,OAAO;EAAE,MAAM;EAAU;EAAI;CAAM;AACrC"}
@@ -2,4 +2,4 @@
2
2
  * Bedrock Converse utility exports.
3
3
  */
4
4
  export { convertToConverseMessages, extractImageInfo, langchainReasoningBlockToBedrockReasoningBlock, concatenateLangchainReasoningBlocks, } from './message_inputs';
5
- export { convertConverseMessageToLangChainMessage, handleConverseStreamContentBlockStart, handleConverseStreamContentBlockDelta, handleConverseStreamMetadata, bedrockReasoningBlockToLangchainReasoningBlock, bedrockReasoningDeltaToLangchainPartialReasoningBlock, } from './message_outputs';
5
+ export { convertConverseMessageToLangChainMessage, createConverseToolUseStopChunk, handleConverseStreamContentBlockStart, handleConverseStreamContentBlockDelta, handleConverseStreamMetadata, bedrockReasoningBlockToLangchainReasoningBlock, bedrockReasoningDeltaToLangchainPartialReasoningBlock, } from './message_outputs';
@@ -25,6 +25,15 @@ export declare function handleConverseStreamContentBlockDelta(contentBlockDelta:
25
25
  * Handle a content block start event from Bedrock Converse stream.
26
26
  */
27
27
  export declare function handleConverseStreamContentBlockStart(contentBlockStart: ContentBlockStartEvent): ChatGenerationChunk | null;
28
+ /**
29
+ * Build the chunk emitted when a Converse `contentBlockStop` event closes a
30
+ * toolUse block. The Converse protocol guarantees a block's input is complete
31
+ * at `contentBlockStop`, so this chunk carries an explicit streamed tool-call
32
+ * seal for that block index. The empty `args` delta merges as a no-op into the
33
+ * accumulated tool call; id/name are omitted so the chunk matches the existing
34
+ * entry purely by index.
35
+ */
36
+ export declare function createConverseToolUseStopChunk(contentBlockIndex: number): ChatGenerationChunk;
28
37
  /**
29
38
  * Handle a metadata event from Bedrock Converse stream.
30
39
  */
@@ -1,4 +1,5 @@
1
1
  import { ChatGoogle } from '@langchain/google-gauth';
2
+ import { AIMessageChunk } from '@langchain/core/messages';
2
3
  import type { GeminiContent, GoogleAIModelRequestParams, GoogleAbstractedClient } from '@langchain/google-common';
3
4
  import type { CallbackManagerForLLMRun } from '@langchain/core/callbacks/manager';
4
5
  import type { BaseMessage, UsageMetadata } from '@langchain/core/messages';
@@ -29,6 +30,15 @@ import type { GoogleThinkingConfig, VertexAIClientOptions } from '@/types';
29
30
  * overriding `_convertToUsageMetadata`.
30
31
  */
31
32
  export declare function repairStreamUsageMetadata(current: UsageMetadata | undefined, generationInfoUsage: UsageMetadata | undefined): UsageMetadata | undefined;
33
+ /**
34
+ * The Gemini API delivers function calls as complete objects — never as
35
+ * partial arg deltas. `@langchain/google-common` pre-parses each streamed
36
+ * functionCall part into `tool_calls` (invalid args land in
37
+ * `invalid_tool_calls` instead), so a chunk whose tool-call chunks all parsed
38
+ * cleanly is sealed on arrival for eager tool execution. Anything that fails
39
+ * the parse check is left unstamped and falls back to the lazy path.
40
+ */
41
+ export declare function sealCompleteStreamedToolCalls(message: AIMessageChunk): void;
32
42
  /**
33
43
  * Fixes thought signatures on functionCall parts in the formatted Gemini request.
34
44
  *
@@ -350,6 +350,14 @@ export declare class ToolNode<T = any> extends RunnableCallable<T, T> {
350
350
  */
351
351
  private dispatchPostToolBatchAndInjectContext;
352
352
  private dispatchStepCompleted;
353
+ /**
354
+ * Emits the completed run step for a single host-reported result before
355
+ * the batch resolves. Mirrors the batch loop's output formatting exactly;
356
+ * callers gate on the no-hooks/no-HITL configuration, so the raw result
357
+ * content here is also the final content. Returns whether the event was
358
+ * actually dispatched so the caller can fall back to batch-time emission.
359
+ */
360
+ private dispatchEarlyToolCompletion;
353
361
  /**
354
362
  * Converts InjectedMessage instances to LangChain HumanMessage objects.
355
363
  * Both 'user' and 'system' roles become HumanMessage to avoid provider
@@ -1,7 +1,11 @@
1
1
  export declare const STREAMED_TOOL_CALL_ADAPTER_METADATA_KEY = "lc_streamed_tool_call_adapter";
2
2
  export declare const STREAMED_TOOL_CALL_SEAL_METADATA_KEY = "lc_streamed_tool_call_seal";
3
3
  export declare const OPENAI_RESPONSES_STREAMED_TOOL_CALL_ADAPTER = "openai_responses";
4
- export type StreamedToolCallAdapter = typeof OPENAI_RESPONSES_STREAMED_TOOL_CALL_ADAPTER;
4
+ export declare const BEDROCK_CONVERSE_STREAMED_TOOL_CALL_ADAPTER = "bedrock_converse";
5
+ export declare const GOOGLE_STREAMED_TOOL_CALL_ADAPTER = "google_genai";
6
+ export declare const OPENAI_CHAT_SEQUENTIAL_STREAMED_TOOL_CALL_ADAPTER = "openai_chat_sequential";
7
+ export type StreamedToolCallAdapter = typeof OPENAI_RESPONSES_STREAMED_TOOL_CALL_ADAPTER | typeof BEDROCK_CONVERSE_STREAMED_TOOL_CALL_ADAPTER | typeof GOOGLE_STREAMED_TOOL_CALL_ADAPTER | typeof OPENAI_CHAT_SEQUENTIAL_STREAMED_TOOL_CALL_ADAPTER;
8
+ export declare function streamedToolCallAdapterAllowsSequentialSeal(metadata: Record<string, unknown> | undefined): boolean;
5
9
  export type StreamedToolCallSeal = {
6
10
  kind: 'single';
7
11
  id?: string;
@@ -382,6 +382,16 @@ export type ToolExecuteBatchRequest = {
382
382
  resolve: (results: ToolExecuteResult[]) => void;
383
383
  /** Promise rejector - handler calls this on fatal error */
384
384
  reject: (error: Error) => void;
385
+ /**
386
+ * Optional per-call result channel. When present, the handler MAY invoke
387
+ * this as each tool call settles (before the final `resolve`) so the
388
+ * graph can emit that call's completion event without waiting for the
389
+ * slowest call in the batch. Purely an emission fast-path: the handler
390
+ * must still pass every result to `resolve`, which remains the
391
+ * authoritative batch outcome. Only provided when no post-tool hooks or
392
+ * human-in-the-loop flows could change a result after execution.
393
+ */
394
+ onResult?: (result: ToolExecuteResult) => void;
385
395
  };
386
396
  /**
387
397
  * A message injected into graph state by any tool execution handler.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@librechat/agents",
3
- "version": "3.2.33",
3
+ "version": "3.2.34",
4
4
  "main": "./dist/cjs/main.cjs",
5
5
  "module": "./dist/esm/main.mjs",
6
6
  "types": "./dist/types/index.d.ts",