@librechat/agents 3.2.0 → 3.2.1
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 +154 -67
- package/dist/cjs/graphs/Graph.cjs.map +1 -1
- package/dist/cjs/messages/core.cjs +93 -7
- package/dist/cjs/messages/core.cjs.map +1 -1
- package/dist/cjs/stream.cjs +10 -8
- package/dist/cjs/stream.cjs.map +1 -1
- package/dist/esm/graphs/Graph.mjs +155 -68
- package/dist/esm/graphs/Graph.mjs.map +1 -1
- package/dist/esm/messages/core.mjs +94 -8
- package/dist/esm/messages/core.mjs.map +1 -1
- package/dist/esm/stream.mjs +10 -8
- package/dist/esm/stream.mjs.map +1 -1
- package/package.json +1 -1
- package/src/graphs/Graph.ts +246 -104
- package/src/graphs/__tests__/Graph.reasoning.test.ts +747 -0
- package/src/messages/core.ts +126 -11
- package/src/messages/formatAgentMessages.test.ts +122 -0
- package/src/specs/deepseek.simple.test.ts +8 -3
- package/src/specs/moonshot.simple.test.ts +8 -3
- package/src/splitStream.test.ts +64 -0
- package/src/stream.ts +12 -10
package/src/graphs/Graph.ts
CHANGED
|
@@ -89,6 +89,10 @@ const { AGENT, TOOLS, SUMMARIZE } = GraphNodeKeys;
|
|
|
89
89
|
/** Minimum relative variance before calibrated toolSchemaTokens overrides current value. */
|
|
90
90
|
const CALIBRATION_VARIANCE_THRESHOLD = 0.15;
|
|
91
91
|
|
|
92
|
+
type ReasoningKey = 'reasoning_content' | 'reasoning';
|
|
93
|
+
type ReasoningSummary = { summary?: Array<{ text?: string }> };
|
|
94
|
+
type ReasoningDetail = { type?: string; text?: string };
|
|
95
|
+
|
|
92
96
|
function getHandlerDispatchedEventKey(
|
|
93
97
|
eventName: string,
|
|
94
98
|
stepId: string
|
|
@@ -96,6 +100,197 @@ function getHandlerDispatchedEventKey(
|
|
|
96
100
|
return `${eventName}:${stepId}`;
|
|
97
101
|
}
|
|
98
102
|
|
|
103
|
+
function getReasoningText(
|
|
104
|
+
value: string | Partial<ReasoningSummary> | null | undefined
|
|
105
|
+
): string | undefined {
|
|
106
|
+
if (typeof value === 'string') {
|
|
107
|
+
return value !== '' ? value : undefined;
|
|
108
|
+
}
|
|
109
|
+
const summaryText = value?.summary
|
|
110
|
+
?.map((summary) => summary.text ?? '')
|
|
111
|
+
.filter((text) => text !== '')
|
|
112
|
+
.join('');
|
|
113
|
+
return summaryText != null && summaryText !== '' ? summaryText : undefined;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function getReasoningDetailsText(
|
|
117
|
+
value: ReasoningDetail[] | null | undefined
|
|
118
|
+
): string | undefined {
|
|
119
|
+
if (!Array.isArray(value)) {
|
|
120
|
+
return undefined;
|
|
121
|
+
}
|
|
122
|
+
const reasoningText = value
|
|
123
|
+
.filter((detail) => detail.type === 'reasoning.text')
|
|
124
|
+
.map((detail) => detail.text ?? '')
|
|
125
|
+
.filter((text) => text !== '')
|
|
126
|
+
.join('');
|
|
127
|
+
return reasoningText !== '' ? reasoningText : undefined;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function getResponseReasoningContent({
|
|
131
|
+
responseMessage,
|
|
132
|
+
reasoningKey,
|
|
133
|
+
}: {
|
|
134
|
+
responseMessage?: Partial<AIMessageChunk>;
|
|
135
|
+
reasoningKey: ReasoningKey;
|
|
136
|
+
}): string | undefined {
|
|
137
|
+
const additionalKwargs = responseMessage?.additional_kwargs;
|
|
138
|
+
if (additionalKwargs == null) {
|
|
139
|
+
return undefined;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const keyedReasoning = getReasoningText(
|
|
143
|
+
additionalKwargs[reasoningKey] as
|
|
144
|
+
| string
|
|
145
|
+
| Partial<ReasoningSummary>
|
|
146
|
+
| null
|
|
147
|
+
| undefined
|
|
148
|
+
);
|
|
149
|
+
if (keyedReasoning != null) {
|
|
150
|
+
return keyedReasoning;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const reasoningContent = getReasoningText(
|
|
154
|
+
additionalKwargs.reasoning_content as
|
|
155
|
+
| string
|
|
156
|
+
| Partial<ReasoningSummary>
|
|
157
|
+
| null
|
|
158
|
+
| undefined
|
|
159
|
+
);
|
|
160
|
+
if (reasoningContent != null) {
|
|
161
|
+
return reasoningContent;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const reasoning = getReasoningText(
|
|
165
|
+
additionalKwargs.reasoning as
|
|
166
|
+
| string
|
|
167
|
+
| Partial<ReasoningSummary>
|
|
168
|
+
| null
|
|
169
|
+
| undefined
|
|
170
|
+
);
|
|
171
|
+
if (reasoning != null) {
|
|
172
|
+
return reasoning;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
return getReasoningDetailsText(
|
|
176
|
+
additionalKwargs.reasoning_details as ReasoningDetail[] | null | undefined
|
|
177
|
+
);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function getTextMessageDeltaContent(
|
|
181
|
+
content: MessageContent | undefined
|
|
182
|
+
): t.MessageDelta['content'] | undefined {
|
|
183
|
+
if (content == null) {
|
|
184
|
+
return undefined;
|
|
185
|
+
}
|
|
186
|
+
if (typeof content === 'string') {
|
|
187
|
+
return content !== ''
|
|
188
|
+
? [{ type: ContentTypes.TEXT, text: content }]
|
|
189
|
+
: undefined;
|
|
190
|
+
}
|
|
191
|
+
if (content.length === 0) {
|
|
192
|
+
return undefined;
|
|
193
|
+
}
|
|
194
|
+
if (
|
|
195
|
+
!content.every(
|
|
196
|
+
(contentPart) =>
|
|
197
|
+
typeof contentPart === 'object' &&
|
|
198
|
+
'type' in contentPart &&
|
|
199
|
+
typeof contentPart.type === 'string' &&
|
|
200
|
+
contentPart.type.startsWith('text')
|
|
201
|
+
)
|
|
202
|
+
) {
|
|
203
|
+
return undefined;
|
|
204
|
+
}
|
|
205
|
+
return content as t.MessageDelta['content'];
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
async function dispatchTextMessageContent({
|
|
209
|
+
graph,
|
|
210
|
+
stepKey,
|
|
211
|
+
content,
|
|
212
|
+
metadata,
|
|
213
|
+
}: {
|
|
214
|
+
graph: Graph<t.BaseGraphState>;
|
|
215
|
+
stepKey: string;
|
|
216
|
+
content: t.MessageDelta['content'];
|
|
217
|
+
metadata: Record<string, unknown>;
|
|
218
|
+
}): Promise<boolean> {
|
|
219
|
+
const messageId = getMessageId(stepKey, graph) ?? '';
|
|
220
|
+
if (!messageId) {
|
|
221
|
+
return false;
|
|
222
|
+
}
|
|
223
|
+
await graph.dispatchRunStep(
|
|
224
|
+
stepKey,
|
|
225
|
+
{
|
|
226
|
+
type: StepTypes.MESSAGE_CREATION,
|
|
227
|
+
message_creation: { message_id: messageId },
|
|
228
|
+
},
|
|
229
|
+
metadata
|
|
230
|
+
);
|
|
231
|
+
const stepId = graph.getStepIdByKey(stepKey);
|
|
232
|
+
await graph.dispatchMessageDelta(stepId, { content }, metadata);
|
|
233
|
+
return true;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
async function dispatchReasoningContent({
|
|
237
|
+
graph,
|
|
238
|
+
agentContext,
|
|
239
|
+
reasoningContent,
|
|
240
|
+
metadata,
|
|
241
|
+
}: {
|
|
242
|
+
graph: Graph<t.BaseGraphState>;
|
|
243
|
+
agentContext: AgentContext;
|
|
244
|
+
reasoningContent: string;
|
|
245
|
+
metadata: Record<string, unknown>;
|
|
246
|
+
}): Promise<boolean> {
|
|
247
|
+
const previousTokenType = agentContext.currentTokenType;
|
|
248
|
+
const previousTokenTypeSwitch = agentContext.tokenTypeSwitch;
|
|
249
|
+
const previousTransitionCount = agentContext.reasoningTransitionCount;
|
|
250
|
+
|
|
251
|
+
agentContext.currentTokenType = ContentTypes.THINK;
|
|
252
|
+
agentContext.tokenTypeSwitch = 'reasoning';
|
|
253
|
+
|
|
254
|
+
const stepKey = graph.getStepKey(metadata);
|
|
255
|
+
const messageId = getMessageId(stepKey, graph) ?? '';
|
|
256
|
+
if (!messageId) {
|
|
257
|
+
agentContext.currentTokenType = previousTokenType;
|
|
258
|
+
agentContext.tokenTypeSwitch = previousTokenTypeSwitch;
|
|
259
|
+
agentContext.reasoningTransitionCount = previousTransitionCount;
|
|
260
|
+
return false;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
await graph.dispatchRunStep(
|
|
264
|
+
stepKey,
|
|
265
|
+
{
|
|
266
|
+
type: StepTypes.MESSAGE_CREATION,
|
|
267
|
+
message_creation: { message_id: messageId },
|
|
268
|
+
},
|
|
269
|
+
metadata
|
|
270
|
+
);
|
|
271
|
+
const stepId = graph.getStepIdByKey(stepKey);
|
|
272
|
+
await graph.dispatchReasoningDelta(
|
|
273
|
+
stepId,
|
|
274
|
+
{
|
|
275
|
+
content: [{ type: ContentTypes.THINK, think: reasoningContent }],
|
|
276
|
+
},
|
|
277
|
+
metadata
|
|
278
|
+
);
|
|
279
|
+
return true;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
function markPostReasoningContent(agentContext: AgentContext): void {
|
|
283
|
+
if (
|
|
284
|
+
agentContext.tokenTypeSwitch !== 'reasoning' ||
|
|
285
|
+
agentContext.currentTokenType === ContentTypes.TEXT
|
|
286
|
+
) {
|
|
287
|
+
return;
|
|
288
|
+
}
|
|
289
|
+
agentContext.currentTokenType = ContentTypes.TEXT;
|
|
290
|
+
agentContext.tokenTypeSwitch = 'content';
|
|
291
|
+
agentContext.reasoningTransitionCount++;
|
|
292
|
+
}
|
|
293
|
+
|
|
99
294
|
export abstract class Graph<
|
|
100
295
|
T extends t.BaseGraphState = t.BaseGraphState,
|
|
101
296
|
_TNodeName extends string = string,
|
|
@@ -1472,60 +1667,37 @@ export class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode> {
|
|
|
1472
1667
|
const toolCalls = (responseMessage as AIMessageChunk | undefined)
|
|
1473
1668
|
?.tool_calls;
|
|
1474
1669
|
const hasToolCalls = Array.isArray(toolCalls) && toolCalls.length > 0;
|
|
1670
|
+
const metadata = config.metadata as Record<string, unknown>;
|
|
1671
|
+
const responseReasoningContent = getResponseReasoningContent({
|
|
1672
|
+
responseMessage: responseMessage as Partial<AIMessageChunk> | undefined,
|
|
1673
|
+
reasoningKey: agentContext.reasoningKey,
|
|
1674
|
+
});
|
|
1675
|
+
const textMessageContent = getTextMessageDeltaContent(
|
|
1676
|
+
responseMessage?.content as MessageContent | undefined
|
|
1677
|
+
);
|
|
1475
1678
|
|
|
1476
1679
|
if (hasToolCalls) {
|
|
1477
|
-
const
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
message_creation: { message_id: messageId },
|
|
1499
|
-
},
|
|
1500
|
-
metadata
|
|
1501
|
-
);
|
|
1502
|
-
const stepId = this.getStepIdByKey(stepKey);
|
|
1503
|
-
if (typeof content === 'string') {
|
|
1504
|
-
await this.dispatchMessageDelta(
|
|
1505
|
-
stepId,
|
|
1506
|
-
{
|
|
1507
|
-
content: [{ type: ContentTypes.TEXT, text: content }],
|
|
1508
|
-
},
|
|
1509
|
-
metadata
|
|
1510
|
-
);
|
|
1511
|
-
} else if (
|
|
1512
|
-
Array.isArray(content) &&
|
|
1513
|
-
content.every(
|
|
1514
|
-
(c) =>
|
|
1515
|
-
typeof c === 'object' &&
|
|
1516
|
-
'type' in c &&
|
|
1517
|
-
typeof c.type === 'string' &&
|
|
1518
|
-
c.type.startsWith('text')
|
|
1519
|
-
)
|
|
1520
|
-
) {
|
|
1521
|
-
await this.dispatchMessageDelta(
|
|
1522
|
-
stepId,
|
|
1523
|
-
{
|
|
1524
|
-
content: content as t.MessageDelta['content'],
|
|
1525
|
-
},
|
|
1526
|
-
metadata
|
|
1527
|
-
);
|
|
1528
|
-
}
|
|
1680
|
+
const dispatchedReasoning =
|
|
1681
|
+
responseReasoningContent != null &&
|
|
1682
|
+
(await dispatchReasoningContent({
|
|
1683
|
+
graph: this,
|
|
1684
|
+
agentContext,
|
|
1685
|
+
reasoningContent: responseReasoningContent,
|
|
1686
|
+
metadata,
|
|
1687
|
+
}));
|
|
1688
|
+
if (dispatchedReasoning) {
|
|
1689
|
+
markPostReasoningContent(agentContext);
|
|
1690
|
+
}
|
|
1691
|
+
if (textMessageContent != null) {
|
|
1692
|
+
const stepKey = this.getStepKey(metadata);
|
|
1693
|
+
const dispatchedText = await dispatchTextMessageContent({
|
|
1694
|
+
graph: this,
|
|
1695
|
+
stepKey,
|
|
1696
|
+
content: textMessageContent,
|
|
1697
|
+
metadata,
|
|
1698
|
+
});
|
|
1699
|
+
if (dispatchedText) {
|
|
1700
|
+
markPostReasoningContent(agentContext);
|
|
1529
1701
|
}
|
|
1530
1702
|
}
|
|
1531
1703
|
|
|
@@ -1533,60 +1705,30 @@ export class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode> {
|
|
|
1533
1705
|
}
|
|
1534
1706
|
|
|
1535
1707
|
/**
|
|
1536
|
-
* When streaming
|
|
1537
|
-
*
|
|
1538
|
-
*
|
|
1708
|
+
* When streaming events are unavailable, ChatModelStreamHandler never
|
|
1709
|
+
* fires. Dispatch final reasoning/text content here. getMessageId makes
|
|
1710
|
+
* this a no-op when the streaming path already handled the same step.
|
|
1539
1711
|
*/
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
await
|
|
1712
|
+
if (!hasToolCalls && responseMessage != null) {
|
|
1713
|
+
const dispatchedReasoning =
|
|
1714
|
+
responseReasoningContent != null &&
|
|
1715
|
+
(await dispatchReasoningContent({
|
|
1716
|
+
graph: this,
|
|
1717
|
+
agentContext,
|
|
1718
|
+
reasoningContent: responseReasoningContent,
|
|
1719
|
+
metadata,
|
|
1720
|
+
}));
|
|
1721
|
+
if (dispatchedReasoning && textMessageContent != null) {
|
|
1722
|
+
markPostReasoningContent(agentContext);
|
|
1723
|
+
}
|
|
1724
|
+
if (textMessageContent != null) {
|
|
1725
|
+
const stepKey = this.getStepKey(metadata);
|
|
1726
|
+
await dispatchTextMessageContent({
|
|
1727
|
+
graph: this,
|
|
1555
1728
|
stepKey,
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
},
|
|
1560
|
-
metadata
|
|
1561
|
-
);
|
|
1562
|
-
const stepId = this.getStepIdByKey(stepKey);
|
|
1563
|
-
const content = responseMessage.content;
|
|
1564
|
-
if (typeof content === 'string') {
|
|
1565
|
-
await this.dispatchMessageDelta(
|
|
1566
|
-
stepId,
|
|
1567
|
-
{
|
|
1568
|
-
content: [{ type: ContentTypes.TEXT, text: content }],
|
|
1569
|
-
},
|
|
1570
|
-
metadata
|
|
1571
|
-
);
|
|
1572
|
-
} else if (
|
|
1573
|
-
Array.isArray(content) &&
|
|
1574
|
-
content.every(
|
|
1575
|
-
(c) =>
|
|
1576
|
-
typeof c === 'object' &&
|
|
1577
|
-
'type' in c &&
|
|
1578
|
-
typeof c.type === 'string' &&
|
|
1579
|
-
c.type.startsWith('text')
|
|
1580
|
-
)
|
|
1581
|
-
) {
|
|
1582
|
-
await this.dispatchMessageDelta(
|
|
1583
|
-
stepId,
|
|
1584
|
-
{
|
|
1585
|
-
content: content as t.MessageDelta['content'],
|
|
1586
|
-
},
|
|
1587
|
-
metadata
|
|
1588
|
-
);
|
|
1589
|
-
}
|
|
1729
|
+
content: textMessageContent,
|
|
1730
|
+
metadata,
|
|
1731
|
+
});
|
|
1590
1732
|
}
|
|
1591
1733
|
}
|
|
1592
1734
|
|