@librechat/agents 2.3.97 → 2.3.99

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/src/stream.ts CHANGED
@@ -15,7 +15,11 @@ function getNonEmptyValue(possibleValues: string[]): string | undefined {
15
15
  return undefined;
16
16
  }
17
17
 
18
- export const getMessageId = (stepKey: string, graph: Graph<t.BaseGraphState>, returnExistingId = false): string | undefined => {
18
+ export const getMessageId = (
19
+ stepKey: string,
20
+ graph: Graph<t.BaseGraphState>,
21
+ returnExistingId = false
22
+ ): string | undefined => {
19
23
  const messageId = graph.messageIdsByStepKey.get(stepKey);
20
24
  if (messageId != null && messageId) {
21
25
  return returnExistingId ? messageId : undefined;
@@ -33,7 +37,11 @@ export const getMessageId = (stepKey: string, graph: Graph<t.BaseGraphState>, re
33
37
  return message_id;
34
38
  };
35
39
 
36
- export const handleToolCalls = (toolCalls?: ToolCall[], metadata?: Record<string, unknown>, graph?: Graph): void => {
40
+ export const handleToolCalls = (
41
+ toolCalls?: ToolCall[],
42
+ metadata?: Record<string, unknown>,
43
+ graph?: Graph
44
+ ): void => {
37
45
  if (!graph || !metadata) {
38
46
  console.warn(`Graph or metadata not found in ${event} event`);
39
47
  return;
@@ -67,19 +75,28 @@ export const handleToolCalls = (toolCalls?: ToolCall[], metadata?: Record<string
67
75
 
68
76
  const dispatchToolCallIds = (lastMessageStepId: string): void => {
69
77
  graph.dispatchMessageDelta(lastMessageStepId, {
70
- content: [{
71
- type: 'text',
72
- text: '',
73
- tool_call_ids: [toolCallId],
74
- }],
78
+ content: [
79
+ {
80
+ type: 'text',
81
+ text: '',
82
+ tool_call_ids: [toolCallId],
83
+ },
84
+ ],
75
85
  });
76
86
  };
77
- /* If the previous step exists and is a message creation */
78
- if (prevStepId && prevRunStep && prevRunStep.type === StepTypes.MESSAGE_CREATION) {
87
+ /* If the previous step exists and is a message creation */
88
+ if (
89
+ prevStepId &&
90
+ prevRunStep &&
91
+ prevRunStep.type === StepTypes.MESSAGE_CREATION
92
+ ) {
79
93
  dispatchToolCallIds(prevStepId);
80
94
  graph.messageStepHasToolCalls.set(prevStepId, true);
81
95
  /* If the previous step doesn't exist or is not a message creation */
82
- } else if (!prevRunStep || prevRunStep.type !== StepTypes.MESSAGE_CREATION) {
96
+ } else if (
97
+ !prevRunStep ||
98
+ prevRunStep.type !== StepTypes.MESSAGE_CREATION
99
+ ) {
83
100
  const messageId = getMessageId(stepKey, graph, true) ?? '';
84
101
  const stepId = graph.dispatchRunStep(stepKey, {
85
102
  type: StepTypes.MESSAGE_CREATION,
@@ -99,7 +116,12 @@ export const handleToolCalls = (toolCalls?: ToolCall[], metadata?: Record<string
99
116
  };
100
117
 
101
118
  export class ChatModelStreamHandler implements t.EventHandler {
102
- handle(event: string, data: t.StreamEventData, metadata?: Record<string, unknown>, graph?: Graph): void {
119
+ handle(
120
+ event: string,
121
+ data: t.StreamEventData,
122
+ metadata?: Record<string, unknown>,
123
+ graph?: Graph
124
+ ): void {
103
125
  if (!graph) {
104
126
  throw new Error('Graph not found');
105
127
  }
@@ -112,17 +134,27 @@ export class ChatModelStreamHandler implements t.EventHandler {
112
134
  }
113
135
 
114
136
  const chunk = data.chunk as Partial<AIMessageChunk>;
115
- const content = (chunk.additional_kwargs?.[graph.reasoningKey] as string | undefined) ?? chunk.content;
137
+ const content =
138
+ (chunk.additional_kwargs?.[graph.reasoningKey] as string | undefined) ??
139
+ chunk.content;
116
140
  this.handleReasoning(chunk, graph);
117
141
 
118
142
  let hasToolCalls = false;
119
- if (chunk.tool_calls && chunk.tool_calls.length > 0 && chunk.tool_calls.every((tc) => tc.id != null && tc.id !== '')) {
143
+ if (
144
+ chunk.tool_calls &&
145
+ chunk.tool_calls.length > 0 &&
146
+ chunk.tool_calls.every((tc) => tc.id != null && tc.id !== '')
147
+ ) {
120
148
  hasToolCalls = true;
121
149
  handleToolCalls(chunk.tool_calls, metadata, graph);
122
150
  }
123
151
 
124
- const hasToolCallChunks = (chunk.tool_call_chunks && chunk.tool_call_chunks.length > 0) ?? false;
125
- const isEmptyContent = typeof content === 'undefined' || !content.length || typeof content === 'string' && !content;
152
+ const hasToolCallChunks =
153
+ (chunk.tool_call_chunks && chunk.tool_call_chunks.length > 0) ?? false;
154
+ const isEmptyContent =
155
+ typeof content === 'undefined' ||
156
+ !content.length ||
157
+ (typeof content === 'string' && !content);
126
158
  const isEmptyChunk = isEmptyContent && !hasToolCallChunks;
127
159
  const chunkId = chunk.id ?? '';
128
160
  if (isEmptyChunk && chunkId && chunkId.startsWith('msg')) {
@@ -141,11 +173,17 @@ export class ChatModelStreamHandler implements t.EventHandler {
141
173
 
142
174
  const stepKey = graph.getStepKey(metadata);
143
175
 
144
- if (hasToolCallChunks
145
- && chunk.tool_call_chunks
146
- && chunk.tool_call_chunks.length
147
- && typeof chunk.tool_call_chunks[0]?.index === 'number') {
148
- this.handleToolCallChunks({ graph, stepKey, toolCallChunks: chunk.tool_call_chunks });
176
+ if (
177
+ hasToolCallChunks &&
178
+ chunk.tool_call_chunks &&
179
+ chunk.tool_call_chunks.length &&
180
+ typeof chunk.tool_call_chunks[0]?.index === 'number'
181
+ ) {
182
+ this.handleToolCallChunks({
183
+ graph,
184
+ stepKey,
185
+ toolCallChunks: chunk.tool_call_chunks,
186
+ });
149
187
  }
150
188
 
151
189
  if (isEmptyContent) {
@@ -165,7 +203,6 @@ export class ChatModelStreamHandler implements t.EventHandler {
165
203
  const stepId = graph.getStepIdByKey(stepKey);
166
204
  const runStep = graph.getRunStep(stepId);
167
205
  if (!runStep) {
168
-
169
206
  console.warn(`\n
170
207
  ==============================================================
171
208
 
@@ -187,38 +224,53 @@ hasToolCallChunks: ${hasToolCallChunks}
187
224
  /* Note: tool call chunks may have non-empty content that matches the current tool chunk generation */
188
225
  if (typeof content === 'string' && runStep.type === StepTypes.TOOL_CALLS) {
189
226
  return;
190
- } else if (hasToolCallChunks && (chunk.tool_call_chunks?.some((tc) => tc.args === content) ?? false)) {
227
+ } else if (
228
+ hasToolCallChunks &&
229
+ (chunk.tool_call_chunks?.some((tc) => tc.args === content) ?? false)
230
+ ) {
191
231
  return;
192
232
  } else if (typeof content === 'string') {
193
233
  if (graph.currentTokenType === ContentTypes.TEXT) {
194
234
  graph.dispatchMessageDelta(stepId, {
195
- content: [{
196
- type: ContentTypes.TEXT,
197
- text: content,
198
- }],
235
+ content: [
236
+ {
237
+ type: ContentTypes.TEXT,
238
+ text: content,
239
+ },
240
+ ],
199
241
  });
200
242
  } else {
201
243
  graph.dispatchReasoningDelta(stepId, {
202
- content: [{
203
- type: ContentTypes.THINK,
204
- think: content,
205
- }],
244
+ content: [
245
+ {
246
+ type: ContentTypes.THINK,
247
+ think: content,
248
+ },
249
+ ],
206
250
  });
207
251
  }
208
- } else if (content.every((c) => c.type?.startsWith(ContentTypes.TEXT) ?? false)) {
252
+ } else if (
253
+ content.every((c) => c.type?.startsWith(ContentTypes.TEXT) ?? false)
254
+ ) {
209
255
  graph.dispatchMessageDelta(stepId, {
210
256
  content,
211
257
  });
212
- } else if (content.every(
213
- (c) =>
214
- (c.type?.startsWith(ContentTypes.THINKING) ?? false) ||
215
- (c.type?.startsWith(ContentTypes.REASONING_CONTENT) ?? false)
216
- )) {
258
+ } else if (
259
+ content.every(
260
+ (c) =>
261
+ (c.type?.startsWith(ContentTypes.THINKING) ?? false) ||
262
+ (c.type?.startsWith(ContentTypes.REASONING_CONTENT) ?? false)
263
+ )
264
+ ) {
217
265
  graph.dispatchReasoningDelta(stepId, {
218
266
  content: content.map((c) => ({
219
267
  type: ContentTypes.THINK,
220
- think: (c as t.ThinkingContentText).thinking ?? (c as Partial<t.BedrockReasoningContentText>).reasoningText?.text ?? '',
221
- }))});
268
+ think:
269
+ (c as t.ThinkingContentText).thinking ??
270
+ (c as Partial<t.BedrockReasoningContentText>).reasoningText?.text ??
271
+ '',
272
+ })),
273
+ });
222
274
  }
223
275
  }
224
276
  handleToolCallChunks = ({
@@ -228,7 +280,7 @@ hasToolCallChunks: ${hasToolCallChunks}
228
280
  }: {
229
281
  graph: Graph;
230
282
  stepKey: string;
231
- toolCallChunks: ToolCallChunk[],
283
+ toolCallChunks: ToolCallChunk[];
232
284
  }): void => {
233
285
  let prevStepId: string;
234
286
  let prevRunStep: t.RunStep | undefined;
@@ -251,7 +303,9 @@ hasToolCallChunks: ${hasToolCallChunks}
251
303
 
252
304
  /** Edge Case: Tool Call Run Step or `tool_call_ids` never dispatched */
253
305
  const tool_calls: ToolCall[] | undefined =
254
- prevStepId && prevRunStep && prevRunStep.type === StepTypes.MESSAGE_CREATION
306
+ prevStepId &&
307
+ prevRunStep &&
308
+ prevRunStep.type === StepTypes.MESSAGE_CREATION
255
309
  ? []
256
310
  : undefined;
257
311
 
@@ -262,7 +316,11 @@ hasToolCallChunks: ${hasToolCallChunks}
262
316
  }
263
317
  if (toolCallChunk.id === '') {
264
318
  toolCallChunk.id = undefined;
265
- } else if (tool_calls != null && toolCallChunk.id != null && toolCallChunk.name != null) {
319
+ } else if (
320
+ tool_calls != null &&
321
+ toolCallChunk.id != null &&
322
+ toolCallChunk.name != null
323
+ ) {
266
324
  tool_calls.push({
267
325
  args: {},
268
326
  id: toolCallChunk.id,
@@ -273,14 +331,18 @@ hasToolCallChunks: ${hasToolCallChunks}
273
331
  }
274
332
 
275
333
  let stepId: string = _stepId;
276
- const alreadyDispatched = prevRunStep?.type === StepTypes.MESSAGE_CREATION && graph.messageStepHasToolCalls.has(prevStepId);
334
+ const alreadyDispatched =
335
+ prevRunStep?.type === StepTypes.MESSAGE_CREATION &&
336
+ graph.messageStepHasToolCalls.has(prevStepId);
277
337
  if (!alreadyDispatched && tool_calls?.length === toolCallChunks.length) {
278
338
  graph.dispatchMessageDelta(prevStepId, {
279
- content: [{
280
- type: ContentTypes.TEXT,
281
- text: '',
282
- tool_call_ids: tool_calls.map((tc) => tc.id ?? ''),
283
- }],
339
+ content: [
340
+ {
341
+ type: ContentTypes.TEXT,
342
+ text: '',
343
+ tool_call_ids: tool_calls.map((tc) => tc.id ?? ''),
344
+ },
345
+ ],
284
346
  });
285
347
  graph.messageStepHasToolCalls.set(prevStepId, true);
286
348
  stepId = graph.dispatchRunStep(stepKey, {
@@ -294,21 +356,45 @@ hasToolCallChunks: ${hasToolCallChunks}
294
356
  });
295
357
  };
296
358
  handleReasoning(chunk: Partial<AIMessageChunk>, graph: Graph): void {
297
- let reasoning_content = chunk.additional_kwargs?.[graph.reasoningKey] as string | undefined;
298
- if (Array.isArray(chunk.content) && (chunk.content[0]?.type === 'thinking' || chunk.content[0]?.type === 'reasoning_content')) {
359
+ let reasoning_content = chunk.additional_kwargs?.[graph.reasoningKey] as
360
+ | string
361
+ | undefined;
362
+ if (
363
+ Array.isArray(chunk.content) &&
364
+ (chunk.content[0]?.type === 'thinking' ||
365
+ chunk.content[0]?.type === 'reasoning_content')
366
+ ) {
299
367
  reasoning_content = 'valid';
300
368
  }
301
- if (reasoning_content != null && reasoning_content && (chunk.content == null || chunk.content === '' || reasoning_content === 'valid')) {
369
+ if (
370
+ reasoning_content != null &&
371
+ reasoning_content &&
372
+ (chunk.content == null ||
373
+ chunk.content === '' ||
374
+ reasoning_content === 'valid')
375
+ ) {
302
376
  graph.currentTokenType = ContentTypes.THINK;
303
377
  graph.tokenTypeSwitch = 'reasoning';
304
378
  return;
305
- } else if (graph.tokenTypeSwitch === 'reasoning' && graph.currentTokenType !== ContentTypes.TEXT && chunk.content != null && chunk.content !== '') {
379
+ } else if (
380
+ graph.tokenTypeSwitch === 'reasoning' &&
381
+ graph.currentTokenType !== ContentTypes.TEXT &&
382
+ chunk.content != null &&
383
+ chunk.content !== ''
384
+ ) {
306
385
  graph.currentTokenType = ContentTypes.TEXT;
307
386
  graph.tokenTypeSwitch = 'content';
308
- } else if (chunk.content != null && typeof chunk.content === 'string' && chunk.content.includes('<think>')) {
387
+ } else if (
388
+ chunk.content != null &&
389
+ typeof chunk.content === 'string' &&
390
+ chunk.content.includes('<think>')
391
+ ) {
309
392
  graph.currentTokenType = ContentTypes.THINK;
310
393
  graph.tokenTypeSwitch = 'content';
311
- } else if (graph.lastToken != null && graph.lastToken.includes('</think>')) {
394
+ } else if (
395
+ graph.lastToken != null &&
396
+ graph.lastToken.includes('</think>')
397
+ ) {
312
398
  graph.currentTokenType = ContentTypes.TEXT;
313
399
  graph.tokenTypeSwitch = 'content';
314
400
  }
@@ -327,7 +413,7 @@ export function createContentAggregator(): t.ContentAggregatorResult {
327
413
  const updateContent = (
328
414
  index: number,
329
415
  contentPart: t.MessageContentComplex,
330
- finalUpdate = false,
416
+ finalUpdate = false
331
417
  ): void => {
332
418
  const partType = contentPart.type ?? '';
333
419
  if (!partType) {
@@ -382,21 +468,44 @@ export function createContentAggregator(): t.ContentAggregatorResult {
382
468
  };
383
469
 
384
470
  contentParts[index] = update;
385
- } else if (partType === ContentTypes.IMAGE_URL && 'image_url' in contentPart) {
386
- const currentContent = contentParts[index] as { type: 'image_url'; image_url: string };
471
+ } else if (
472
+ partType === ContentTypes.IMAGE_URL &&
473
+ 'image_url' in contentPart
474
+ ) {
475
+ const currentContent = contentParts[index] as {
476
+ type: 'image_url';
477
+ image_url: string;
478
+ };
387
479
  contentParts[index] = {
388
480
  ...currentContent,
389
481
  };
390
- } else if (partType === ContentTypes.TOOL_CALL && 'tool_call' in contentPart) {
391
- const existingContent = contentParts[index] as Omit<t.ToolCallContent, 'tool_call'> & { tool_call?: ToolCall } | undefined;
482
+ } else if (
483
+ partType === ContentTypes.TOOL_CALL &&
484
+ 'tool_call' in contentPart
485
+ ) {
486
+ const existingContent = contentParts[index] as
487
+ | (Omit<t.ToolCallContent, 'tool_call'> & { tool_call?: ToolCall })
488
+ | undefined;
392
489
 
393
- const args = finalUpdate
490
+ const toolCallArgs =
491
+ (contentPart.tool_call.args as unknown as string | undefined) ?? '';
492
+ let args = finalUpdate
394
493
  ? contentPart.tool_call.args
395
- : (existingContent?.tool_call?.args || '') + (contentPart.tool_call.args ?? '');
494
+ : (existingContent?.tool_call?.args ?? '');
495
+ if (!finalUpdate && typeof args === 'string' && args !== toolCallArgs) {
496
+ args += toolCallArgs;
497
+ }
396
498
 
397
- const id = getNonEmptyValue([contentPart.tool_call.id, existingContent?.tool_call?.id]) ?? '';
499
+ const id =
500
+ getNonEmptyValue([
501
+ contentPart.tool_call.id,
502
+ existingContent?.tool_call?.id,
503
+ ]) ?? '';
398
504
  const name =
399
- getNonEmptyValue([contentPart.tool_call.name, existingContent?.tool_call?.name]) ?? '';
505
+ getNonEmptyValue([
506
+ contentPart.tool_call.name,
507
+ existingContent?.tool_call?.name,
508
+ ]) ?? '';
400
509
 
401
510
  const newToolCall: ToolCall & t.PartMetadata = {
402
511
  id,
@@ -417,22 +526,42 @@ export function createContentAggregator(): t.ContentAggregatorResult {
417
526
  }
418
527
  };
419
528
 
420
- const aggregateContent = ({ event, data }: {
529
+ const aggregateContent = ({
530
+ event,
531
+ data,
532
+ }: {
421
533
  event: GraphEvents;
422
- data: t.RunStep | t.AgentUpdate | t.MessageDeltaEvent | t.RunStepDeltaEvent | { result: t.ToolEndEvent };
534
+ data:
535
+ | t.RunStep
536
+ | t.AgentUpdate
537
+ | t.MessageDeltaEvent
538
+ | t.RunStepDeltaEvent
539
+ | { result: t.ToolEndEvent };
423
540
  }): void => {
424
-
425
541
  if (event === GraphEvents.ON_RUN_STEP) {
426
542
  const runStep = data as t.RunStep;
427
543
  stepMap.set(runStep.id, runStep);
428
544
 
429
545
  // Store tool call IDs if present
430
- if (runStep.stepDetails.type === StepTypes.TOOL_CALLS && runStep.stepDetails.tool_calls) {
431
- runStep.stepDetails.tool_calls.forEach((toolCall) => {
546
+ if (
547
+ runStep.stepDetails.type === StepTypes.TOOL_CALLS &&
548
+ runStep.stepDetails.tool_calls
549
+ ) {
550
+ (runStep.stepDetails.tool_calls as ToolCall[]).forEach((toolCall) => {
432
551
  const toolCallId = toolCall.id ?? '';
433
552
  if ('id' in toolCall && toolCallId) {
434
553
  toolCallIdMap.set(runStep.id, toolCallId);
435
554
  }
555
+ const contentPart: t.MessageContentComplex = {
556
+ type: ContentTypes.TOOL_CALL,
557
+ tool_call: {
558
+ args: toolCall.args,
559
+ name: toolCall.name,
560
+ id: toolCallId,
561
+ },
562
+ };
563
+
564
+ updateContent(runStep.index, contentPart);
436
565
  });
437
566
  }
438
567
  } else if (event === GraphEvents.ON_MESSAGE_DELTA) {
@@ -450,7 +579,10 @@ export function createContentAggregator(): t.ContentAggregatorResult {
450
579
 
451
580
  updateContent(runStep.index, contentPart);
452
581
  }
453
- } else if (event === GraphEvents.ON_AGENT_UPDATE && (data as t.AgentUpdate | undefined)?.agent_update) {
582
+ } else if (
583
+ event === GraphEvents.ON_AGENT_UPDATE &&
584
+ (data as t.AgentUpdate | undefined)?.agent_update
585
+ ) {
454
586
  const contentPart = data as t.AgentUpdate;
455
587
  updateContent(contentPart.agent_update.index, contentPart);
456
588
  } else if (event === GraphEvents.ON_REASONING_DELTA) {
@@ -480,7 +612,6 @@ export function createContentAggregator(): t.ContentAggregatorResult {
480
612
  runStepDelta.delta.type === StepTypes.TOOL_CALLS &&
481
613
  runStepDelta.delta.tool_calls
482
614
  ) {
483
-
484
615
  runStepDelta.delta.tool_calls.forEach((toolCallDelta) => {
485
616
  const toolCallId = toolCallIdMap.get(runStepDelta.id);
486
617
 
@@ -503,7 +634,9 @@ export function createContentAggregator(): t.ContentAggregatorResult {
503
634
 
504
635
  const runStep = stepMap.get(stepId);
505
636
  if (!runStep) {
506
- console.warn('No run step or runId found for completed tool call event');
637
+ console.warn(
638
+ 'No run step or runId found for completed tool call event'
639
+ );
507
640
  return;
508
641
  }
509
642
 
@@ -514,7 +647,6 @@ export function createContentAggregator(): t.ContentAggregatorResult {
514
647
 
515
648
  updateContent(runStep.index, contentPart, true);
516
649
  }
517
-
518
650
  };
519
651
 
520
652
  return { contentParts, aggregateContent, stepMap };
@@ -100,7 +100,7 @@ export const llmConfigs: Record<string, t.LLMConfig | undefined> = {
100
100
  } as t.VertexAIClientOptions & t.LLMConfig,
101
101
  [Providers.GOOGLE]: {
102
102
  provider: Providers.GOOGLE,
103
- model: 'gemini-2.0-flash-exp',
103
+ model: 'gemini-2.5-pro-exp-03-25',
104
104
  streaming: true,
105
105
  streamUsage: true,
106
106
  },