@librechat/agents 2.4.38 → 2.4.40
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/graphs/Graph.cjs +2 -1
- package/dist/cjs/graphs/Graph.cjs.map +1 -1
- package/dist/cjs/llm/anthropic/types.cjs.map +1 -1
- package/dist/cjs/llm/anthropic/utils/message_inputs.cjs +13 -3
- package/dist/cjs/llm/anthropic/utils/message_inputs.cjs.map +1 -1
- package/dist/cjs/llm/anthropic/utils/message_outputs.cjs +22 -2
- package/dist/cjs/llm/anthropic/utils/message_outputs.cjs.map +1 -1
- package/dist/cjs/llm/openai/index.cjs +101 -2
- package/dist/cjs/llm/openai/index.cjs.map +1 -1
- package/dist/esm/graphs/Graph.mjs +2 -1
- package/dist/esm/graphs/Graph.mjs.map +1 -1
- package/dist/esm/llm/anthropic/types.mjs.map +1 -1
- package/dist/esm/llm/anthropic/utils/message_inputs.mjs +13 -3
- package/dist/esm/llm/anthropic/utils/message_inputs.mjs.map +1 -1
- package/dist/esm/llm/anthropic/utils/message_outputs.mjs +22 -2
- package/dist/esm/llm/anthropic/utils/message_outputs.mjs.map +1 -1
- package/dist/esm/llm/openai/index.mjs +100 -4
- package/dist/esm/llm/openai/index.mjs.map +1 -1
- package/dist/types/llm/anthropic/types.d.ts +7 -3
- package/dist/types/llm/anthropic/utils/message_inputs.d.ts +1 -1
- package/dist/types/llm/anthropic/utils/output_parsers.d.ts +2 -2
- package/dist/types/llm/openai/index.d.ts +37 -3
- package/package.json +13 -13
- package/src/graphs/Graph.ts +3 -4
- package/src/llm/anthropic/types.ts +23 -3
- package/src/llm/anthropic/utils/message_inputs.ts +21 -5
- package/src/llm/anthropic/utils/message_outputs.ts +21 -2
- package/src/llm/anthropic/utils/output_parsers.ts +23 -4
- package/src/llm/openai/index.ts +170 -14
- package/src/scripts/simple.ts +1 -1
|
@@ -2,8 +2,40 @@ import { AzureOpenAI as AzureOpenAIClient } from 'openai';
|
|
|
2
2
|
import { ChatXAI as OriginalChatXAI } from '@langchain/xai';
|
|
3
3
|
import { ChatDeepSeek as OriginalChatDeepSeek } from '@langchain/deepseek';
|
|
4
4
|
import { OpenAIClient, ChatOpenAI as OriginalChatOpenAI, AzureChatOpenAI as OriginalAzureChatOpenAI } from '@langchain/openai';
|
|
5
|
-
import type {
|
|
5
|
+
import type { BindToolsInput } from '@langchain/core/language_models/chat_models';
|
|
6
|
+
import type { AIMessageChunk } from '@langchain/core/messages';
|
|
7
|
+
import type { Runnable } from '@langchain/core/runnables';
|
|
6
8
|
import type * as t from '@langchain/openai';
|
|
9
|
+
import { BaseLanguageModelInput } from '@langchain/core/language_models/base';
|
|
10
|
+
type ResponsesCreateParams = Parameters<OpenAIClient.Responses['create']>[0];
|
|
11
|
+
type ResponsesTool = Exclude<ResponsesCreateParams['tools'], undefined>[number];
|
|
12
|
+
type ChatOpenAIToolType = BindToolsInput | OpenAIClient.ChatCompletionTool | ResponsesTool;
|
|
13
|
+
type HeaderValue = string | undefined | null;
|
|
14
|
+
export type HeadersLike = Headers | readonly HeaderValue[][] | Record<string, HeaderValue | readonly HeaderValue[]> | undefined | null | {
|
|
15
|
+
values: Headers;
|
|
16
|
+
[key: string]: unknown;
|
|
17
|
+
};
|
|
18
|
+
export declare function isHeaders(headers: unknown): headers is Headers;
|
|
19
|
+
export declare function normalizeHeaders(headers: HeadersLike): Record<string, HeaderValue | readonly HeaderValue[]>;
|
|
20
|
+
type OpenAICoreRequestOptions = OpenAIClient.RequestOptions;
|
|
21
|
+
/**
|
|
22
|
+
* Formats a tool in either OpenAI format, or LangChain structured tool format
|
|
23
|
+
* into an OpenAI tool format. If the tool is already in OpenAI format, return without
|
|
24
|
+
* any changes. If it is in LangChain structured tool format, convert it to OpenAI tool format
|
|
25
|
+
* using OpenAI's `zodFunction` util, falling back to `convertToOpenAIFunction` if the parameters
|
|
26
|
+
* returned from the `zodFunction` util are not defined.
|
|
27
|
+
*
|
|
28
|
+
* @param {BindToolsInput} tool The tool to convert to an OpenAI tool.
|
|
29
|
+
* @param {Object} [fields] Additional fields to add to the OpenAI tool.
|
|
30
|
+
* @returns {ToolDefinition} The inputted tool in OpenAI tool format.
|
|
31
|
+
*/
|
|
32
|
+
export declare function _convertToOpenAITool(tool: BindToolsInput, fields?: {
|
|
33
|
+
/**
|
|
34
|
+
* If `true`, model output is guaranteed to exactly match the JSON Schema
|
|
35
|
+
* provided in the function definition.
|
|
36
|
+
*/
|
|
37
|
+
strict?: boolean;
|
|
38
|
+
}): OpenAIClient.ChatCompletionTool;
|
|
7
39
|
export declare class CustomOpenAIClient extends OpenAIClient {
|
|
8
40
|
abortHandler?: () => void;
|
|
9
41
|
fetchWithTimeout(url: RequestInfo, init: RequestInit | undefined, ms: number, controller: AbortController): Promise<Response>;
|
|
@@ -14,11 +46,12 @@ export declare class CustomAzureOpenAIClient extends AzureOpenAIClient {
|
|
|
14
46
|
}
|
|
15
47
|
export declare class ChatOpenAI extends OriginalChatOpenAI<t.ChatOpenAICallOptions> {
|
|
16
48
|
get exposedClient(): CustomOpenAIClient;
|
|
17
|
-
|
|
49
|
+
bindTools(tools: ChatOpenAIToolType[], kwargs?: Partial<t.ChatOpenAICallOptions>): Runnable<BaseLanguageModelInput, AIMessageChunk, t.ChatOpenAICallOptions>;
|
|
50
|
+
protected _getClientOptions(options?: OpenAICoreRequestOptions): OpenAICoreRequestOptions;
|
|
18
51
|
}
|
|
19
52
|
export declare class AzureChatOpenAI extends OriginalAzureChatOpenAI {
|
|
20
53
|
get exposedClient(): CustomOpenAIClient;
|
|
21
|
-
protected _getClientOptions(options:
|
|
54
|
+
protected _getClientOptions(options: OpenAICoreRequestOptions | undefined): OpenAICoreRequestOptions;
|
|
22
55
|
}
|
|
23
56
|
export declare class ChatDeepSeek extends OriginalChatDeepSeek {
|
|
24
57
|
get exposedClient(): CustomOpenAIClient;
|
|
@@ -28,3 +61,4 @@ export declare class ChatXAI extends OriginalChatXAI {
|
|
|
28
61
|
get exposedClient(): CustomOpenAIClient;
|
|
29
62
|
protected _getClientOptions(options?: OpenAICoreRequestOptions): OpenAICoreRequestOptions;
|
|
30
63
|
}
|
|
64
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@librechat/agents",
|
|
3
|
-
"version": "2.4.
|
|
3
|
+
"version": "2.4.40",
|
|
4
4
|
"main": "./dist/cjs/main.cjs",
|
|
5
5
|
"module": "./dist/esm/main.mjs",
|
|
6
6
|
"types": "./dist/types/index.d.ts",
|
|
@@ -72,18 +72,18 @@
|
|
|
72
72
|
"format": "prettier --write ."
|
|
73
73
|
},
|
|
74
74
|
"dependencies": {
|
|
75
|
-
"@langchain/anthropic": "^0.3.
|
|
76
|
-
"@langchain/aws": "^0.1.
|
|
77
|
-
"@langchain/community": "^0.3.
|
|
78
|
-
"@langchain/core": "^0.3.
|
|
79
|
-
"@langchain/deepseek": "^0.0.
|
|
80
|
-
"@langchain/google-genai": "^0.2.
|
|
81
|
-
"@langchain/google-vertexai": "^0.2.
|
|
82
|
-
"@langchain/langgraph": "^0.
|
|
83
|
-
"@langchain/mistralai": "^0.2.
|
|
84
|
-
"@langchain/ollama": "^0.2.
|
|
85
|
-
"@langchain/openai": "^0.5.
|
|
86
|
-
"@langchain/xai": "^0.0.
|
|
75
|
+
"@langchain/anthropic": "^0.3.23",
|
|
76
|
+
"@langchain/aws": "^0.1.11",
|
|
77
|
+
"@langchain/community": "^0.3.47",
|
|
78
|
+
"@langchain/core": "^0.3.60",
|
|
79
|
+
"@langchain/deepseek": "^0.0.2",
|
|
80
|
+
"@langchain/google-genai": "^0.2.13",
|
|
81
|
+
"@langchain/google-vertexai": "^0.2.13",
|
|
82
|
+
"@langchain/langgraph": "^0.3.4",
|
|
83
|
+
"@langchain/mistralai": "^0.2.1",
|
|
84
|
+
"@langchain/ollama": "^0.2.3",
|
|
85
|
+
"@langchain/openai": "^0.5.14",
|
|
86
|
+
"@langchain/xai": "^0.0.3",
|
|
87
87
|
"cheerio": "^1.0.0",
|
|
88
88
|
"dotenv": "^16.4.7",
|
|
89
89
|
"https-proxy-agent": "^7.0.6",
|
package/src/graphs/Graph.ts
CHANGED
|
@@ -184,10 +184,9 @@ export class StandardGraph extends Graph<t.BaseGraphState, GraphNode> {
|
|
|
184
184
|
finalInstructions &&
|
|
185
185
|
provider === Providers.ANTHROPIC &&
|
|
186
186
|
((
|
|
187
|
-
clientOptions as t.AnthropicClientOptions
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
) ??
|
|
187
|
+
(clientOptions as t.AnthropicClientOptions).clientOptions
|
|
188
|
+
?.defaultHeaders as Record<string, string> | undefined
|
|
189
|
+
)?.['anthropic-beta']?.includes('prompt-caching') ??
|
|
191
190
|
false)
|
|
192
191
|
) {
|
|
193
192
|
finalInstructions = {
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import Anthropic from '@anthropic-ai/sdk';
|
|
2
2
|
import { BindToolsInput } from '@langchain/core/language_models/chat_models';
|
|
3
3
|
|
|
4
|
+
export type AnthropicStreamUsage = Anthropic.Usage;
|
|
5
|
+
export type AnthropicMessageDeltaEvent = Anthropic.MessageDeltaEvent;
|
|
6
|
+
export type AnthropicMessageStartEvent = Anthropic.MessageStartEvent;
|
|
7
|
+
|
|
4
8
|
export type AnthropicToolResponse = {
|
|
5
9
|
type: 'tool_use';
|
|
6
10
|
id: string;
|
|
@@ -9,10 +13,7 @@ export type AnthropicToolResponse = {
|
|
|
9
13
|
input: Record<string, any>;
|
|
10
14
|
};
|
|
11
15
|
|
|
12
|
-
export type AnthropicStreamUsage = Anthropic.Usage;
|
|
13
16
|
export type AnthropicMessageParam = Anthropic.MessageParam;
|
|
14
|
-
export type AnthropicMessageDeltaEvent = Anthropic.MessageDeltaEvent;
|
|
15
|
-
export type AnthropicMessageStartEvent = Anthropic.MessageStartEvent;
|
|
16
17
|
export type AnthropicMessageResponse =
|
|
17
18
|
| Anthropic.ContentBlock
|
|
18
19
|
| AnthropicToolResponse;
|
|
@@ -42,6 +43,25 @@ export type AnthropicDocumentBlockParam = Anthropic.Messages.DocumentBlockParam;
|
|
|
42
43
|
export type AnthropicThinkingBlockParam = Anthropic.Messages.ThinkingBlockParam;
|
|
43
44
|
export type AnthropicRedactedThinkingBlockParam =
|
|
44
45
|
Anthropic.Messages.RedactedThinkingBlockParam;
|
|
46
|
+
export type AnthropicServerToolUseBlockParam =
|
|
47
|
+
Anthropic.Messages.ServerToolUseBlockParam;
|
|
48
|
+
export type AnthropicWebSearchToolResultBlockParam =
|
|
49
|
+
Anthropic.Messages.WebSearchToolResultBlockParam;
|
|
50
|
+
export type AnthropicWebSearchResultBlockParam =
|
|
51
|
+
Anthropic.Messages.WebSearchResultBlockParam;
|
|
52
|
+
|
|
53
|
+
// Union of all possible content block types including server tool use
|
|
54
|
+
export type AnthropicContentBlock =
|
|
55
|
+
| AnthropicTextBlockParam
|
|
56
|
+
| AnthropicImageBlockParam
|
|
57
|
+
| AnthropicToolUseBlockParam
|
|
58
|
+
| AnthropicToolResultBlockParam
|
|
59
|
+
| AnthropicDocumentBlockParam
|
|
60
|
+
| AnthropicThinkingBlockParam
|
|
61
|
+
| AnthropicRedactedThinkingBlockParam
|
|
62
|
+
| AnthropicServerToolUseBlockParam
|
|
63
|
+
| AnthropicWebSearchToolResultBlockParam
|
|
64
|
+
| AnthropicWebSearchResultBlockParam;
|
|
45
65
|
|
|
46
66
|
export function isAnthropicImageBlockParam(
|
|
47
67
|
block: unknown
|
|
@@ -21,7 +21,7 @@ import {
|
|
|
21
21
|
parseBase64DataUrl,
|
|
22
22
|
} from '@langchain/core/messages';
|
|
23
23
|
import { ToolCall } from '@langchain/core/messages/tool';
|
|
24
|
-
import
|
|
24
|
+
import {
|
|
25
25
|
AnthropicImageBlockParam,
|
|
26
26
|
AnthropicMessageCreateParams,
|
|
27
27
|
AnthropicTextBlockParam,
|
|
@@ -31,8 +31,10 @@ import type {
|
|
|
31
31
|
AnthropicDocumentBlockParam,
|
|
32
32
|
AnthropicThinkingBlockParam,
|
|
33
33
|
AnthropicRedactedThinkingBlockParam,
|
|
34
|
+
AnthropicServerToolUseBlockParam,
|
|
35
|
+
AnthropicWebSearchToolResultBlockParam,
|
|
36
|
+
isAnthropicImageBlockParam,
|
|
34
37
|
} from '@/llm/anthropic/types';
|
|
35
|
-
import { isAnthropicImageBlockParam } from '@/llm/anthropic/types';
|
|
36
38
|
|
|
37
39
|
function _formatImage(imageUrl: string) {
|
|
38
40
|
const parsed = parseBase64DataUrl({ dataUrl: imageUrl });
|
|
@@ -119,7 +121,6 @@ function _ensureMessageContents(
|
|
|
119
121
|
{
|
|
120
122
|
type: 'tool_result',
|
|
121
123
|
// rare case: message.content could be undefined
|
|
122
|
-
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
123
124
|
...(message.content != null
|
|
124
125
|
? { content: _formatContent(message.content) }
|
|
125
126
|
: {}),
|
|
@@ -347,7 +348,14 @@ const standardContentBlockConverter: StandardContentBlockConverter<{
|
|
|
347
348
|
};
|
|
348
349
|
|
|
349
350
|
function _formatContent(content: MessageContent) {
|
|
350
|
-
const toolTypes = [
|
|
351
|
+
const toolTypes = [
|
|
352
|
+
'tool_use',
|
|
353
|
+
'tool_result',
|
|
354
|
+
'input_json_delta',
|
|
355
|
+
'server_tool_use',
|
|
356
|
+
'web_search_tool_result',
|
|
357
|
+
'web_search_result',
|
|
358
|
+
];
|
|
351
359
|
const textTypes = ['text', 'text_delta'];
|
|
352
360
|
|
|
353
361
|
if (typeof content === 'string') {
|
|
@@ -408,6 +416,9 @@ function _formatContent(content: MessageContent) {
|
|
|
408
416
|
type: 'text' as const, // Explicitly setting the type as "text"
|
|
409
417
|
text: contentPart.text,
|
|
410
418
|
...(cacheControl ? { cache_control: cacheControl } : {}),
|
|
419
|
+
...('citations' in contentPart && contentPart.citations
|
|
420
|
+
? { citations: contentPart.citations }
|
|
421
|
+
: {}),
|
|
411
422
|
};
|
|
412
423
|
} else if (toolTypes.find((t) => t === contentPart.type)) {
|
|
413
424
|
const contentPartCopy = { ...contentPart };
|
|
@@ -502,7 +513,8 @@ export function _convertMessagesToAnthropicPayload(
|
|
|
502
513
|
content.find(
|
|
503
514
|
(contentPart) =>
|
|
504
515
|
(contentPart.type === 'tool_use' ||
|
|
505
|
-
contentPart.type === 'input_json_delta'
|
|
516
|
+
contentPart.type === 'input_json_delta' ||
|
|
517
|
+
contentPart.type === 'server_tool_use') &&
|
|
506
518
|
contentPart.id === toolCall.id
|
|
507
519
|
)
|
|
508
520
|
);
|
|
@@ -548,6 +560,8 @@ function mergeMessages(messages: AnthropicMessageCreateParams['messages']) {
|
|
|
548
560
|
| AnthropicDocumentBlockParam
|
|
549
561
|
| AnthropicThinkingBlockParam
|
|
550
562
|
| AnthropicRedactedThinkingBlockParam
|
|
563
|
+
| AnthropicServerToolUseBlockParam
|
|
564
|
+
| AnthropicWebSearchToolResultBlockParam
|
|
551
565
|
>
|
|
552
566
|
): Array<
|
|
553
567
|
| AnthropicTextBlockParam
|
|
@@ -557,6 +571,8 @@ function mergeMessages(messages: AnthropicMessageCreateParams['messages']) {
|
|
|
557
571
|
| AnthropicDocumentBlockParam
|
|
558
572
|
| AnthropicThinkingBlockParam
|
|
559
573
|
| AnthropicRedactedThinkingBlockParam
|
|
574
|
+
| AnthropicServerToolUseBlockParam
|
|
575
|
+
| AnthropicWebSearchToolResultBlockParam
|
|
560
576
|
> => {
|
|
561
577
|
if (typeof content === 'string') {
|
|
562
578
|
return [
|
|
@@ -77,7 +77,12 @@ export function _makeMessageChunkFromAnthropicEvent(
|
|
|
77
77
|
};
|
|
78
78
|
} else if (
|
|
79
79
|
data.type === 'content_block_start' &&
|
|
80
|
-
[
|
|
80
|
+
[
|
|
81
|
+
'tool_use',
|
|
82
|
+
'document',
|
|
83
|
+
'server_tool_use',
|
|
84
|
+
'web_search_tool_result',
|
|
85
|
+
].includes(data.content_block.type)
|
|
81
86
|
) {
|
|
82
87
|
const contentBlock = data.content_block;
|
|
83
88
|
let toolCallChunks: ToolCallChunk[];
|
|
@@ -90,6 +95,16 @@ export function _makeMessageChunkFromAnthropicEvent(
|
|
|
90
95
|
args: '',
|
|
91
96
|
},
|
|
92
97
|
];
|
|
98
|
+
} else if (contentBlock.type === 'server_tool_use') {
|
|
99
|
+
// Handle anthropic built-in server tool use (like web search)
|
|
100
|
+
toolCallChunks = [
|
|
101
|
+
{
|
|
102
|
+
id: contentBlock.id,
|
|
103
|
+
index: data.index,
|
|
104
|
+
name: contentBlock.name,
|
|
105
|
+
args: '',
|
|
106
|
+
},
|
|
107
|
+
];
|
|
93
108
|
} else {
|
|
94
109
|
toolCallChunks = [];
|
|
95
110
|
}
|
|
@@ -101,7 +116,11 @@ export function _makeMessageChunkFromAnthropicEvent(
|
|
|
101
116
|
{
|
|
102
117
|
index: data.index,
|
|
103
118
|
...data.content_block,
|
|
104
|
-
input:
|
|
119
|
+
input:
|
|
120
|
+
contentBlock.type === 'server_tool_use' ||
|
|
121
|
+
contentBlock.type === 'tool_use'
|
|
122
|
+
? ''
|
|
123
|
+
: undefined,
|
|
105
124
|
},
|
|
106
125
|
],
|
|
107
126
|
additional_kwargs: {},
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
|
2
2
|
/* eslint-disable @typescript-eslint/no-empty-object-type */
|
|
3
|
-
import { z } from 'zod';
|
|
4
3
|
import {
|
|
5
4
|
BaseLLMOutputParser,
|
|
6
5
|
OutputParserException,
|
|
7
6
|
} from '@langchain/core/output_parsers';
|
|
8
7
|
import { JsonOutputKeyToolsParserParams } from '@langchain/core/output_parsers/openai_tools';
|
|
8
|
+
import {
|
|
9
|
+
interopSafeParseAsync,
|
|
10
|
+
InteropZodType,
|
|
11
|
+
} from '@langchain/core/utils/types';
|
|
9
12
|
import { ChatGeneration } from '@langchain/core/outputs';
|
|
10
13
|
import { ToolCall } from '@langchain/core/messages/tool';
|
|
11
14
|
|
|
@@ -31,7 +34,7 @@ export class AnthropicToolsOutputParser<
|
|
|
31
34
|
/** Whether to return only the first tool call. */
|
|
32
35
|
returnSingle = false;
|
|
33
36
|
|
|
34
|
-
zodSchema?:
|
|
37
|
+
zodSchema?: InteropZodType<T>;
|
|
35
38
|
|
|
36
39
|
constructor(params: AnthropicToolsOutputParserParams<T>) {
|
|
37
40
|
super(params);
|
|
@@ -62,7 +65,10 @@ export class AnthropicToolsOutputParser<
|
|
|
62
65
|
if (this.zodSchema === undefined) {
|
|
63
66
|
return parsedResult as T;
|
|
64
67
|
}
|
|
65
|
-
const zodParsedResult = await
|
|
68
|
+
const zodParsedResult = await interopSafeParseAsync(
|
|
69
|
+
this.zodSchema,
|
|
70
|
+
parsedResult
|
|
71
|
+
);
|
|
66
72
|
if (zodParsedResult.success) {
|
|
67
73
|
return zodParsedResult.data;
|
|
68
74
|
} else {
|
|
@@ -71,7 +77,7 @@ export class AnthropicToolsOutputParser<
|
|
|
71
77
|
result,
|
|
72
78
|
null,
|
|
73
79
|
2
|
|
74
|
-
)}". Error: ${JSON.stringify(zodParsedResult.error.
|
|
80
|
+
)}". Error: ${JSON.stringify(zodParsedResult.error.issues)}`,
|
|
75
81
|
JSON.stringify(parsedResult, null, 2)
|
|
76
82
|
);
|
|
77
83
|
}
|
|
@@ -100,6 +106,7 @@ export class AnthropicToolsOutputParser<
|
|
|
100
106
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
101
107
|
export function extractToolCalls(content: Record<string, any>[]) {
|
|
102
108
|
const toolCalls: ToolCall[] = [];
|
|
109
|
+
|
|
103
110
|
for (const block of content) {
|
|
104
111
|
if (block.type === 'tool_use') {
|
|
105
112
|
toolCalls.push({
|
|
@@ -108,7 +115,19 @@ export function extractToolCalls(content: Record<string, any>[]) {
|
|
|
108
115
|
id: block.id,
|
|
109
116
|
type: 'tool_call',
|
|
110
117
|
});
|
|
118
|
+
} else if (
|
|
119
|
+
block.type === 'server_tool_use' &&
|
|
120
|
+
block.name === 'web_search'
|
|
121
|
+
) {
|
|
122
|
+
// Handle Anthropic built-in web search tool
|
|
123
|
+
toolCalls.push({
|
|
124
|
+
name: block.name,
|
|
125
|
+
args: block.input,
|
|
126
|
+
id: block.id,
|
|
127
|
+
type: 'tool_call',
|
|
128
|
+
});
|
|
111
129
|
}
|
|
112
130
|
}
|
|
131
|
+
|
|
113
132
|
return toolCalls;
|
|
114
133
|
}
|
package/src/llm/openai/index.ts
CHANGED
|
@@ -4,17 +4,150 @@ import { ChatDeepSeek as OriginalChatDeepSeek } from '@langchain/deepseek';
|
|
|
4
4
|
import {
|
|
5
5
|
getEndpoint,
|
|
6
6
|
OpenAIClient,
|
|
7
|
+
formatToOpenAITool,
|
|
7
8
|
ChatOpenAI as OriginalChatOpenAI,
|
|
8
9
|
AzureChatOpenAI as OriginalAzureChatOpenAI,
|
|
9
10
|
} from '@langchain/openai';
|
|
10
|
-
import
|
|
11
|
+
import { isLangChainTool } from '@langchain/core/utils/function_calling';
|
|
12
|
+
import type { BindToolsInput } from '@langchain/core/language_models/chat_models';
|
|
13
|
+
import type { OpenAIEndpointConfig } from '@langchain/openai/dist/utils/azure';
|
|
14
|
+
import type { AIMessageChunk } from '@langchain/core/messages';
|
|
15
|
+
import type { Runnable } from '@langchain/core/runnables';
|
|
11
16
|
import type * as t from '@langchain/openai';
|
|
17
|
+
import {
|
|
18
|
+
isOpenAITool,
|
|
19
|
+
ToolDefinition,
|
|
20
|
+
BaseLanguageModelInput,
|
|
21
|
+
} from '@langchain/core/language_models/base';
|
|
22
|
+
|
|
23
|
+
type ResponsesCreateParams = Parameters<OpenAIClient.Responses['create']>[0];
|
|
24
|
+
type ResponsesTool = Exclude<ResponsesCreateParams['tools'], undefined>[number];
|
|
25
|
+
|
|
26
|
+
type ChatOpenAIToolType =
|
|
27
|
+
| BindToolsInput
|
|
28
|
+
| OpenAIClient.ChatCompletionTool
|
|
29
|
+
| ResponsesTool;
|
|
30
|
+
|
|
31
|
+
type HeaderValue = string | undefined | null;
|
|
32
|
+
export type HeadersLike =
|
|
33
|
+
| Headers
|
|
34
|
+
| readonly HeaderValue[][]
|
|
35
|
+
| Record<string, HeaderValue | readonly HeaderValue[]>
|
|
36
|
+
| undefined
|
|
37
|
+
| null
|
|
38
|
+
// NullableHeaders
|
|
39
|
+
| { values: Headers; [key: string]: unknown };
|
|
40
|
+
|
|
41
|
+
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
|
|
42
|
+
const iife = <T>(fn: () => T) => fn();
|
|
43
|
+
|
|
44
|
+
export function isHeaders(headers: unknown): headers is Headers {
|
|
45
|
+
return (
|
|
46
|
+
typeof Headers !== 'undefined' &&
|
|
47
|
+
headers !== null &&
|
|
48
|
+
typeof headers === 'object' &&
|
|
49
|
+
Object.prototype.toString.call(headers) === '[object Headers]'
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function normalizeHeaders(
|
|
54
|
+
headers: HeadersLike
|
|
55
|
+
): Record<string, HeaderValue | readonly HeaderValue[]> {
|
|
56
|
+
const output = iife(() => {
|
|
57
|
+
// If headers is a Headers instance
|
|
58
|
+
if (isHeaders(headers)) {
|
|
59
|
+
return headers;
|
|
60
|
+
}
|
|
61
|
+
// If headers is an array of [key, value] pairs
|
|
62
|
+
else if (Array.isArray(headers)) {
|
|
63
|
+
return new Headers(headers);
|
|
64
|
+
}
|
|
65
|
+
// If headers is a NullableHeaders-like object (has 'values' property that is a Headers)
|
|
66
|
+
else if (
|
|
67
|
+
typeof headers === 'object' &&
|
|
68
|
+
headers !== null &&
|
|
69
|
+
'values' in headers &&
|
|
70
|
+
isHeaders(headers.values)
|
|
71
|
+
) {
|
|
72
|
+
return headers.values;
|
|
73
|
+
}
|
|
74
|
+
// If headers is a plain object
|
|
75
|
+
else if (typeof headers === 'object' && headers !== null) {
|
|
76
|
+
const entries: [string, string][] = Object.entries(headers)
|
|
77
|
+
.filter(([, v]) => typeof v === 'string')
|
|
78
|
+
.map(([k, v]) => [k, v as string]);
|
|
79
|
+
return new Headers(entries);
|
|
80
|
+
}
|
|
81
|
+
return new Headers();
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
return Object.fromEntries(output.entries());
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
type OpenAICoreRequestOptions = OpenAIClient.RequestOptions;
|
|
12
88
|
|
|
13
89
|
function createAbortHandler(controller: AbortController): () => void {
|
|
14
90
|
return function (): void {
|
|
15
91
|
controller.abort();
|
|
16
92
|
};
|
|
17
93
|
}
|
|
94
|
+
/**
|
|
95
|
+
* Formats a tool in either OpenAI format, or LangChain structured tool format
|
|
96
|
+
* into an OpenAI tool format. If the tool is already in OpenAI format, return without
|
|
97
|
+
* any changes. If it is in LangChain structured tool format, convert it to OpenAI tool format
|
|
98
|
+
* using OpenAI's `zodFunction` util, falling back to `convertToOpenAIFunction` if the parameters
|
|
99
|
+
* returned from the `zodFunction` util are not defined.
|
|
100
|
+
*
|
|
101
|
+
* @param {BindToolsInput} tool The tool to convert to an OpenAI tool.
|
|
102
|
+
* @param {Object} [fields] Additional fields to add to the OpenAI tool.
|
|
103
|
+
* @returns {ToolDefinition} The inputted tool in OpenAI tool format.
|
|
104
|
+
*/
|
|
105
|
+
export function _convertToOpenAITool(
|
|
106
|
+
tool: BindToolsInput,
|
|
107
|
+
fields?: {
|
|
108
|
+
/**
|
|
109
|
+
* If `true`, model output is guaranteed to exactly match the JSON Schema
|
|
110
|
+
* provided in the function definition.
|
|
111
|
+
*/
|
|
112
|
+
strict?: boolean;
|
|
113
|
+
}
|
|
114
|
+
): OpenAIClient.ChatCompletionTool {
|
|
115
|
+
let toolDef: OpenAIClient.ChatCompletionTool | undefined;
|
|
116
|
+
|
|
117
|
+
if (isLangChainTool(tool)) {
|
|
118
|
+
toolDef = formatToOpenAITool(tool);
|
|
119
|
+
} else {
|
|
120
|
+
toolDef = tool as ToolDefinition;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
if (fields?.strict !== undefined) {
|
|
124
|
+
toolDef.function.strict = fields.strict;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return toolDef;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function _convertChatOpenAIToolTypeToOpenAITool(
|
|
131
|
+
tool: ChatOpenAIToolType,
|
|
132
|
+
fields?: {
|
|
133
|
+
strict?: boolean;
|
|
134
|
+
}
|
|
135
|
+
): OpenAIClient.ChatCompletionTool {
|
|
136
|
+
if (isOpenAITool(tool)) {
|
|
137
|
+
if (fields?.strict !== undefined) {
|
|
138
|
+
return {
|
|
139
|
+
...tool,
|
|
140
|
+
function: {
|
|
141
|
+
...tool.function,
|
|
142
|
+
strict: fields.strict,
|
|
143
|
+
},
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return tool;
|
|
148
|
+
}
|
|
149
|
+
return _convertToOpenAITool(tool, fields);
|
|
150
|
+
}
|
|
18
151
|
|
|
19
152
|
export class CustomOpenAIClient extends OpenAIClient {
|
|
20
153
|
abortHandler?: () => void;
|
|
@@ -87,13 +220,36 @@ export class CustomAzureOpenAIClient extends AzureOpenAIClient {
|
|
|
87
220
|
}
|
|
88
221
|
}
|
|
89
222
|
|
|
223
|
+
function isBuiltInTool(tool: ChatOpenAIToolType): tool is ResponsesTool {
|
|
224
|
+
return 'type' in tool && tool.type !== 'function';
|
|
225
|
+
}
|
|
226
|
+
|
|
90
227
|
export class ChatOpenAI extends OriginalChatOpenAI<t.ChatOpenAICallOptions> {
|
|
91
228
|
public get exposedClient(): CustomOpenAIClient {
|
|
92
229
|
return this.client;
|
|
93
230
|
}
|
|
231
|
+
override bindTools(
|
|
232
|
+
tools: ChatOpenAIToolType[],
|
|
233
|
+
kwargs?: Partial<t.ChatOpenAICallOptions>
|
|
234
|
+
): Runnable<BaseLanguageModelInput, AIMessageChunk, t.ChatOpenAICallOptions> {
|
|
235
|
+
let strict: boolean | undefined;
|
|
236
|
+
if (kwargs?.strict !== undefined) {
|
|
237
|
+
strict = kwargs.strict;
|
|
238
|
+
} else if (this.supportsStrictToolCalling !== undefined) {
|
|
239
|
+
strict = this.supportsStrictToolCalling;
|
|
240
|
+
}
|
|
241
|
+
return this.withConfig({
|
|
242
|
+
tools: tools.map((tool) =>
|
|
243
|
+
isBuiltInTool(tool)
|
|
244
|
+
? tool
|
|
245
|
+
: _convertChatOpenAIToolTypeToOpenAITool(tool, { strict })
|
|
246
|
+
),
|
|
247
|
+
...kwargs,
|
|
248
|
+
} as Partial<t.ChatOpenAICallOptions>);
|
|
249
|
+
}
|
|
94
250
|
protected _getClientOptions(
|
|
95
|
-
options?:
|
|
96
|
-
):
|
|
251
|
+
options?: OpenAICoreRequestOptions
|
|
252
|
+
): OpenAICoreRequestOptions {
|
|
97
253
|
if (!(this.client as OpenAIClient | undefined)) {
|
|
98
254
|
const openAIEndpointConfig: t.OpenAIEndpointConfig = {
|
|
99
255
|
baseURL: this.clientConfig.baseURL,
|
|
@@ -115,7 +271,7 @@ export class ChatOpenAI extends OriginalChatOpenAI<t.ChatOpenAICallOptions> {
|
|
|
115
271
|
const requestOptions = {
|
|
116
272
|
...this.clientConfig,
|
|
117
273
|
...options,
|
|
118
|
-
} as
|
|
274
|
+
} as OpenAICoreRequestOptions;
|
|
119
275
|
return requestOptions;
|
|
120
276
|
}
|
|
121
277
|
}
|
|
@@ -125,10 +281,10 @@ export class AzureChatOpenAI extends OriginalAzureChatOpenAI {
|
|
|
125
281
|
return this.client;
|
|
126
282
|
}
|
|
127
283
|
protected _getClientOptions(
|
|
128
|
-
options:
|
|
129
|
-
):
|
|
130
|
-
if (!(this.client as AzureOpenAIClient | undefined)) {
|
|
131
|
-
const openAIEndpointConfig:
|
|
284
|
+
options: OpenAICoreRequestOptions | undefined
|
|
285
|
+
): OpenAICoreRequestOptions {
|
|
286
|
+
if (!(this.client as unknown as AzureOpenAIClient | undefined)) {
|
|
287
|
+
const openAIEndpointConfig: OpenAIEndpointConfig = {
|
|
132
288
|
azureOpenAIApiDeploymentName: this.azureOpenAIApiDeploymentName,
|
|
133
289
|
azureOpenAIApiInstanceName: this.azureOpenAIApiInstanceName,
|
|
134
290
|
azureOpenAIApiKey: this.azureOpenAIApiKey,
|
|
@@ -154,25 +310,26 @@ export class AzureChatOpenAI extends OriginalAzureChatOpenAI {
|
|
|
154
310
|
delete params.baseURL;
|
|
155
311
|
}
|
|
156
312
|
|
|
313
|
+
const defaultHeaders = normalizeHeaders(params.defaultHeaders);
|
|
157
314
|
params.defaultHeaders = {
|
|
158
315
|
...params.defaultHeaders,
|
|
159
316
|
'User-Agent':
|
|
160
|
-
|
|
161
|
-
? `${
|
|
317
|
+
defaultHeaders['User-Agent'] != null
|
|
318
|
+
? `${defaultHeaders['User-Agent']}: langchainjs-azure-openai-v2`
|
|
162
319
|
: 'langchainjs-azure-openai-v2',
|
|
163
320
|
};
|
|
164
321
|
|
|
165
322
|
this.client = new CustomAzureOpenAIClient({
|
|
166
323
|
apiVersion: this.azureOpenAIApiVersion,
|
|
167
324
|
azureADTokenProvider: this.azureADTokenProvider,
|
|
168
|
-
...params,
|
|
169
|
-
});
|
|
325
|
+
...(params as t.AzureOpenAIInput),
|
|
326
|
+
}) as unknown as CustomOpenAIClient;
|
|
170
327
|
}
|
|
171
328
|
|
|
172
329
|
const requestOptions = {
|
|
173
330
|
...this.clientConfig,
|
|
174
331
|
...options,
|
|
175
|
-
} as
|
|
332
|
+
} as OpenAICoreRequestOptions;
|
|
176
333
|
if (this.azureOpenAIApiKey != null) {
|
|
177
334
|
requestOptions.headers = {
|
|
178
335
|
'api-key': this.azureOpenAIApiKey,
|
|
@@ -186,7 +343,6 @@ export class AzureChatOpenAI extends OriginalAzureChatOpenAI {
|
|
|
186
343
|
return requestOptions;
|
|
187
344
|
}
|
|
188
345
|
}
|
|
189
|
-
|
|
190
346
|
export class ChatDeepSeek extends OriginalChatDeepSeek {
|
|
191
347
|
public get exposedClient(): CustomOpenAIClient {
|
|
192
348
|
return this.client;
|
package/src/scripts/simple.ts
CHANGED
|
@@ -99,7 +99,7 @@ async function testStandardStreaming(): Promise<void> {
|
|
|
99
99
|
const openAIConfig = llmConfig as t.OpenAIClientOptions;
|
|
100
100
|
if (openAIConfig.configuration) {
|
|
101
101
|
openAIConfig.configuration.fetch = (
|
|
102
|
-
url:
|
|
102
|
+
url: string | URL | Request,
|
|
103
103
|
init?: RequestInit
|
|
104
104
|
) => {
|
|
105
105
|
console.log('Fetching:', url);
|