@librechat/agents 2.4.54 → 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.
- package/dist/cjs/llm/openai/index.cjs +106 -2
- package/dist/cjs/llm/openai/index.cjs.map +1 -1
- package/dist/cjs/llm/openai/utils/index.cjs +55 -0
- package/dist/cjs/llm/openai/utils/index.cjs.map +1 -1
- package/dist/cjs/stream.cjs +6 -1
- package/dist/cjs/stream.cjs.map +1 -1
- package/dist/cjs/tools/handlers.cjs +1 -1
- package/dist/cjs/tools/handlers.cjs.map +1 -1
- package/dist/esm/llm/openai/index.mjs +107 -3
- package/dist/esm/llm/openai/index.mjs.map +1 -1
- package/dist/esm/llm/openai/utils/index.mjs +56 -2
- package/dist/esm/llm/openai/utils/index.mjs.map +1 -1
- package/dist/esm/stream.mjs +6 -1
- package/dist/esm/stream.mjs.map +1 -1
- package/dist/esm/tools/handlers.mjs +1 -1
- package/dist/esm/tools/handlers.mjs.map +1 -1
- package/dist/types/llm/openai/index.d.ts +3 -2
- package/package.json +1 -1
- package/src/llm/openai/index.ts +145 -5
- package/src/stream.ts +8 -1
- package/src/tools/handlers.ts +2 -2
package/src/llm/openai/index.ts
CHANGED
|
@@ -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 {
|
|
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*
|
|
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 {
|
package/src/stream.ts
CHANGED
|
@@ -473,12 +473,19 @@ export function createContentAggregator(): t.ContentAggregatorResult {
|
|
|
473
473
|
|
|
474
474
|
const toolCallArgs = (contentPart.tool_call as t.ToolCallPart).args;
|
|
475
475
|
/** When args are a valid object, they are likely already invoked */
|
|
476
|
-
|
|
476
|
+
let args =
|
|
477
477
|
finalUpdate ||
|
|
478
478
|
typeof existingContent?.tool_call?.args === 'object' ||
|
|
479
479
|
typeof toolCallArgs === 'object'
|
|
480
480
|
? contentPart.tool_call.args
|
|
481
481
|
: (existingContent?.tool_call?.args ?? '') + (toolCallArgs ?? '');
|
|
482
|
+
if (
|
|
483
|
+
finalUpdate &&
|
|
484
|
+
args == null &&
|
|
485
|
+
existingContent?.tool_call?.args != null
|
|
486
|
+
) {
|
|
487
|
+
args = existingContent.tool_call.args;
|
|
488
|
+
}
|
|
482
489
|
|
|
483
490
|
const id =
|
|
484
491
|
getNonEmptyValue([
|
package/src/tools/handlers.ts
CHANGED
|
@@ -279,7 +279,7 @@ function handleAnthropicSearchResults({
|
|
|
279
279
|
graph,
|
|
280
280
|
}: {
|
|
281
281
|
contentPart: t.ToolResultContent;
|
|
282
|
-
toolCall: ToolCall
|
|
282
|
+
toolCall: Partial<ToolCall>;
|
|
283
283
|
metadata?: Record<string, unknown>;
|
|
284
284
|
graph: Graph;
|
|
285
285
|
}): void {
|
|
@@ -306,7 +306,7 @@ function handleAnthropicSearchResults({
|
|
|
306
306
|
});
|
|
307
307
|
|
|
308
308
|
const name = toolCall.name;
|
|
309
|
-
const input = toolCall.args;
|
|
309
|
+
const input = toolCall.args ?? {};
|
|
310
310
|
const artifact = {
|
|
311
311
|
[Constants.WEB_SEARCH]: searchResultData,
|
|
312
312
|
};
|