@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.
Files changed (49) hide show
  1. package/dist/cjs/common/enum.cjs +1 -0
  2. package/dist/cjs/common/enum.cjs.map +1 -1
  3. package/dist/cjs/graphs/Graph.cjs +11 -0
  4. package/dist/cjs/graphs/Graph.cjs.map +1 -1
  5. package/dist/cjs/llm/anthropic/llm.cjs +46 -16
  6. package/dist/cjs/llm/anthropic/llm.cjs.map +1 -1
  7. package/dist/cjs/llm/anthropic/utils/message_inputs.cjs +25 -1
  8. package/dist/cjs/llm/anthropic/utils/message_inputs.cjs.map +1 -1
  9. package/dist/cjs/llm/anthropic/utils/message_outputs.cjs +93 -31
  10. package/dist/cjs/llm/anthropic/utils/message_outputs.cjs.map +1 -1
  11. package/dist/cjs/splitStream.cjs +8 -2
  12. package/dist/cjs/splitStream.cjs.map +1 -1
  13. package/dist/cjs/stream.cjs +13 -2
  14. package/dist/cjs/stream.cjs.map +1 -1
  15. package/dist/esm/common/enum.mjs +1 -0
  16. package/dist/esm/common/enum.mjs.map +1 -1
  17. package/dist/esm/graphs/Graph.mjs +11 -0
  18. package/dist/esm/graphs/Graph.mjs.map +1 -1
  19. package/dist/esm/llm/anthropic/llm.mjs +46 -16
  20. package/dist/esm/llm/anthropic/llm.mjs.map +1 -1
  21. package/dist/esm/llm/anthropic/utils/message_inputs.mjs +25 -1
  22. package/dist/esm/llm/anthropic/utils/message_inputs.mjs.map +1 -1
  23. package/dist/esm/llm/anthropic/utils/message_outputs.mjs +93 -31
  24. package/dist/esm/llm/anthropic/utils/message_outputs.mjs.map +1 -1
  25. package/dist/esm/splitStream.mjs +8 -2
  26. package/dist/esm/splitStream.mjs.map +1 -1
  27. package/dist/esm/stream.mjs +13 -2
  28. package/dist/esm/stream.mjs.map +1 -1
  29. package/dist/types/common/enum.d.ts +1 -0
  30. package/dist/types/llm/anthropic/llm.d.ts +3 -2
  31. package/dist/types/llm/anthropic/types.d.ts +10 -0
  32. package/dist/types/scripts/caching.d.ts +1 -0
  33. package/dist/types/scripts/thinking.d.ts +1 -0
  34. package/dist/types/splitStream.d.ts +2 -0
  35. package/dist/types/types/stream.d.ts +8 -2
  36. package/package.json +9 -7
  37. package/src/common/enum.ts +1 -0
  38. package/src/graphs/Graph.ts +14 -2
  39. package/src/llm/anthropic/llm.spec.ts +1069 -0
  40. package/src/llm/anthropic/llm.ts +65 -22
  41. package/src/llm/anthropic/types.ts +11 -2
  42. package/src/llm/anthropic/utils/message_inputs.ts +31 -1
  43. package/src/llm/anthropic/utils/message_outputs.ts +112 -42
  44. package/src/scripts/caching.ts +124 -0
  45. package/src/scripts/thinking.ts +152 -0
  46. package/src/scripts/tools.ts +2 -2
  47. package/src/splitStream.ts +8 -3
  48. package/src/stream.ts +11 -2
  49. 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
+ });
@@ -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
- // [GraphEvents.CHAT_MODEL_END]: new ModelEndHandler(),
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 => {
@@ -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.choices?.[0]?.delta.content ?? '';
150
- const reasoning_content = chunk.choices?.[0]?.delta[this.reasoningKey] ?? '';
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
- const reasoning_content = chunk.additional_kwargs?.[graph.reasoningKey] as string | undefined;
275
- if (reasoning_content != null && reasoning_content && (chunk.content == null || chunk.content === '')) {
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;
@@ -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;