@librechat/agents 2.4.21 → 2.4.30
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/anthropic/index.cjs +1 -1
- package/dist/cjs/llm/anthropic/index.cjs.map +1 -1
- package/dist/cjs/llm/anthropic/types.cjs +50 -0
- package/dist/cjs/llm/anthropic/types.cjs.map +1 -0
- package/dist/cjs/llm/anthropic/utils/message_inputs.cjs +227 -21
- package/dist/cjs/llm/anthropic/utils/message_inputs.cjs.map +1 -1
- package/dist/cjs/llm/anthropic/utils/message_outputs.cjs +1 -0
- package/dist/cjs/llm/anthropic/utils/message_outputs.cjs.map +1 -1
- package/dist/cjs/llm/openai/index.cjs.map +1 -1
- package/dist/cjs/messages/core.cjs +91 -51
- package/dist/cjs/messages/core.cjs.map +1 -1
- package/dist/cjs/run.cjs.map +1 -1
- package/dist/esm/llm/anthropic/index.mjs +1 -1
- package/dist/esm/llm/anthropic/index.mjs.map +1 -1
- package/dist/esm/llm/anthropic/types.mjs +48 -0
- package/dist/esm/llm/anthropic/types.mjs.map +1 -0
- package/dist/esm/llm/anthropic/utils/message_inputs.mjs +228 -22
- package/dist/esm/llm/anthropic/utils/message_inputs.mjs.map +1 -1
- package/dist/esm/llm/anthropic/utils/message_outputs.mjs +1 -0
- package/dist/esm/llm/anthropic/utils/message_outputs.mjs.map +1 -1
- package/dist/esm/llm/openai/index.mjs.map +1 -1
- package/dist/esm/messages/core.mjs +91 -51
- package/dist/esm/messages/core.mjs.map +1 -1
- package/dist/esm/run.mjs.map +1 -1
- package/dist/types/llm/anthropic/index.d.ts +3 -4
- package/dist/types/llm/anthropic/types.d.ts +4 -35
- package/dist/types/llm/anthropic/utils/message_inputs.d.ts +2 -2
- package/dist/types/llm/anthropic/utils/message_outputs.d.ts +1 -3
- package/dist/types/llm/anthropic/utils/output_parsers.d.ts +22 -0
- package/dist/types/llm/openai/index.d.ts +3 -2
- package/dist/types/messages/core.d.ts +1 -1
- package/dist/types/tools/example.d.ts +21 -3
- package/package.json +9 -9
- package/src/llm/anthropic/index.ts +6 -5
- package/src/llm/anthropic/llm.spec.ts +176 -179
- package/src/llm/anthropic/types.ts +64 -39
- package/src/llm/anthropic/utils/message_inputs.ts +275 -37
- package/src/llm/anthropic/utils/message_outputs.ts +4 -21
- package/src/llm/anthropic/utils/output_parsers.ts +114 -0
- package/src/llm/openai/index.ts +7 -6
- package/src/messages/core.ts +197 -96
- package/src/run.ts +1 -1
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
|
2
|
+
/* eslint-disable @typescript-eslint/no-empty-object-type */
|
|
3
|
+
import { z } from 'zod';
|
|
4
|
+
import {
|
|
5
|
+
BaseLLMOutputParser,
|
|
6
|
+
OutputParserException,
|
|
7
|
+
} from '@langchain/core/output_parsers';
|
|
8
|
+
import { JsonOutputKeyToolsParserParams } from '@langchain/core/output_parsers/openai_tools';
|
|
9
|
+
import { ChatGeneration } from '@langchain/core/outputs';
|
|
10
|
+
import { ToolCall } from '@langchain/core/messages/tool';
|
|
11
|
+
|
|
12
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
13
|
+
interface AnthropicToolsOutputParserParams<T extends Record<string, any>>
|
|
14
|
+
extends JsonOutputKeyToolsParserParams<T> {}
|
|
15
|
+
|
|
16
|
+
export class AnthropicToolsOutputParser<
|
|
17
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
18
|
+
T extends Record<string, any> = Record<string, any>,
|
|
19
|
+
> extends BaseLLMOutputParser<T> {
|
|
20
|
+
static lc_name() {
|
|
21
|
+
return 'AnthropicToolsOutputParser';
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
lc_namespace = ['langchain', 'anthropic', 'output_parsers'];
|
|
25
|
+
|
|
26
|
+
returnId = false;
|
|
27
|
+
|
|
28
|
+
/** The type of tool calls to return. */
|
|
29
|
+
keyName: string;
|
|
30
|
+
|
|
31
|
+
/** Whether to return only the first tool call. */
|
|
32
|
+
returnSingle = false;
|
|
33
|
+
|
|
34
|
+
zodSchema?: z.ZodType<T>;
|
|
35
|
+
|
|
36
|
+
constructor(params: AnthropicToolsOutputParserParams<T>) {
|
|
37
|
+
super(params);
|
|
38
|
+
this.keyName = params.keyName;
|
|
39
|
+
this.returnSingle = params.returnSingle ?? this.returnSingle;
|
|
40
|
+
this.zodSchema = params.zodSchema;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
protected async _validateResult(result: unknown): Promise<T> {
|
|
44
|
+
let parsedResult = result;
|
|
45
|
+
if (typeof result === 'string') {
|
|
46
|
+
try {
|
|
47
|
+
parsedResult = JSON.parse(result);
|
|
48
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
49
|
+
} catch (e: any) {
|
|
50
|
+
throw new OutputParserException(
|
|
51
|
+
`Failed to parse. Text: "${JSON.stringify(
|
|
52
|
+
result,
|
|
53
|
+
null,
|
|
54
|
+
2
|
|
55
|
+
)}". Error: ${JSON.stringify(e.message)}`,
|
|
56
|
+
result
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
} else {
|
|
60
|
+
parsedResult = result;
|
|
61
|
+
}
|
|
62
|
+
if (this.zodSchema === undefined) {
|
|
63
|
+
return parsedResult as T;
|
|
64
|
+
}
|
|
65
|
+
const zodParsedResult = await this.zodSchema.safeParseAsync(parsedResult);
|
|
66
|
+
if (zodParsedResult.success) {
|
|
67
|
+
return zodParsedResult.data;
|
|
68
|
+
} else {
|
|
69
|
+
throw new OutputParserException(
|
|
70
|
+
`Failed to parse. Text: "${JSON.stringify(
|
|
71
|
+
result,
|
|
72
|
+
null,
|
|
73
|
+
2
|
|
74
|
+
)}". Error: ${JSON.stringify(zodParsedResult.error.errors)}`,
|
|
75
|
+
JSON.stringify(parsedResult, null, 2)
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
async parseResult(generations: ChatGeneration[]): Promise<T> {
|
|
81
|
+
const tools = generations.flatMap((generation) => {
|
|
82
|
+
const { message } = generation;
|
|
83
|
+
if (!Array.isArray(message.content)) {
|
|
84
|
+
return [];
|
|
85
|
+
}
|
|
86
|
+
const tool = extractToolCalls(message.content)[0];
|
|
87
|
+
return tool;
|
|
88
|
+
});
|
|
89
|
+
if (tools[0] === undefined) {
|
|
90
|
+
throw new Error(
|
|
91
|
+
'No parseable tool calls provided to AnthropicToolsOutputParser.'
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
const [tool] = tools;
|
|
95
|
+
const validatedResult = await this._validateResult(tool.args);
|
|
96
|
+
return validatedResult;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
101
|
+
export function extractToolCalls(content: Record<string, any>[]) {
|
|
102
|
+
const toolCalls: ToolCall[] = [];
|
|
103
|
+
for (const block of content) {
|
|
104
|
+
if (block.type === 'tool_use') {
|
|
105
|
+
toolCalls.push({
|
|
106
|
+
name: block.name,
|
|
107
|
+
args: block.input,
|
|
108
|
+
id: block.id,
|
|
109
|
+
type: 'tool_call',
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return toolCalls;
|
|
114
|
+
}
|
package/src/llm/openai/index.ts
CHANGED
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
ChatOpenAI as OriginalChatOpenAI,
|
|
8
8
|
AzureChatOpenAI as OriginalAzureChatOpenAI,
|
|
9
9
|
} from '@langchain/openai';
|
|
10
|
+
import type { OpenAICoreRequestOptions } from 'node_modules/@langchain/deepseek/node_modules/@langchain/openai';
|
|
10
11
|
import type * as t from '@langchain/openai';
|
|
11
12
|
|
|
12
13
|
function createAbortHandler(controller: AbortController): () => void {
|
|
@@ -191,8 +192,8 @@ export class ChatDeepSeek extends OriginalChatDeepSeek {
|
|
|
191
192
|
return this.client;
|
|
192
193
|
}
|
|
193
194
|
protected _getClientOptions(
|
|
194
|
-
options?:
|
|
195
|
-
):
|
|
195
|
+
options?: OpenAICoreRequestOptions
|
|
196
|
+
): OpenAICoreRequestOptions {
|
|
196
197
|
if (!(this.client as OpenAIClient | undefined)) {
|
|
197
198
|
const openAIEndpointConfig: t.OpenAIEndpointConfig = {
|
|
198
199
|
baseURL: this.clientConfig.baseURL,
|
|
@@ -214,7 +215,7 @@ export class ChatDeepSeek extends OriginalChatDeepSeek {
|
|
|
214
215
|
const requestOptions = {
|
|
215
216
|
...this.clientConfig,
|
|
216
217
|
...options,
|
|
217
|
-
} as
|
|
218
|
+
} as OpenAICoreRequestOptions;
|
|
218
219
|
return requestOptions;
|
|
219
220
|
}
|
|
220
221
|
}
|
|
@@ -224,8 +225,8 @@ export class ChatXAI extends OriginalChatXAI {
|
|
|
224
225
|
return this.client;
|
|
225
226
|
}
|
|
226
227
|
protected _getClientOptions(
|
|
227
|
-
options?:
|
|
228
|
-
):
|
|
228
|
+
options?: OpenAICoreRequestOptions
|
|
229
|
+
): OpenAICoreRequestOptions {
|
|
229
230
|
if (!(this.client as OpenAIClient | undefined)) {
|
|
230
231
|
const openAIEndpointConfig: t.OpenAIEndpointConfig = {
|
|
231
232
|
baseURL: this.clientConfig.baseURL,
|
|
@@ -247,7 +248,7 @@ export class ChatXAI extends OriginalChatXAI {
|
|
|
247
248
|
const requestOptions = {
|
|
248
249
|
...this.clientConfig,
|
|
249
250
|
...options,
|
|
250
|
-
} as
|
|
251
|
+
} as OpenAICoreRequestOptions;
|
|
251
252
|
return requestOptions;
|
|
252
253
|
}
|
|
253
254
|
}
|
package/src/messages/core.ts
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
// src/messages.ts
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
AIMessageChunk,
|
|
4
|
+
HumanMessage,
|
|
5
|
+
ToolMessage,
|
|
6
|
+
AIMessage,
|
|
7
|
+
BaseMessage,
|
|
8
|
+
} from '@langchain/core/messages';
|
|
3
9
|
import type { ToolCall } from '@langchain/core/messages/tool';
|
|
4
10
|
import type * as t from '@/types';
|
|
5
11
|
import { Providers } from '@/common';
|
|
@@ -7,7 +13,7 @@ import { Providers } from '@/common';
|
|
|
7
13
|
export function getConverseOverrideMessage({
|
|
8
14
|
userMessage,
|
|
9
15
|
lastMessageX,
|
|
10
|
-
lastMessageY
|
|
16
|
+
lastMessageY,
|
|
11
17
|
}: {
|
|
12
18
|
userMessage: string[];
|
|
13
19
|
lastMessageX: AIMessageChunk | null;
|
|
@@ -43,13 +49,22 @@ const allowedTypesByProvider: Record<string, string[]> = {
|
|
|
43
49
|
const modifyContent = ({
|
|
44
50
|
provider,
|
|
45
51
|
messageType,
|
|
46
|
-
content
|
|
52
|
+
content,
|
|
47
53
|
}: {
|
|
48
|
-
provider: Providers
|
|
54
|
+
provider: Providers;
|
|
55
|
+
messageType: string;
|
|
56
|
+
content: t.ExtendedMessageContent[];
|
|
49
57
|
}): t.ExtendedMessageContent[] => {
|
|
50
|
-
const allowedTypes =
|
|
51
|
-
|
|
52
|
-
|
|
58
|
+
const allowedTypes =
|
|
59
|
+
allowedTypesByProvider[provider] ?? allowedTypesByProvider.default;
|
|
60
|
+
return content.map((item) => {
|
|
61
|
+
if (
|
|
62
|
+
item &&
|
|
63
|
+
typeof item === 'object' &&
|
|
64
|
+
'type' in item &&
|
|
65
|
+
item.type != null &&
|
|
66
|
+
item.type
|
|
67
|
+
) {
|
|
53
68
|
let newType = item.type;
|
|
54
69
|
if (newType.endsWith('_delta')) {
|
|
55
70
|
newType = newType.replace('_delta', '');
|
|
@@ -59,7 +74,12 @@ const modifyContent = ({
|
|
|
59
74
|
}
|
|
60
75
|
|
|
61
76
|
/* Handle the edge case for empty object 'tool_use' input in AI messages */
|
|
62
|
-
if (
|
|
77
|
+
if (
|
|
78
|
+
messageType === 'ai' &&
|
|
79
|
+
newType === 'tool_use' &&
|
|
80
|
+
'input' in item &&
|
|
81
|
+
item.input === ''
|
|
82
|
+
) {
|
|
63
83
|
return { ...item, type: newType, input: '{}' };
|
|
64
84
|
}
|
|
65
85
|
|
|
@@ -69,7 +89,9 @@ const modifyContent = ({
|
|
|
69
89
|
});
|
|
70
90
|
};
|
|
71
91
|
|
|
72
|
-
type ContentBlock =
|
|
92
|
+
type ContentBlock =
|
|
93
|
+
| Partial<t.BedrockReasoningContentText>
|
|
94
|
+
| t.MessageDeltaUpdate;
|
|
73
95
|
|
|
74
96
|
function reduceBlocks(blocks: ContentBlock[]): ContentBlock[] {
|
|
75
97
|
const reduced: ContentBlock[] = [];
|
|
@@ -78,14 +100,25 @@ function reduceBlocks(blocks: ContentBlock[]): ContentBlock[] {
|
|
|
78
100
|
const lastBlock = reduced[reduced.length - 1] as ContentBlock | undefined;
|
|
79
101
|
|
|
80
102
|
// Merge consecutive 'reasoning_content'
|
|
81
|
-
if (
|
|
103
|
+
if (
|
|
104
|
+
block.type === 'reasoning_content' &&
|
|
105
|
+
lastBlock?.type === 'reasoning_content'
|
|
106
|
+
) {
|
|
82
107
|
// append text if exists
|
|
83
108
|
if (block.reasoningText?.text != null && block.reasoningText.text) {
|
|
84
|
-
(
|
|
109
|
+
(
|
|
110
|
+
lastBlock.reasoningText as t.BedrockReasoningContentText['reasoningText']
|
|
111
|
+
).text =
|
|
112
|
+
(lastBlock.reasoningText?.text ?? '') + block.reasoningText.text;
|
|
85
113
|
}
|
|
86
114
|
// preserve the signature if exists
|
|
87
|
-
if (
|
|
88
|
-
|
|
115
|
+
if (
|
|
116
|
+
block.reasoningText?.signature != null &&
|
|
117
|
+
block.reasoningText.signature
|
|
118
|
+
) {
|
|
119
|
+
(
|
|
120
|
+
lastBlock.reasoningText as t.BedrockReasoningContentText['reasoningText']
|
|
121
|
+
).signature = block.reasoningText.signature;
|
|
89
122
|
}
|
|
90
123
|
}
|
|
91
124
|
// Merge consecutive 'text'
|
|
@@ -102,19 +135,35 @@ function reduceBlocks(blocks: ContentBlock[]): ContentBlock[] {
|
|
|
102
135
|
return reduced;
|
|
103
136
|
}
|
|
104
137
|
|
|
105
|
-
export function modifyDeltaProperties(
|
|
138
|
+
export function modifyDeltaProperties(
|
|
139
|
+
provider: Providers,
|
|
140
|
+
obj?: AIMessageChunk
|
|
141
|
+
): AIMessageChunk | undefined {
|
|
106
142
|
if (!obj || typeof obj !== 'object') return obj;
|
|
107
143
|
|
|
108
|
-
const messageType = obj
|
|
144
|
+
const messageType = (obj as Partial<AIMessageChunk>)._getType
|
|
145
|
+
? obj._getType()
|
|
146
|
+
: '';
|
|
109
147
|
|
|
110
148
|
if (provider === Providers.BEDROCK && Array.isArray(obj.content)) {
|
|
111
149
|
obj.content = reduceBlocks(obj.content as ContentBlock[]);
|
|
112
150
|
}
|
|
113
151
|
if (Array.isArray(obj.content)) {
|
|
114
|
-
obj.content = modifyContent({
|
|
152
|
+
obj.content = modifyContent({
|
|
153
|
+
provider,
|
|
154
|
+
messageType,
|
|
155
|
+
content: obj.content,
|
|
156
|
+
});
|
|
115
157
|
}
|
|
116
|
-
if (
|
|
117
|
-
obj
|
|
158
|
+
if (
|
|
159
|
+
(obj as Partial<AIMessageChunk>).lc_kwargs &&
|
|
160
|
+
Array.isArray(obj.lc_kwargs.content)
|
|
161
|
+
) {
|
|
162
|
+
obj.lc_kwargs.content = modifyContent({
|
|
163
|
+
provider,
|
|
164
|
+
messageType,
|
|
165
|
+
content: obj.lc_kwargs.content,
|
|
166
|
+
});
|
|
118
167
|
}
|
|
119
168
|
return obj;
|
|
120
169
|
}
|
|
@@ -124,48 +173,65 @@ export function formatAnthropicMessage(message: AIMessageChunk): AIMessage {
|
|
|
124
173
|
return new AIMessage({ content: message.content });
|
|
125
174
|
}
|
|
126
175
|
|
|
127
|
-
const toolCallMap = new Map(message.tool_calls.map(tc => [tc.id, tc]));
|
|
176
|
+
const toolCallMap = new Map(message.tool_calls.map((tc) => [tc.id, tc]));
|
|
128
177
|
let formattedContent: string | t.ExtendedMessageContent[];
|
|
129
178
|
|
|
130
179
|
if (Array.isArray(message.content)) {
|
|
131
|
-
formattedContent = message.content.reduce<t.ExtendedMessageContent[]>(
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
try {
|
|
148
|
-
const parsedInput = JSON.parse(extendedItem.input);
|
|
149
|
-
const toolCall = message.tool_calls?.find(tc => tc.args.input === parsedInput.input);
|
|
180
|
+
formattedContent = message.content.reduce<t.ExtendedMessageContent[]>(
|
|
181
|
+
(acc, item) => {
|
|
182
|
+
if (typeof item === 'object') {
|
|
183
|
+
const extendedItem = item as t.ExtendedMessageContent;
|
|
184
|
+
if (
|
|
185
|
+
extendedItem.type === 'text' &&
|
|
186
|
+
extendedItem.text != null &&
|
|
187
|
+
extendedItem.text
|
|
188
|
+
) {
|
|
189
|
+
acc.push({ type: 'text', text: extendedItem.text });
|
|
190
|
+
} else if (
|
|
191
|
+
extendedItem.type === 'tool_use' &&
|
|
192
|
+
extendedItem.id != null &&
|
|
193
|
+
extendedItem.id
|
|
194
|
+
) {
|
|
195
|
+
const toolCall = toolCallMap.get(extendedItem.id);
|
|
150
196
|
if (toolCall) {
|
|
151
197
|
acc.push({
|
|
152
198
|
type: 'tool_use',
|
|
153
|
-
id:
|
|
199
|
+
id: extendedItem.id,
|
|
154
200
|
name: toolCall.name,
|
|
155
|
-
input: toolCall.args as unknown as string
|
|
201
|
+
input: toolCall.args as unknown as string,
|
|
156
202
|
});
|
|
157
203
|
}
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
|
|
204
|
+
} else if (
|
|
205
|
+
'input' in extendedItem &&
|
|
206
|
+
extendedItem.input != null &&
|
|
207
|
+
extendedItem.input
|
|
208
|
+
) {
|
|
209
|
+
try {
|
|
210
|
+
const parsedInput = JSON.parse(extendedItem.input);
|
|
211
|
+
const toolCall = message.tool_calls?.find(
|
|
212
|
+
(tc) => tc.args.input === parsedInput.input
|
|
213
|
+
);
|
|
214
|
+
if (toolCall) {
|
|
215
|
+
acc.push({
|
|
216
|
+
type: 'tool_use',
|
|
217
|
+
id: toolCall.id,
|
|
218
|
+
name: toolCall.name,
|
|
219
|
+
input: toolCall.args as unknown as string,
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
} catch {
|
|
223
|
+
if (extendedItem.input) {
|
|
224
|
+
acc.push({ type: 'text', text: extendedItem.input });
|
|
225
|
+
}
|
|
161
226
|
}
|
|
162
227
|
}
|
|
228
|
+
} else if (typeof item === 'string') {
|
|
229
|
+
acc.push({ type: 'text', text: item });
|
|
163
230
|
}
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
}, []);
|
|
231
|
+
return acc;
|
|
232
|
+
},
|
|
233
|
+
[]
|
|
234
|
+
);
|
|
169
235
|
} else if (typeof message.content === 'string') {
|
|
170
236
|
formattedContent = message.content;
|
|
171
237
|
} else {
|
|
@@ -179,39 +245,48 @@ export function formatAnthropicMessage(message: AIMessageChunk): AIMessage {
|
|
|
179
245
|
// type: 'tool_call',
|
|
180
246
|
// }));
|
|
181
247
|
|
|
182
|
-
const formattedToolCalls: t.AgentToolCall[] = message.tool_calls.map(
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
248
|
+
const formattedToolCalls: t.AgentToolCall[] = message.tool_calls.map(
|
|
249
|
+
(toolCall) => ({
|
|
250
|
+
id: toolCall.id ?? '',
|
|
251
|
+
type: 'function',
|
|
252
|
+
function: {
|
|
253
|
+
name: toolCall.name,
|
|
254
|
+
arguments: toolCall.args,
|
|
255
|
+
},
|
|
256
|
+
})
|
|
257
|
+
);
|
|
190
258
|
|
|
191
259
|
return new AIMessage({
|
|
192
260
|
content: formattedContent,
|
|
193
261
|
tool_calls: formattedToolCalls as ToolCall[],
|
|
194
262
|
additional_kwargs: {
|
|
195
263
|
...message.additional_kwargs,
|
|
196
|
-
}
|
|
264
|
+
},
|
|
197
265
|
});
|
|
198
266
|
}
|
|
199
267
|
|
|
200
|
-
export function convertMessagesToContent(
|
|
268
|
+
export function convertMessagesToContent(
|
|
269
|
+
messages: BaseMessage[]
|
|
270
|
+
): t.MessageContentComplex[] {
|
|
201
271
|
const processedContent: t.MessageContentComplex[] = [];
|
|
202
272
|
|
|
203
273
|
const addContentPart = (message: BaseMessage | null): void => {
|
|
204
|
-
const content =
|
|
274
|
+
const content =
|
|
275
|
+
message?.lc_kwargs.content != null
|
|
276
|
+
? message.lc_kwargs.content
|
|
277
|
+
: message?.content;
|
|
205
278
|
if (content === undefined) {
|
|
206
279
|
return;
|
|
207
280
|
}
|
|
208
281
|
if (typeof content === 'string') {
|
|
209
282
|
processedContent.push({
|
|
210
283
|
type: 'text',
|
|
211
|
-
text: content
|
|
284
|
+
text: content,
|
|
212
285
|
});
|
|
213
286
|
} else if (Array.isArray(content)) {
|
|
214
|
-
const filteredContent = content.filter(
|
|
287
|
+
const filteredContent = content.filter(
|
|
288
|
+
(item) => item != null && item.type !== 'tool_use'
|
|
289
|
+
);
|
|
215
290
|
processedContent.push(...filteredContent);
|
|
216
291
|
}
|
|
217
292
|
};
|
|
@@ -223,10 +298,13 @@ export function convertMessagesToContent(messages: BaseMessage[]): t.MessageCont
|
|
|
223
298
|
const message = messages[i] as BaseMessage | null;
|
|
224
299
|
const messageType = message?._getType();
|
|
225
300
|
|
|
226
|
-
if (
|
|
301
|
+
if (
|
|
302
|
+
messageType === 'ai' &&
|
|
303
|
+
((message as AIMessage).tool_calls?.length ?? 0) > 0
|
|
304
|
+
) {
|
|
227
305
|
const tool_calls = (message as AIMessage).tool_calls || [];
|
|
228
306
|
for (const tool_call of tool_calls) {
|
|
229
|
-
if (!tool_call.id) {
|
|
307
|
+
if (tool_call.id == null || !tool_call.id) {
|
|
230
308
|
continue;
|
|
231
309
|
}
|
|
232
310
|
|
|
@@ -236,7 +314,10 @@ export function convertMessagesToContent(messages: BaseMessage[]): t.MessageCont
|
|
|
236
314
|
addContentPart(message);
|
|
237
315
|
currentAIMessageIndex = processedContent.length - 1;
|
|
238
316
|
continue;
|
|
239
|
-
} else if (
|
|
317
|
+
} else if (
|
|
318
|
+
messageType === 'tool' &&
|
|
319
|
+
(message as ToolMessage).tool_call_id
|
|
320
|
+
) {
|
|
240
321
|
const id = (message as ToolMessage).tool_call_id;
|
|
241
322
|
const output = (message as ToolMessage).content;
|
|
242
323
|
const tool_call = toolCallMap.get(id);
|
|
@@ -265,35 +346,41 @@ export function formatAnthropicArtifactContent(messages: BaseMessage[]): void {
|
|
|
265
346
|
if (!(lastMessage instanceof ToolMessage)) return;
|
|
266
347
|
|
|
267
348
|
// Find the latest AIMessage with tool_calls that this tool message belongs to
|
|
268
|
-
const latestAIParentIndex = findLastIndex(
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
349
|
+
const latestAIParentIndex = findLastIndex(
|
|
350
|
+
messages,
|
|
351
|
+
(msg) =>
|
|
352
|
+
(msg instanceof AIMessageChunk &&
|
|
353
|
+
(msg.tool_calls?.length ?? 0) > 0 &&
|
|
354
|
+
msg.tool_calls?.some((tc) => tc.id === lastMessage.tool_call_id)) ??
|
|
355
|
+
false
|
|
272
356
|
);
|
|
273
357
|
|
|
274
358
|
if (latestAIParentIndex === -1) return;
|
|
275
359
|
|
|
276
360
|
// Check if any tool message after the AI message has array artifact content
|
|
277
361
|
const hasArtifactContent = messages.some(
|
|
278
|
-
(msg, i) =>
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
362
|
+
(msg, i) =>
|
|
363
|
+
i > latestAIParentIndex &&
|
|
364
|
+
msg instanceof ToolMessage &&
|
|
365
|
+
msg.artifact != null &&
|
|
366
|
+
msg.artifact?.content != null &&
|
|
367
|
+
Array.isArray(msg.artifact.content)
|
|
283
368
|
);
|
|
284
369
|
|
|
285
370
|
if (!hasArtifactContent) return;
|
|
286
371
|
|
|
287
372
|
const message = messages[latestAIParentIndex] as AIMessageChunk;
|
|
288
|
-
const toolCallIds = message.tool_calls?.map(tc => tc.id) ?? [];
|
|
373
|
+
const toolCallIds = message.tool_calls?.map((tc) => tc.id) ?? [];
|
|
289
374
|
|
|
290
375
|
for (let j = latestAIParentIndex + 1; j < messages.length; j++) {
|
|
291
376
|
const msg = messages[j];
|
|
292
|
-
if (
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
377
|
+
if (
|
|
378
|
+
msg instanceof ToolMessage &&
|
|
379
|
+
toolCallIds.includes(msg.tool_call_id) &&
|
|
380
|
+
msg.artifact != null &&
|
|
381
|
+
Array.isArray(msg.artifact?.content) &&
|
|
382
|
+
Array.isArray(msg.content)
|
|
383
|
+
) {
|
|
297
384
|
msg.content = msg.content.concat(msg.artifact.content);
|
|
298
385
|
}
|
|
299
386
|
}
|
|
@@ -304,21 +391,25 @@ export function formatArtifactPayload(messages: BaseMessage[]): void {
|
|
|
304
391
|
if (!(lastMessageY instanceof ToolMessage)) return;
|
|
305
392
|
|
|
306
393
|
// Find the latest AIMessage with tool_calls that this tool message belongs to
|
|
307
|
-
const latestAIParentIndex = findLastIndex(
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
394
|
+
const latestAIParentIndex = findLastIndex(
|
|
395
|
+
messages,
|
|
396
|
+
(msg) =>
|
|
397
|
+
(msg instanceof AIMessageChunk &&
|
|
398
|
+
(msg.tool_calls?.length ?? 0) > 0 &&
|
|
399
|
+
msg.tool_calls?.some((tc) => tc.id === lastMessageY.tool_call_id)) ??
|
|
400
|
+
false
|
|
311
401
|
);
|
|
312
402
|
|
|
313
403
|
if (latestAIParentIndex === -1) return;
|
|
314
404
|
|
|
315
405
|
// Check if any tool message after the AI message has array artifact content
|
|
316
406
|
const hasArtifactContent = messages.some(
|
|
317
|
-
(msg, i) =>
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
407
|
+
(msg, i) =>
|
|
408
|
+
i > latestAIParentIndex &&
|
|
409
|
+
msg instanceof ToolMessage &&
|
|
410
|
+
msg.artifact != null &&
|
|
411
|
+
msg.artifact?.content != null &&
|
|
412
|
+
Array.isArray(msg.artifact.content)
|
|
322
413
|
);
|
|
323
414
|
|
|
324
415
|
if (!hasArtifactContent) return;
|
|
@@ -326,20 +417,27 @@ export function formatArtifactPayload(messages: BaseMessage[]): void {
|
|
|
326
417
|
// Collect all relevant tool messages and their artifacts
|
|
327
418
|
const relevantMessages = messages
|
|
328
419
|
.slice(latestAIParentIndex + 1)
|
|
329
|
-
.filter(msg => msg instanceof ToolMessage) as ToolMessage[];
|
|
420
|
+
.filter((msg) => msg instanceof ToolMessage) as ToolMessage[];
|
|
330
421
|
|
|
331
422
|
// Aggregate all content and artifacts
|
|
332
423
|
const aggregatedContent: t.MessageContentComplex[] = [];
|
|
333
424
|
|
|
334
|
-
relevantMessages.forEach(msg => {
|
|
425
|
+
relevantMessages.forEach((msg) => {
|
|
335
426
|
if (!Array.isArray(msg.artifact?.content)) {
|
|
336
427
|
return;
|
|
337
428
|
}
|
|
338
|
-
|
|
339
|
-
|
|
429
|
+
let currentContent = msg.content;
|
|
430
|
+
if (!Array.isArray(currentContent)) {
|
|
431
|
+
currentContent = [
|
|
432
|
+
{
|
|
433
|
+
type: 'text',
|
|
434
|
+
text: msg.content,
|
|
435
|
+
},
|
|
436
|
+
];
|
|
340
437
|
}
|
|
341
|
-
aggregatedContent.push(...
|
|
342
|
-
msg.content =
|
|
438
|
+
aggregatedContent.push(...currentContent);
|
|
439
|
+
msg.content =
|
|
440
|
+
'Tool response is included in the next message as a Human message';
|
|
343
441
|
aggregatedContent.push(...msg.artifact.content);
|
|
344
442
|
});
|
|
345
443
|
|
|
@@ -349,11 +447,14 @@ export function formatArtifactPayload(messages: BaseMessage[]): void {
|
|
|
349
447
|
}
|
|
350
448
|
}
|
|
351
449
|
|
|
352
|
-
export function findLastIndex<T>(
|
|
450
|
+
export function findLastIndex<T>(
|
|
451
|
+
array: T[],
|
|
452
|
+
predicate: (value: T) => boolean
|
|
453
|
+
): number {
|
|
353
454
|
for (let i = array.length - 1; i >= 0; i--) {
|
|
354
455
|
if (predicate(array[i])) {
|
|
355
456
|
return i;
|
|
356
457
|
}
|
|
357
458
|
}
|
|
358
459
|
return -1;
|
|
359
|
-
}
|
|
460
|
+
}
|
package/src/run.ts
CHANGED