@librechat/agents 2.4.55 → 2.4.56

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.
@@ -1,7 +1,11 @@
1
1
  import { AzureOpenAI as AzureOpenAIClient } from 'openai';
2
+ import { AIMessageChunk } from '@langchain/core/messages';
2
3
  import { ChatXAI as OriginalChatXAI } from '@langchain/xai';
3
- import { ChatDeepSeek as OriginalChatDeepSeek } from '@langchain/deepseek';
4
+ import { ChatGenerationChunk } from '@langchain/core/outputs';
4
5
  import { ToolDefinition } from '@langchain/core/language_models/base';
6
+ import { isLangChainTool } from '@langchain/core/utils/function_calling';
7
+ import { ChatDeepSeek as OriginalChatDeepSeek } from '@langchain/deepseek';
8
+ import { CallbackManagerForLLMRun } from '@langchain/core/callbacks/manager';
5
9
  import {
6
10
  getEndpoint,
7
11
  OpenAIClient,
@@ -9,19 +13,26 @@ import {
9
13
  ChatOpenAI as OriginalChatOpenAI,
10
14
  AzureChatOpenAI as OriginalAzureChatOpenAI,
11
15
  } from '@langchain/openai';
12
- import type { ChatGenerationChunk } from '@langchain/core/outputs';
13
- import { isLangChainTool } from '@langchain/core/utils/function_calling';
14
- import { CallbackManagerForLLMRun } from '@langchain/core/callbacks/manager';
15
16
  import type { BindToolsInput } from '@langchain/core/language_models/chat_models';
16
17
  import type { OpenAIEndpointConfig } from '@langchain/openai/dist/utils/azure';
17
18
  import type { BaseMessage } from '@langchain/core/messages';
18
19
  import type * as t from '@langchain/openai';
19
20
  import {
21
+ _convertMessagesToOpenAIParams,
20
22
  _convertMessagesToOpenAIResponsesParams,
21
23
  _convertOpenAIResponsesDeltaToBaseMessageChunk,
22
24
  type ResponseReturnStreamEvents,
23
25
  } from './utils';
24
26
 
27
+ // TODO import from SDK when available
28
+ type OpenAIRoleEnum =
29
+ | 'system'
30
+ | 'developer'
31
+ | 'assistant'
32
+ | 'user'
33
+ | 'function'
34
+ | 'tool';
35
+
25
36
  type HeaderValue = string | undefined | null;
26
37
  export type HeadersLike =
27
38
  | Headers
@@ -78,6 +89,9 @@ export function normalizeHeaders(
78
89
  return Object.fromEntries(output.entries());
79
90
  }
80
91
 
92
+ type OpenAICompletionParam =
93
+ OpenAIClient.Chat.Completions.ChatCompletionMessageParam;
94
+
81
95
  type OpenAICoreRequestOptions = OpenAIClient.RequestOptions;
82
96
 
83
97
  function createAbortHandler(controller: AbortController): () => void {
@@ -229,7 +243,7 @@ export class ChatOpenAI extends OriginalChatOpenAI<t.ChatOpenAICallOptions> {
229
243
  runManager?: CallbackManagerForLLMRun
230
244
  ): AsyncGenerator<ChatGenerationChunk> {
231
245
  if (!this._useResponseApi(options)) {
232
- return yield* super._streamResponseChunks(messages, options, runManager);
246
+ return yield* this._streamResponseChunks2(messages, options, runManager);
233
247
  }
234
248
  const streamIterable = await this.responseApiWithRetry(
235
249
  {
@@ -262,6 +276,132 @@ export class ChatOpenAI extends OriginalChatOpenAI<t.ChatOpenAICallOptions> {
262
276
 
263
277
  return;
264
278
  }
279
+
280
+ async *_streamResponseChunks2(
281
+ messages: BaseMessage[],
282
+ options: this['ParsedCallOptions'],
283
+ runManager?: CallbackManagerForLLMRun
284
+ ): AsyncGenerator<ChatGenerationChunk> {
285
+ const messagesMapped: OpenAICompletionParam[] =
286
+ _convertMessagesToOpenAIParams(messages, this.model);
287
+
288
+ const params = {
289
+ ...this.invocationParams(options, {
290
+ streaming: true,
291
+ }),
292
+ messages: messagesMapped,
293
+ stream: true as const,
294
+ };
295
+ let defaultRole: OpenAIRoleEnum | undefined;
296
+
297
+ const streamIterable = await this.completionWithRetry(params, options);
298
+ let usage: OpenAIClient.Completions.CompletionUsage | undefined;
299
+ for await (const data of streamIterable) {
300
+ const choice = data.choices[0] as
301
+ | Partial<OpenAIClient.Chat.Completions.ChatCompletionChunk.Choice>
302
+ | undefined;
303
+ if (data.usage) {
304
+ usage = data.usage;
305
+ }
306
+ if (!choice) {
307
+ continue;
308
+ }
309
+
310
+ const { delta } = choice;
311
+ if (!delta) {
312
+ continue;
313
+ }
314
+ const chunk = this._convertOpenAIDeltaToBaseMessageChunk(
315
+ delta,
316
+ data,
317
+ defaultRole
318
+ );
319
+ if ('reasoning_content' in delta) {
320
+ chunk.additional_kwargs.reasoning_content = delta.reasoning_content;
321
+ }
322
+ defaultRole = delta.role ?? defaultRole;
323
+ const newTokenIndices = {
324
+ prompt: options.promptIndex ?? 0,
325
+ completion: choice.index ?? 0,
326
+ };
327
+ if (typeof chunk.content !== 'string') {
328
+ // eslint-disable-next-line no-console
329
+ console.log(
330
+ '[WARNING]: Received non-string content from OpenAI. This is currently not supported.'
331
+ );
332
+ continue;
333
+ }
334
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
335
+ const generationInfo: Record<string, any> = { ...newTokenIndices };
336
+ if (choice.finish_reason != null) {
337
+ generationInfo.finish_reason = choice.finish_reason;
338
+ // Only include system fingerprint in the last chunk for now
339
+ // to avoid concatenation issues
340
+ generationInfo.system_fingerprint = data.system_fingerprint;
341
+ generationInfo.model_name = data.model;
342
+ generationInfo.service_tier = data.service_tier;
343
+ }
344
+ if (this.logprobs == true) {
345
+ generationInfo.logprobs = choice.logprobs;
346
+ }
347
+ const generationChunk = new ChatGenerationChunk({
348
+ message: chunk,
349
+ text: chunk.content,
350
+ generationInfo,
351
+ });
352
+ yield generationChunk;
353
+ await runManager?.handleLLMNewToken(
354
+ generationChunk.text || '',
355
+ newTokenIndices,
356
+ undefined,
357
+ undefined,
358
+ undefined,
359
+ { chunk: generationChunk }
360
+ );
361
+ }
362
+ if (usage) {
363
+ const inputTokenDetails = {
364
+ ...(usage.prompt_tokens_details?.audio_tokens != null && {
365
+ audio: usage.prompt_tokens_details.audio_tokens,
366
+ }),
367
+ ...(usage.prompt_tokens_details?.cached_tokens != null && {
368
+ cache_read: usage.prompt_tokens_details.cached_tokens,
369
+ }),
370
+ };
371
+ const outputTokenDetails = {
372
+ ...(usage.completion_tokens_details?.audio_tokens != null && {
373
+ audio: usage.completion_tokens_details.audio_tokens,
374
+ }),
375
+ ...(usage.completion_tokens_details?.reasoning_tokens != null && {
376
+ reasoning: usage.completion_tokens_details.reasoning_tokens,
377
+ }),
378
+ };
379
+ const generationChunk = new ChatGenerationChunk({
380
+ message: new AIMessageChunk({
381
+ content: '',
382
+ response_metadata: {
383
+ usage: { ...usage },
384
+ },
385
+ usage_metadata: {
386
+ input_tokens: usage.prompt_tokens,
387
+ output_tokens: usage.completion_tokens,
388
+ total_tokens: usage.total_tokens,
389
+ ...(Object.keys(inputTokenDetails).length > 0 && {
390
+ input_token_details: inputTokenDetails,
391
+ }),
392
+ ...(Object.keys(outputTokenDetails).length > 0 && {
393
+ output_token_details: outputTokenDetails,
394
+ }),
395
+ },
396
+ }),
397
+ text: '',
398
+ });
399
+ yield generationChunk;
400
+ }
401
+ if (options.signal?.aborted === true) {
402
+ throw new Error('AbortError');
403
+ }
404
+ }
265
405
  }
266
406
 
267
407
  export class AzureChatOpenAI extends OriginalAzureChatOpenAI {