@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.
@@ -66,6 +66,112 @@ const CALIBRATION_VARIANCE_THRESHOLD = 0.15;
66
66
  function getHandlerDispatchedEventKey(eventName, stepId) {
67
67
  return `${eventName}:${stepId}`;
68
68
  }
69
+ function getReasoningText(value) {
70
+ if (typeof value === 'string') {
71
+ return value !== '' ? value : undefined;
72
+ }
73
+ const summaryText = value?.summary
74
+ ?.map((summary) => summary.text ?? '')
75
+ .filter((text) => text !== '')
76
+ .join('');
77
+ return summaryText != null && summaryText !== '' ? summaryText : undefined;
78
+ }
79
+ function getReasoningDetailsText(value) {
80
+ if (!Array.isArray(value)) {
81
+ return undefined;
82
+ }
83
+ const reasoningText = value
84
+ .filter((detail) => detail.type === 'reasoning.text')
85
+ .map((detail) => detail.text ?? '')
86
+ .filter((text) => text !== '')
87
+ .join('');
88
+ return reasoningText !== '' ? reasoningText : undefined;
89
+ }
90
+ function getResponseReasoningContent({ responseMessage, reasoningKey, }) {
91
+ const additionalKwargs = responseMessage?.additional_kwargs;
92
+ if (additionalKwargs == null) {
93
+ return undefined;
94
+ }
95
+ const keyedReasoning = getReasoningText(additionalKwargs[reasoningKey]);
96
+ if (keyedReasoning != null) {
97
+ return keyedReasoning;
98
+ }
99
+ const reasoningContent = getReasoningText(additionalKwargs.reasoning_content);
100
+ if (reasoningContent != null) {
101
+ return reasoningContent;
102
+ }
103
+ const reasoning = getReasoningText(additionalKwargs.reasoning);
104
+ if (reasoning != null) {
105
+ return reasoning;
106
+ }
107
+ return getReasoningDetailsText(additionalKwargs.reasoning_details);
108
+ }
109
+ function getTextMessageDeltaContent(content) {
110
+ if (content == null) {
111
+ return undefined;
112
+ }
113
+ if (typeof content === 'string') {
114
+ return content !== ''
115
+ ? [{ type: _enum.ContentTypes.TEXT, text: content }]
116
+ : undefined;
117
+ }
118
+ if (content.length === 0) {
119
+ return undefined;
120
+ }
121
+ if (!content.every((contentPart) => typeof contentPart === 'object' &&
122
+ 'type' in contentPart &&
123
+ typeof contentPart.type === 'string' &&
124
+ contentPart.type.startsWith('text'))) {
125
+ return undefined;
126
+ }
127
+ return content;
128
+ }
129
+ async function dispatchTextMessageContent({ graph, stepKey, content, metadata, }) {
130
+ const messageId = ids.getMessageId(stepKey, graph) ?? '';
131
+ if (!messageId) {
132
+ return false;
133
+ }
134
+ await graph.dispatchRunStep(stepKey, {
135
+ type: _enum.StepTypes.MESSAGE_CREATION,
136
+ message_creation: { message_id: messageId },
137
+ }, metadata);
138
+ const stepId = graph.getStepIdByKey(stepKey);
139
+ await graph.dispatchMessageDelta(stepId, { content }, metadata);
140
+ return true;
141
+ }
142
+ async function dispatchReasoningContent({ graph, agentContext, reasoningContent, metadata, }) {
143
+ const previousTokenType = agentContext.currentTokenType;
144
+ const previousTokenTypeSwitch = agentContext.tokenTypeSwitch;
145
+ const previousTransitionCount = agentContext.reasoningTransitionCount;
146
+ agentContext.currentTokenType = _enum.ContentTypes.THINK;
147
+ agentContext.tokenTypeSwitch = 'reasoning';
148
+ const stepKey = graph.getStepKey(metadata);
149
+ const messageId = ids.getMessageId(stepKey, graph) ?? '';
150
+ if (!messageId) {
151
+ agentContext.currentTokenType = previousTokenType;
152
+ agentContext.tokenTypeSwitch = previousTokenTypeSwitch;
153
+ agentContext.reasoningTransitionCount = previousTransitionCount;
154
+ return false;
155
+ }
156
+ await graph.dispatchRunStep(stepKey, {
157
+ type: _enum.StepTypes.MESSAGE_CREATION,
158
+ message_creation: { message_id: messageId },
159
+ }, metadata);
160
+ const stepId = graph.getStepIdByKey(stepKey);
161
+ await graph.dispatchReasoningDelta(stepId, {
162
+ content: [{ type: _enum.ContentTypes.THINK, think: reasoningContent }],
163
+ }, metadata);
164
+ return true;
165
+ }
166
+ function markPostReasoningContent(agentContext) {
167
+ if (agentContext.tokenTypeSwitch !== 'reasoning' ||
168
+ agentContext.currentTokenType === _enum.ContentTypes.TEXT) {
169
+ return;
170
+ }
171
+ agentContext.currentTokenType = _enum.ContentTypes.TEXT;
172
+ agentContext.tokenTypeSwitch = 'content';
173
+ agentContext.reasoningTransitionCount++;
174
+ }
69
175
  class Graph {
70
176
  messageStepHasToolCalls = new Map();
71
177
  messageIdsByStepKey = new Map();
@@ -1074,80 +1180,61 @@ class StandardGraph extends Graph {
1074
1180
  const toolCalls = responseMessage
1075
1181
  ?.tool_calls;
1076
1182
  const hasToolCalls = Array.isArray(toolCalls) && toolCalls.length > 0;
1183
+ const metadata = config.metadata;
1184
+ const responseReasoningContent = getResponseReasoningContent({
1185
+ responseMessage: responseMessage,
1186
+ reasoningKey: agentContext.reasoningKey,
1187
+ });
1188
+ const textMessageContent = getTextMessageDeltaContent(responseMessage?.content);
1077
1189
  if (hasToolCalls) {
1078
- const metadata = config.metadata;
1079
- const stepKey = this.getStepKey(metadata);
1080
- const content = responseMessage?.content;
1081
- const hasTextContent = content != null &&
1082
- (typeof content === 'string'
1083
- ? content !== ''
1084
- : Array.isArray(content) && content.length > 0);
1085
- /**
1086
- * Dispatch text content BEFORE creating TOOL_CALLS steps.
1087
- * getMessageId returns a new ID only on the first call for a step key;
1088
- * if the for-await consumer already claimed it, this is a no-op.
1089
- */
1090
- if (hasTextContent) {
1091
- const messageId = ids.getMessageId(stepKey, this) ?? '';
1092
- if (messageId) {
1093
- await this.dispatchRunStep(stepKey, {
1094
- type: _enum.StepTypes.MESSAGE_CREATION,
1095
- message_creation: { message_id: messageId },
1096
- }, metadata);
1097
- const stepId = this.getStepIdByKey(stepKey);
1098
- if (typeof content === 'string') {
1099
- await this.dispatchMessageDelta(stepId, {
1100
- content: [{ type: _enum.ContentTypes.TEXT, text: content }],
1101
- }, metadata);
1102
- }
1103
- else if (Array.isArray(content) &&
1104
- content.every((c) => typeof c === 'object' &&
1105
- 'type' in c &&
1106
- typeof c.type === 'string' &&
1107
- c.type.startsWith('text'))) {
1108
- await this.dispatchMessageDelta(stepId, {
1109
- content: content,
1110
- }, metadata);
1111
- }
1190
+ const dispatchedReasoning = responseReasoningContent != null &&
1191
+ (await dispatchReasoningContent({
1192
+ graph: this,
1193
+ agentContext,
1194
+ reasoningContent: responseReasoningContent,
1195
+ metadata,
1196
+ }));
1197
+ if (dispatchedReasoning) {
1198
+ markPostReasoningContent(agentContext);
1199
+ }
1200
+ if (textMessageContent != null) {
1201
+ const stepKey = this.getStepKey(metadata);
1202
+ const dispatchedText = await dispatchTextMessageContent({
1203
+ graph: this,
1204
+ stepKey,
1205
+ content: textMessageContent,
1206
+ metadata,
1207
+ });
1208
+ if (dispatchedText) {
1209
+ markPostReasoningContent(agentContext);
1112
1210
  }
1113
1211
  }
1114
1212
  await handlers.handleToolCalls(toolCalls, metadata, this);
1115
1213
  }
1116
1214
  /**
1117
- * When streaming is disabled, on_chat_model_stream events are never
1118
- * emitted so ChatModelStreamHandler never fires. Dispatch the text
1119
- * content as MESSAGE_CREATION + MESSAGE_DELTA here.
1215
+ * When streaming events are unavailable, ChatModelStreamHandler never
1216
+ * fires. Dispatch final reasoning/text content here. getMessageId makes
1217
+ * this a no-op when the streaming path already handled the same step.
1120
1218
  */
1121
- const disableStreaming = agentContext.clientOptions
1122
- ?.disableStreaming === true;
1123
- if (disableStreaming &&
1124
- !hasToolCalls &&
1125
- responseMessage != null &&
1126
- responseMessage.content != null) {
1127
- const metadata = config.metadata;
1128
- const stepKey = this.getStepKey(metadata);
1129
- const messageId = ids.getMessageId(stepKey, this) ?? '';
1130
- if (messageId) {
1131
- await this.dispatchRunStep(stepKey, {
1132
- type: _enum.StepTypes.MESSAGE_CREATION,
1133
- message_creation: { message_id: messageId },
1134
- }, metadata);
1135
- const stepId = this.getStepIdByKey(stepKey);
1136
- const content = responseMessage.content;
1137
- if (typeof content === 'string') {
1138
- await this.dispatchMessageDelta(stepId, {
1139
- content: [{ type: _enum.ContentTypes.TEXT, text: content }],
1140
- }, metadata);
1141
- }
1142
- else if (Array.isArray(content) &&
1143
- content.every((c) => typeof c === 'object' &&
1144
- 'type' in c &&
1145
- typeof c.type === 'string' &&
1146
- c.type.startsWith('text'))) {
1147
- await this.dispatchMessageDelta(stepId, {
1148
- content: content,
1149
- }, metadata);
1150
- }
1219
+ if (!hasToolCalls && responseMessage != null) {
1220
+ const dispatchedReasoning = responseReasoningContent != null &&
1221
+ (await dispatchReasoningContent({
1222
+ graph: this,
1223
+ agentContext,
1224
+ reasoningContent: responseReasoningContent,
1225
+ metadata,
1226
+ }));
1227
+ if (dispatchedReasoning && textMessageContent != null) {
1228
+ markPostReasoningContent(agentContext);
1229
+ }
1230
+ if (textMessageContent != null) {
1231
+ const stepKey = this.getStepKey(metadata);
1232
+ await dispatchTextMessageContent({
1233
+ graph: this,
1234
+ stepKey,
1235
+ content: textMessageContent,
1236
+ metadata,
1237
+ });
1151
1238
  }
1152
1239
  }
1153
1240
  const invokeElapsed = ((Date.now() - invokeStart) / 1000).toFixed(2);