@librechat/agents 2.1.2 → 2.1.4
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/common/enum.cjs +1 -0
- package/dist/cjs/common/enum.cjs.map +1 -1
- package/dist/cjs/graphs/Graph.cjs +11 -0
- package/dist/cjs/graphs/Graph.cjs.map +1 -1
- package/dist/cjs/llm/anthropic/llm.cjs +46 -16
- package/dist/cjs/llm/anthropic/llm.cjs.map +1 -1
- package/dist/cjs/llm/anthropic/utils/message_inputs.cjs +25 -1
- package/dist/cjs/llm/anthropic/utils/message_inputs.cjs.map +1 -1
- package/dist/cjs/llm/anthropic/utils/message_outputs.cjs +93 -31
- package/dist/cjs/llm/anthropic/utils/message_outputs.cjs.map +1 -1
- package/dist/cjs/splitStream.cjs +8 -2
- package/dist/cjs/splitStream.cjs.map +1 -1
- package/dist/cjs/stream.cjs +13 -2
- package/dist/cjs/stream.cjs.map +1 -1
- package/dist/esm/common/enum.mjs +1 -0
- package/dist/esm/common/enum.mjs.map +1 -1
- package/dist/esm/graphs/Graph.mjs +11 -0
- package/dist/esm/graphs/Graph.mjs.map +1 -1
- package/dist/esm/llm/anthropic/llm.mjs +46 -16
- package/dist/esm/llm/anthropic/llm.mjs.map +1 -1
- package/dist/esm/llm/anthropic/utils/message_inputs.mjs +25 -1
- package/dist/esm/llm/anthropic/utils/message_inputs.mjs.map +1 -1
- package/dist/esm/llm/anthropic/utils/message_outputs.mjs +93 -31
- package/dist/esm/llm/anthropic/utils/message_outputs.mjs.map +1 -1
- package/dist/esm/splitStream.mjs +8 -2
- package/dist/esm/splitStream.mjs.map +1 -1
- package/dist/esm/stream.mjs +13 -2
- package/dist/esm/stream.mjs.map +1 -1
- package/dist/types/common/enum.d.ts +1 -0
- package/dist/types/llm/anthropic/llm.d.ts +3 -2
- package/dist/types/llm/anthropic/types.d.ts +10 -0
- package/dist/types/scripts/caching.d.ts +1 -0
- package/dist/types/scripts/thinking.d.ts +1 -0
- package/dist/types/splitStream.d.ts +2 -0
- package/dist/types/types/stream.d.ts +8 -2
- package/package.json +9 -7
- package/src/common/enum.ts +1 -0
- package/src/graphs/Graph.ts +14 -2
- package/src/llm/anthropic/llm.spec.ts +1069 -0
- package/src/llm/anthropic/llm.ts +65 -22
- package/src/llm/anthropic/types.ts +11 -2
- package/src/llm/anthropic/utils/message_inputs.ts +31 -1
- package/src/llm/anthropic/utils/message_outputs.ts +112 -42
- package/src/scripts/caching.ts +124 -0
- package/src/scripts/thinking.ts +152 -0
- package/src/scripts/tools.ts +2 -2
- package/src/splitStream.ts +8 -3
- package/src/stream.ts +11 -2
- package/src/types/stream.ts +9 -2
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
// src/scripts/test-thinking.ts
|
|
2
|
+
import { config } from 'dotenv';
|
|
3
|
+
config();
|
|
4
|
+
import { HumanMessage, SystemMessage, BaseMessage } from '@langchain/core/messages';
|
|
5
|
+
import type { UsageMetadata } from '@langchain/core/messages';
|
|
6
|
+
import * as t from '@/types';
|
|
7
|
+
import { ChatModelStreamHandler, createContentAggregator } from '@/stream';
|
|
8
|
+
import { ToolEndHandler, ModelEndHandler } from '@/events';
|
|
9
|
+
import { GraphEvents, Providers } from '@/common';
|
|
10
|
+
import { getLLMConfig } from '@/utils/llmConfig';
|
|
11
|
+
import { getArgs } from '@/scripts/args';
|
|
12
|
+
import { Run } from '@/run';
|
|
13
|
+
|
|
14
|
+
const conversationHistory: BaseMessage[] = [];
|
|
15
|
+
let _contentParts: t.MessageContentComplex[] = [];
|
|
16
|
+
const collectedUsage: UsageMetadata[] = [];
|
|
17
|
+
|
|
18
|
+
async function testThinking(): Promise<void> {
|
|
19
|
+
const { userName } = await getArgs();
|
|
20
|
+
const instructions = `You are a helpful AI assistant for ${userName}. When answering questions, be thorough in your reasoning.`;
|
|
21
|
+
const { contentParts, aggregateContent } = createContentAggregator();
|
|
22
|
+
_contentParts = contentParts as t.MessageContentComplex[];
|
|
23
|
+
|
|
24
|
+
// Set up event handlers
|
|
25
|
+
const customHandlers = {
|
|
26
|
+
[GraphEvents.TOOL_END]: new ToolEndHandler(),
|
|
27
|
+
[GraphEvents.CHAT_MODEL_END]: new ModelEndHandler(collectedUsage),
|
|
28
|
+
[GraphEvents.CHAT_MODEL_STREAM]: new ChatModelStreamHandler(),
|
|
29
|
+
[GraphEvents.ON_RUN_STEP_COMPLETED]: {
|
|
30
|
+
handle: (event: GraphEvents.ON_RUN_STEP_COMPLETED, data: t.StreamEventData): void => {
|
|
31
|
+
console.log('====== ON_RUN_STEP_COMPLETED ======');
|
|
32
|
+
aggregateContent({ event, data: data as unknown as { result: t.ToolEndEvent } });
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
[GraphEvents.ON_RUN_STEP]: {
|
|
36
|
+
handle: (event: GraphEvents.ON_RUN_STEP, data: t.RunStep) => {
|
|
37
|
+
aggregateContent({ event, data });
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
[GraphEvents.ON_RUN_STEP_DELTA]: {
|
|
41
|
+
handle: (event: GraphEvents.ON_RUN_STEP_DELTA, data: t.RunStepDeltaEvent) => {
|
|
42
|
+
aggregateContent({ event, data });
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
[GraphEvents.ON_MESSAGE_DELTA]: {
|
|
46
|
+
handle: (event: GraphEvents.ON_MESSAGE_DELTA, data: t.MessageDeltaEvent) => {
|
|
47
|
+
aggregateContent({ event, data });
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
[GraphEvents.ON_REASONING_DELTA]: {
|
|
51
|
+
handle: (event: GraphEvents.ON_REASONING_DELTA, data: t.ReasoningDeltaEvent) => {
|
|
52
|
+
aggregateContent({ event, data });
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
const baseLlmConfig: t.LLMConfig = getLLMConfig(Providers.ANTHROPIC);
|
|
58
|
+
|
|
59
|
+
if (baseLlmConfig.provider !== 'anthropic') {
|
|
60
|
+
console.error('This test requires Anthropic as the LLM provider. Please specify provider=anthropic');
|
|
61
|
+
process.exit(1);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Enable thinking with token budget
|
|
65
|
+
const llmConfig = {
|
|
66
|
+
...baseLlmConfig,
|
|
67
|
+
model: 'claude-3-7-sonnet-latest',
|
|
68
|
+
thinking: { type: "enabled", budget_tokens: 2000 }
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
const run = await Run.create<t.IState>({
|
|
72
|
+
runId: 'test-thinking-id',
|
|
73
|
+
graphConfig: {
|
|
74
|
+
instructions,
|
|
75
|
+
type: 'standard',
|
|
76
|
+
llmConfig,
|
|
77
|
+
},
|
|
78
|
+
returnContent: true,
|
|
79
|
+
customHandlers: customHandlers as t.RunConfig['customHandlers'],
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
const config = {
|
|
83
|
+
configurable: {
|
|
84
|
+
thread_id: 'thinking-test-thread',
|
|
85
|
+
},
|
|
86
|
+
streamMode: 'values',
|
|
87
|
+
version: 'v2' as const,
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
// Test 1: Regular thinking mode
|
|
91
|
+
console.log('\n\nTest 1: Regular thinking mode');
|
|
92
|
+
const userMessage1 = `What would be the environmental and economic impacts if all cars globally were replaced by electric vehicles overnight?`;
|
|
93
|
+
conversationHistory.push(new HumanMessage(userMessage1));
|
|
94
|
+
|
|
95
|
+
console.log('Running first query with thinking enabled...');
|
|
96
|
+
const firstInputs = { messages: [...conversationHistory] };
|
|
97
|
+
await run.processStream(firstInputs, config);
|
|
98
|
+
|
|
99
|
+
// Extract and display thinking blocks
|
|
100
|
+
const finalMessages = run.getRunMessages();
|
|
101
|
+
|
|
102
|
+
// Test 2: Try multi-turn conversation
|
|
103
|
+
console.log('\n\nTest 2: Multi-turn conversation with thinking enabled');
|
|
104
|
+
const userMessage2 = `Given your previous analysis, what would be the most significant technical challenges in making this transition?`;
|
|
105
|
+
conversationHistory.push(new HumanMessage(userMessage2));
|
|
106
|
+
|
|
107
|
+
console.log('Running second query with thinking enabled...');
|
|
108
|
+
const secondInputs = { messages: [...conversationHistory] };
|
|
109
|
+
await run.processStream(secondInputs, config);
|
|
110
|
+
|
|
111
|
+
// Display thinking blocks for second response
|
|
112
|
+
const finalMessages2 = run.getRunMessages();
|
|
113
|
+
|
|
114
|
+
// Test 3: Redacted thinking mode
|
|
115
|
+
console.log('\n\nTest 3: Redacted thinking mode');
|
|
116
|
+
const magicString = "ANTHROPIC_MAGIC_STRING_TRIGGER_REDACTED_THINKING_46C9A13E193C177646C7398A98432ECCCE4C1253D5E2D82641AC0E52CC2876CB";
|
|
117
|
+
const userMessage3 = `${magicString}\n\nExplain how quantum computing works in simple terms.`;
|
|
118
|
+
|
|
119
|
+
// Reset conversation for clean test
|
|
120
|
+
conversationHistory.length = 0;
|
|
121
|
+
conversationHistory.push(new HumanMessage(userMessage3));
|
|
122
|
+
|
|
123
|
+
console.log('Running query with redacted thinking...');
|
|
124
|
+
const thirdInputs = { messages: [...conversationHistory] };
|
|
125
|
+
await run.processStream(thirdInputs, config);
|
|
126
|
+
|
|
127
|
+
// Display redacted thinking blocks
|
|
128
|
+
const finalMessages3 = run.getRunMessages();
|
|
129
|
+
console.log('\n\nThinking feature test completed!');
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
process.on('unhandledRejection', (reason, promise) => {
|
|
133
|
+
console.error('Unhandled Rejection at:', promise, 'reason:', reason);
|
|
134
|
+
console.log('Conversation history:');
|
|
135
|
+
console.dir(conversationHistory, { depth: null });
|
|
136
|
+
console.log('Content parts:');
|
|
137
|
+
console.dir(_contentParts, { depth: null });
|
|
138
|
+
process.exit(1);
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
process.on('uncaughtException', (err) => {
|
|
142
|
+
console.error('Uncaught Exception:', err);
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
testThinking().catch((err) => {
|
|
146
|
+
console.error(err);
|
|
147
|
+
console.log('Conversation history:');
|
|
148
|
+
console.dir(conversationHistory, { depth: null });
|
|
149
|
+
console.log('Content parts:');
|
|
150
|
+
console.dir(_contentParts, { depth: null });
|
|
151
|
+
process.exit(1);
|
|
152
|
+
});
|
package/src/scripts/tools.ts
CHANGED
|
@@ -6,7 +6,7 @@ import { HumanMessage, BaseMessage } from '@langchain/core/messages';
|
|
|
6
6
|
import { TavilySearchResults } from '@langchain/community/tools/tavily_search';
|
|
7
7
|
import type * as t from '@/types';
|
|
8
8
|
import { ChatModelStreamHandler, createContentAggregator } from '@/stream';
|
|
9
|
-
import { ToolEndHandler } from '@/events';
|
|
9
|
+
import { ToolEndHandler, ModelEndHandler } from '@/events';
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
import { getArgs } from '@/scripts/args';
|
|
@@ -20,7 +20,7 @@ async function testStandardStreaming(): Promise<void> {
|
|
|
20
20
|
const { contentParts, aggregateContent } = createContentAggregator();
|
|
21
21
|
const customHandlers = {
|
|
22
22
|
[GraphEvents.TOOL_END]: new ToolEndHandler(),
|
|
23
|
-
|
|
23
|
+
[GraphEvents.CHAT_MODEL_END]: new ModelEndHandler(),
|
|
24
24
|
[GraphEvents.CHAT_MODEL_STREAM]: new ChatModelStreamHandler(),
|
|
25
25
|
[GraphEvents.ON_RUN_STEP_COMPLETED]: {
|
|
26
26
|
handle: (event: GraphEvents.ON_RUN_STEP_COMPLETED, data: t.StreamEventData): void => {
|
package/src/splitStream.ts
CHANGED
|
@@ -141,14 +141,19 @@ export class SplitStreamHandler {
|
|
|
141
141
|
});
|
|
142
142
|
}
|
|
143
143
|
};
|
|
144
|
+
getDeltaContent(chunk: t.CustomChunk): string {
|
|
145
|
+
return chunk.choices?.[0]?.delta.content ?? '';
|
|
146
|
+
}
|
|
147
|
+
getReasoningDelta(chunk: t.CustomChunk): string {
|
|
148
|
+
return chunk.choices?.[0]?.delta[this.reasoningKey] ?? '';
|
|
149
|
+
}
|
|
144
150
|
handle(chunk?: t.CustomChunk): void {
|
|
145
151
|
if (!chunk) {
|
|
146
152
|
return;
|
|
147
153
|
}
|
|
148
154
|
|
|
149
|
-
const content = chunk
|
|
150
|
-
const reasoning_content =
|
|
151
|
-
|
|
155
|
+
const content = this.getDeltaContent(chunk);
|
|
156
|
+
const reasoning_content = this.getReasoningDelta(chunk);
|
|
152
157
|
if (!content.length && !reasoning_content.length) {
|
|
153
158
|
return;
|
|
154
159
|
}
|
package/src/stream.ts
CHANGED
|
@@ -213,6 +213,12 @@ hasToolCallChunks: ${hasToolCallChunks}
|
|
|
213
213
|
graph.dispatchMessageDelta(stepId, {
|
|
214
214
|
content,
|
|
215
215
|
});
|
|
216
|
+
} else if (content.every((c) => c.type?.startsWith(ContentTypes.THINKING))) {
|
|
217
|
+
graph.dispatchReasoningDelta(stepId, {
|
|
218
|
+
content: content.map((c) => ({
|
|
219
|
+
type: ContentTypes.THINK,
|
|
220
|
+
think: (c as t.ThinkingContentText).thinking,
|
|
221
|
+
}))});
|
|
216
222
|
}
|
|
217
223
|
}
|
|
218
224
|
handleToolCallChunks = ({
|
|
@@ -271,8 +277,11 @@ hasToolCallChunks: ${hasToolCallChunks}
|
|
|
271
277
|
});
|
|
272
278
|
};
|
|
273
279
|
handleReasoning(chunk: Partial<AIMessageChunk>, graph: Graph): void {
|
|
274
|
-
|
|
275
|
-
if (
|
|
280
|
+
let reasoning_content = chunk.additional_kwargs?.[graph.reasoningKey] as string | undefined;
|
|
281
|
+
if (Array.isArray(chunk.content) && chunk.content[0]?.type === 'thinking') {
|
|
282
|
+
reasoning_content = 'valid';
|
|
283
|
+
}
|
|
284
|
+
if (reasoning_content != null && reasoning_content && (chunk.content == null || chunk.content === '' || reasoning_content === 'valid')) {
|
|
276
285
|
graph.currentTokenType = ContentTypes.THINK;
|
|
277
286
|
graph.tokenTypeSwitch = 'reasoning';
|
|
278
287
|
return;
|
package/src/types/stream.ts
CHANGED
|
@@ -216,9 +216,16 @@ export type ReasoningContentText = {
|
|
|
216
216
|
think: string;
|
|
217
217
|
};
|
|
218
218
|
|
|
219
|
+
/** Anthropic's Reasoning Content Block Format */
|
|
220
|
+
export type ThinkingContentText = {
|
|
221
|
+
type: ContentTypes.THINKING;
|
|
222
|
+
index?: number;
|
|
223
|
+
thinking: string;
|
|
224
|
+
};
|
|
225
|
+
|
|
219
226
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
220
|
-
export type MessageContentComplex = (ReasoningContentText | MessageContentText | MessageContentImageUrl | (Record<string, any> & {
|
|
221
|
-
type?: 'text' | 'image_url' | 'think' | string;
|
|
227
|
+
export type MessageContentComplex = (ThinkingContentText | ReasoningContentText | MessageContentText | MessageContentImageUrl | (Record<string, any> & {
|
|
228
|
+
type?: 'text' | 'image_url' | 'think' | 'thinking' | string;
|
|
222
229
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
223
230
|
}) | (Record<string, any> & {
|
|
224
231
|
type?: never;
|