@librechat/agents 2.3.97 → 2.3.98

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,40 @@ 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
490
  const args = finalUpdate
394
491
  ? contentPart.tool_call.args
395
- : (existingContent?.tool_call?.args || '') + (contentPart.tool_call.args ?? '');
396
-
397
- const id = getNonEmptyValue([contentPart.tool_call.id, existingContent?.tool_call?.id]) ?? '';
492
+ : (existingContent?.tool_call?.args || '') +
493
+ (contentPart.tool_call.args ?? '');
494
+
495
+ const id =
496
+ getNonEmptyValue([
497
+ contentPart.tool_call.id,
498
+ existingContent?.tool_call?.id,
499
+ ]) ?? '';
398
500
  const name =
399
- getNonEmptyValue([contentPart.tool_call.name, existingContent?.tool_call?.name]) ?? '';
501
+ getNonEmptyValue([
502
+ contentPart.tool_call.name,
503
+ existingContent?.tool_call?.name,
504
+ ]) ?? '';
400
505
 
401
506
  const newToolCall: ToolCall & t.PartMetadata = {
402
507
  id,
@@ -417,22 +522,45 @@ export function createContentAggregator(): t.ContentAggregatorResult {
417
522
  }
418
523
  };
419
524
 
420
- const aggregateContent = ({ event, data }: {
525
+ const aggregateContent = ({
526
+ event,
527
+ data,
528
+ }: {
421
529
  event: GraphEvents;
422
- data: t.RunStep | t.AgentUpdate | t.MessageDeltaEvent | t.RunStepDeltaEvent | { result: t.ToolEndEvent };
530
+ data:
531
+ | t.RunStep
532
+ | t.AgentUpdate
533
+ | t.MessageDeltaEvent
534
+ | t.RunStepDeltaEvent
535
+ | { result: t.ToolEndEvent };
423
536
  }): void => {
424
-
425
537
  if (event === GraphEvents.ON_RUN_STEP) {
426
538
  const runStep = data as t.RunStep;
427
539
  stepMap.set(runStep.id, runStep);
428
540
 
429
541
  // 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) => {
542
+ if (
543
+ runStep.stepDetails.type === StepTypes.TOOL_CALLS &&
544
+ runStep.stepDetails.tool_calls
545
+ ) {
546
+ (runStep.stepDetails.tool_calls as ToolCall[]).forEach((toolCall) => {
432
547
  const toolCallId = toolCall.id ?? '';
433
548
  if ('id' in toolCall && toolCallId) {
434
549
  toolCallIdMap.set(runStep.id, toolCallId);
435
550
  }
551
+ const contentPart: t.MessageContentComplex = {
552
+ type: ContentTypes.TOOL_CALL,
553
+ tool_call: {
554
+ args:
555
+ (typeof toolCall.args === 'string'
556
+ ? toolCall.args
557
+ : JSON.stringify(toolCall.args)) || '',
558
+ name: toolCall.name,
559
+ id: toolCallId,
560
+ },
561
+ };
562
+
563
+ updateContent(runStep.index, contentPart);
436
564
  });
437
565
  }
438
566
  } else if (event === GraphEvents.ON_MESSAGE_DELTA) {
@@ -450,7 +578,10 @@ export function createContentAggregator(): t.ContentAggregatorResult {
450
578
 
451
579
  updateContent(runStep.index, contentPart);
452
580
  }
453
- } else if (event === GraphEvents.ON_AGENT_UPDATE && (data as t.AgentUpdate | undefined)?.agent_update) {
581
+ } else if (
582
+ event === GraphEvents.ON_AGENT_UPDATE &&
583
+ (data as t.AgentUpdate | undefined)?.agent_update
584
+ ) {
454
585
  const contentPart = data as t.AgentUpdate;
455
586
  updateContent(contentPart.agent_update.index, contentPart);
456
587
  } else if (event === GraphEvents.ON_REASONING_DELTA) {
@@ -480,7 +611,6 @@ export function createContentAggregator(): t.ContentAggregatorResult {
480
611
  runStepDelta.delta.type === StepTypes.TOOL_CALLS &&
481
612
  runStepDelta.delta.tool_calls
482
613
  ) {
483
-
484
614
  runStepDelta.delta.tool_calls.forEach((toolCallDelta) => {
485
615
  const toolCallId = toolCallIdMap.get(runStepDelta.id);
486
616
 
@@ -503,7 +633,9 @@ export function createContentAggregator(): t.ContentAggregatorResult {
503
633
 
504
634
  const runStep = stepMap.get(stepId);
505
635
  if (!runStep) {
506
- console.warn('No run step or runId found for completed tool call event');
636
+ console.warn(
637
+ 'No run step or runId found for completed tool call event'
638
+ );
507
639
  return;
508
640
  }
509
641
 
@@ -514,7 +646,6 @@ export function createContentAggregator(): t.ContentAggregatorResult {
514
646
 
515
647
  updateContent(runStep.index, contentPart, true);
516
648
  }
517
-
518
649
  };
519
650
 
520
651
  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
  },