@librechat/agents 3.0.27 → 3.0.28
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/utils/message_inputs.cjs +35 -2
- package/dist/cjs/llm/anthropic/utils/message_inputs.cjs.map +1 -1
- package/dist/cjs/stream.cjs +4 -1
- package/dist/cjs/stream.cjs.map +1 -1
- package/dist/cjs/tools/ToolNode.cjs +10 -1
- package/dist/cjs/tools/ToolNode.cjs.map +1 -1
- package/dist/esm/llm/anthropic/utils/message_inputs.mjs +35 -2
- package/dist/esm/llm/anthropic/utils/message_inputs.mjs.map +1 -1
- package/dist/esm/stream.mjs +4 -1
- package/dist/esm/stream.mjs.map +1 -1
- package/dist/esm/tools/ToolNode.mjs +10 -1
- package/dist/esm/tools/ToolNode.mjs.map +1 -1
- package/package.json +2 -1
- package/src/llm/anthropic/utils/message_inputs.ts +55 -9
- package/src/scripts/ant_web_search_edge_case.ts +162 -0
- package/src/stream.ts +4 -1
- package/src/tools/ToolNode.ts +12 -1
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
/* eslint-disable no-console */
|
|
2
|
+
// src/scripts/cli.ts
|
|
3
|
+
import { config } from 'dotenv';
|
|
4
|
+
config();
|
|
5
|
+
import { HumanMessage, BaseMessage } from '@langchain/core/messages';
|
|
6
|
+
import type * as t from '@/types';
|
|
7
|
+
import { ChatModelStreamHandler, createContentAggregator } from '@/stream';
|
|
8
|
+
import { ToolEndHandler, ModelEndHandler } from '@/events';
|
|
9
|
+
import { Calculator } from '@/tools/Calculator';
|
|
10
|
+
|
|
11
|
+
import { getArgs } from '@/scripts/args';
|
|
12
|
+
import { Run } from '@/run';
|
|
13
|
+
import { GraphEvents, Callback, Providers } from '@/common';
|
|
14
|
+
import { getLLMConfig } from '@/utils/llmConfig';
|
|
15
|
+
|
|
16
|
+
const conversationHistory: BaseMessage[] = [];
|
|
17
|
+
let _contentParts: (t.MessageContentComplex | undefined)[] = [];
|
|
18
|
+
async function testStandardStreaming(): Promise<void> {
|
|
19
|
+
const { userName, location, currentDate } = await getArgs();
|
|
20
|
+
const { contentParts, aggregateContent } = createContentAggregator();
|
|
21
|
+
_contentParts = contentParts;
|
|
22
|
+
const customHandlers = {
|
|
23
|
+
[GraphEvents.TOOL_END]: new ToolEndHandler(),
|
|
24
|
+
[GraphEvents.CHAT_MODEL_END]: new ModelEndHandler(),
|
|
25
|
+
[GraphEvents.CHAT_MODEL_STREAM]: new ChatModelStreamHandler(),
|
|
26
|
+
[GraphEvents.ON_RUN_STEP_COMPLETED]: {
|
|
27
|
+
handle: (
|
|
28
|
+
event: GraphEvents.ON_RUN_STEP_COMPLETED,
|
|
29
|
+
data: t.StreamEventData
|
|
30
|
+
): void => {
|
|
31
|
+
console.log('====== ON_RUN_STEP_COMPLETED ======');
|
|
32
|
+
// console.dir(data, { depth: null });
|
|
33
|
+
aggregateContent({
|
|
34
|
+
event,
|
|
35
|
+
data: data as unknown as { result: t.ToolEndEvent },
|
|
36
|
+
});
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
[GraphEvents.ON_RUN_STEP]: {
|
|
40
|
+
handle: (
|
|
41
|
+
event: GraphEvents.ON_RUN_STEP,
|
|
42
|
+
data: t.StreamEventData
|
|
43
|
+
): void => {
|
|
44
|
+
console.log('====== ON_RUN_STEP ======');
|
|
45
|
+
console.dir(data, { depth: null });
|
|
46
|
+
aggregateContent({ event, data: data as t.RunStep });
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
[GraphEvents.ON_RUN_STEP_DELTA]: {
|
|
50
|
+
handle: (
|
|
51
|
+
event: GraphEvents.ON_RUN_STEP_DELTA,
|
|
52
|
+
data: t.StreamEventData
|
|
53
|
+
): void => {
|
|
54
|
+
console.log('====== ON_RUN_STEP_DELTA ======');
|
|
55
|
+
console.dir(data, { depth: null });
|
|
56
|
+
aggregateContent({ event, data: data as t.RunStepDeltaEvent });
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
[GraphEvents.ON_MESSAGE_DELTA]: {
|
|
60
|
+
handle: (
|
|
61
|
+
event: GraphEvents.ON_MESSAGE_DELTA,
|
|
62
|
+
data: t.StreamEventData
|
|
63
|
+
): void => {
|
|
64
|
+
// console.log('====== ON_MESSAGE_DELTA ======');
|
|
65
|
+
// console.dir(data, { depth: null });
|
|
66
|
+
aggregateContent({ event, data: data as t.MessageDeltaEvent });
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
[GraphEvents.TOOL_START]: {
|
|
70
|
+
handle: (
|
|
71
|
+
_event: string,
|
|
72
|
+
data: t.StreamEventData,
|
|
73
|
+
metadata?: Record<string, unknown>
|
|
74
|
+
): void => {
|
|
75
|
+
console.log('====== TOOL_START ======');
|
|
76
|
+
// console.dir(data, { depth: null });
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
const llmConfig = getLLMConfig(
|
|
82
|
+
Providers.ANTHROPIC
|
|
83
|
+
) as t.AnthropicClientOptions & t.SharedLLMConfig;
|
|
84
|
+
llmConfig.model = 'claude-haiku-4-5';
|
|
85
|
+
|
|
86
|
+
const run = await Run.create<t.IState>({
|
|
87
|
+
runId: 'test-run-id',
|
|
88
|
+
graphConfig: {
|
|
89
|
+
type: 'standard',
|
|
90
|
+
llmConfig,
|
|
91
|
+
tools: [
|
|
92
|
+
{
|
|
93
|
+
type: 'web_search_20250305',
|
|
94
|
+
name: 'web_search',
|
|
95
|
+
max_uses: 5,
|
|
96
|
+
},
|
|
97
|
+
new Calculator(),
|
|
98
|
+
],
|
|
99
|
+
instructions: 'You are a friendly AI assistant.',
|
|
100
|
+
// additional_instructions: `Always address the user by their name. The user's name is ${userName} and they are located in ${location}.`,
|
|
101
|
+
},
|
|
102
|
+
returnContent: true,
|
|
103
|
+
customHandlers,
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
const config = {
|
|
107
|
+
configurable: {
|
|
108
|
+
provider: Providers.ANTHROPIC,
|
|
109
|
+
thread_id: 'conversation-num-1',
|
|
110
|
+
},
|
|
111
|
+
streamMode: 'values',
|
|
112
|
+
version: 'v2' as const,
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
console.log('Test 1: Web search + calculator (simultaneous tool test)');
|
|
116
|
+
|
|
117
|
+
// const userMessage = `
|
|
118
|
+
// Make a search for the weather in ${location} today, which is ${currentDate}.
|
|
119
|
+
// Before making the search, please let me know what you're about to do, then immediately start searching without hesitation.
|
|
120
|
+
// Make sure to always refer to me by name, which is ${userName}.
|
|
121
|
+
// After giving me a thorough summary, tell me a joke about the weather forecast we went over.
|
|
122
|
+
// `;
|
|
123
|
+
// const userMessage = 'Are massage guns good?';
|
|
124
|
+
// const userMessage = 'What is functional programming?';
|
|
125
|
+
// const userMessage = "Get me today's trending news.";
|
|
126
|
+
// const userMessage = "search recent italy earthquake volcano activity";
|
|
127
|
+
// const userMessage =
|
|
128
|
+
// "use 'Trump' as the exact search query and tell me what you find.";
|
|
129
|
+
const userMessage =
|
|
130
|
+
'Can you search the web for the current population of Tokyo, and also calculate what 15% of that population would be? Do both at the same time.';
|
|
131
|
+
|
|
132
|
+
conversationHistory.push(new HumanMessage(userMessage));
|
|
133
|
+
|
|
134
|
+
const inputs = {
|
|
135
|
+
messages: conversationHistory,
|
|
136
|
+
};
|
|
137
|
+
const finalContentParts = await run.processStream(inputs, config);
|
|
138
|
+
const finalMessages = run.getRunMessages();
|
|
139
|
+
if (finalMessages) {
|
|
140
|
+
conversationHistory.push(...finalMessages);
|
|
141
|
+
console.dir(conversationHistory, { depth: null });
|
|
142
|
+
}
|
|
143
|
+
// console.dir(finalContentParts, { depth: null });
|
|
144
|
+
console.log('\n\n====================\n\n');
|
|
145
|
+
// console.dir(contentParts, { depth: null });
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
process.on('unhandledRejection', (reason, promise) => {
|
|
149
|
+
console.error('Unhandled Rejection at:', promise, 'reason:', reason);
|
|
150
|
+
console.log('Content Parts:');
|
|
151
|
+
console.dir(_contentParts, { depth: null });
|
|
152
|
+
process.exit(1);
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
testStandardStreaming().catch((err) => {
|
|
156
|
+
console.error(err);
|
|
157
|
+
console.log('Conversation history:');
|
|
158
|
+
console.dir(conversationHistory, { depth: null });
|
|
159
|
+
console.log('Content Parts:');
|
|
160
|
+
console.dir(_contentParts, { depth: null });
|
|
161
|
+
process.exit(1);
|
|
162
|
+
});
|
package/src/stream.ts
CHANGED
|
@@ -155,7 +155,10 @@ export class ChatModelStreamHandler implements t.EventHandler {
|
|
|
155
155
|
chunk.tool_calls.length > 0 &&
|
|
156
156
|
chunk.tool_calls.every(
|
|
157
157
|
(tc) =>
|
|
158
|
-
tc.id != null &&
|
|
158
|
+
tc.id != null &&
|
|
159
|
+
tc.id !== '' &&
|
|
160
|
+
(tc as Partial<ToolCall>).name != null &&
|
|
161
|
+
tc.name !== ''
|
|
159
162
|
)
|
|
160
163
|
) {
|
|
161
164
|
hasToolCalls = true;
|
package/src/tools/ToolNode.ts
CHANGED
|
@@ -201,7 +201,18 @@ export class ToolNode<T = any> extends RunnableCallable<T, T> {
|
|
|
201
201
|
|
|
202
202
|
outputs = await Promise.all(
|
|
203
203
|
aiMessage.tool_calls
|
|
204
|
-
?.filter((call) =>
|
|
204
|
+
?.filter((call) => {
|
|
205
|
+
/**
|
|
206
|
+
* Filter out:
|
|
207
|
+
* 1. Already processed tool calls (present in toolMessageIds)
|
|
208
|
+
* 2. Server tool calls (e.g., web_search with IDs starting with 'srvtoolu_')
|
|
209
|
+
* which are executed by the provider's API and don't require invocation
|
|
210
|
+
*/
|
|
211
|
+
return (
|
|
212
|
+
(call.id == null || !toolMessageIds.has(call.id)) &&
|
|
213
|
+
!(call.id?.startsWith('srvtoolu_') ?? false)
|
|
214
|
+
);
|
|
215
|
+
})
|
|
205
216
|
.map((call) => this.runTool(call, config)) ?? []
|
|
206
217
|
);
|
|
207
218
|
}
|