@librechat/agents 3.1.37 → 3.1.39

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 (41) hide show
  1. package/dist/cjs/agents/AgentContext.cjs +3 -0
  2. package/dist/cjs/agents/AgentContext.cjs.map +1 -1
  3. package/dist/cjs/graphs/Graph.cjs +1 -1
  4. package/dist/cjs/graphs/Graph.cjs.map +1 -1
  5. package/dist/cjs/messages/cache.cjs +2 -2
  6. package/dist/cjs/messages/cache.cjs.map +1 -1
  7. package/dist/cjs/stream.cjs +2 -1
  8. package/dist/cjs/stream.cjs.map +1 -1
  9. package/dist/cjs/tools/CodeExecutor.cjs +1 -0
  10. package/dist/cjs/tools/CodeExecutor.cjs.map +1 -1
  11. package/dist/cjs/tools/handlers.cjs +25 -8
  12. package/dist/cjs/tools/handlers.cjs.map +1 -1
  13. package/dist/esm/agents/AgentContext.mjs +3 -0
  14. package/dist/esm/agents/AgentContext.mjs.map +1 -1
  15. package/dist/esm/graphs/Graph.mjs +1 -1
  16. package/dist/esm/graphs/Graph.mjs.map +1 -1
  17. package/dist/esm/messages/cache.mjs +2 -2
  18. package/dist/esm/messages/cache.mjs.map +1 -1
  19. package/dist/esm/stream.mjs +2 -1
  20. package/dist/esm/stream.mjs.map +1 -1
  21. package/dist/esm/tools/CodeExecutor.mjs +1 -0
  22. package/dist/esm/tools/CodeExecutor.mjs.map +1 -1
  23. package/dist/esm/tools/handlers.mjs +25 -8
  24. package/dist/esm/tools/handlers.mjs.map +1 -1
  25. package/dist/types/agents/AgentContext.d.ts +2 -0
  26. package/dist/types/tools/CodeExecutor.d.ts +2 -2
  27. package/dist/types/types/tools.d.ts +1 -0
  28. package/package.json +1 -1
  29. package/src/agents/AgentContext.ts +3 -0
  30. package/src/graphs/Graph.ts +1 -1
  31. package/src/messages/cache.test.ts +41 -0
  32. package/src/messages/cache.ts +2 -2
  33. package/src/scripts/bedrock-content-aggregation-test.ts +265 -0
  34. package/src/scripts/bedrock-parallel-tools-test.ts +203 -0
  35. package/src/scripts/tools.ts +3 -12
  36. package/src/stream.ts +2 -1
  37. package/src/tools/CodeExecutor.ts +1 -0
  38. package/src/tools/__tests__/ToolNode.session.test.ts +465 -0
  39. package/src/tools/__tests__/handlers.test.ts +994 -0
  40. package/src/tools/handlers.ts +32 -13
  41. package/src/types/tools.ts +1 -0
@@ -117,6 +117,7 @@ export async function handleToolCallChunks({
117
117
  metadata
118
118
  );
119
119
  }
120
+
120
121
  await graph.dispatchRunStepDelta(stepId, {
121
122
  type: StepTypes.TOOL_CALLS,
122
123
  tool_calls: toolCallChunks,
@@ -129,7 +130,7 @@ export const handleToolCalls = async (
129
130
  graph?: StandardGraph | MultiAgentGraph
130
131
  ): Promise<void> => {
131
132
  if (!graph || !metadata) {
132
- console.warn(`Graph or metadata not found in ${event} event`);
133
+ console.warn('Graph or metadata not found in `handleToolCalls`');
133
134
  return;
134
135
  }
135
136
 
@@ -143,6 +144,13 @@ export const handleToolCalls = async (
143
144
 
144
145
  const stepKey = graph.getStepKey(metadata);
145
146
 
147
+ /**
148
+ * Track whether we've already reused an empty TOOL_CALLS step created by
149
+ * handleToolCallChunks during streaming. Only reuse it once (for the first
150
+ * tool call); subsequent parallel tool calls must create their own steps.
151
+ */
152
+ let reusedChunkStepId: string | undefined;
153
+
146
154
  for (const tool_call of toolCalls) {
147
155
  const toolCallId = tool_call.id ?? `toolu_${nanoid()}`;
148
156
  tool_call.id = toolCallId;
@@ -159,6 +167,27 @@ export const handleToolCalls = async (
159
167
  // no previous step
160
168
  }
161
169
 
170
+ /**
171
+ * If the previous step is TOOL_CALLS (from handleToolCallChunks or a prior
172
+ * iteration), either reuse it (if empty) or dispatch a new TOOL_CALLS step
173
+ * directly — skip the intermediate MESSAGE_CREATION to avoid orphaned gaps.
174
+ */
175
+ if (prevRunStep?.type === StepTypes.TOOL_CALLS) {
176
+ const details = prevRunStep.stepDetails as t.ToolCallsDetails;
177
+ const isEmpty = !details.tool_calls || details.tool_calls.length === 0;
178
+ if (isEmpty && prevStepId !== reusedChunkStepId) {
179
+ graph.toolCallStepIds.set(toolCallId, prevStepId);
180
+ reusedChunkStepId = prevStepId;
181
+ continue;
182
+ }
183
+ await graph.dispatchRunStep(
184
+ stepKey,
185
+ { type: StepTypes.TOOL_CALLS, tool_calls: [tool_call] },
186
+ metadata
187
+ );
188
+ continue;
189
+ }
190
+
162
191
  /**
163
192
  * NOTE: We do NOT dispatch empty text blocks with tool_call_ids because:
164
193
  * - Empty text blocks cause providers (Anthropic, Bedrock) to reject messages
@@ -167,19 +196,9 @@ export const handleToolCalls = async (
167
196
  * "The content field in the Message object is empty" (Bedrock)
168
197
  * - The tool_calls themselves are sufficient
169
198
  */
170
-
171
- /* If the previous step exists and is a message creation */
172
- if (
173
- prevStepId &&
174
- prevRunStep &&
175
- prevRunStep.type === StepTypes.MESSAGE_CREATION
176
- ) {
199
+ if (prevStepId && prevRunStep) {
177
200
  graph.messageStepHasToolCalls.set(prevStepId, true);
178
- /* If the previous step doesn't exist or is not a message creation */
179
- } else if (
180
- !prevRunStep ||
181
- prevRunStep.type !== StepTypes.MESSAGE_CREATION
182
- ) {
201
+ } else if (!prevRunStep) {
183
202
  const messageId = getMessageId(stepKey, graph, true) ?? '';
184
203
  const stepId = await graph.dispatchRunStep(
185
204
  stepKey,
@@ -60,6 +60,7 @@ export type ToolEndEvent = {
60
60
  tool_call: ToolCall;
61
61
  /** The content index of the tool call */
62
62
  index: number;
63
+ type?: 'tool_call';
63
64
  };
64
65
 
65
66
  export type CodeEnvFile = {