@librechat/agents 3.1.78 → 3.1.79

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 (40) hide show
  1. package/dist/cjs/llm/anthropic/index.cjs +44 -55
  2. package/dist/cjs/llm/anthropic/index.cjs.map +1 -1
  3. package/dist/cjs/llm/anthropic/utils/message_inputs.cjs +33 -21
  4. package/dist/cjs/llm/anthropic/utils/message_inputs.cjs.map +1 -1
  5. package/dist/cjs/llm/anthropic/utils/message_outputs.cjs +0 -4
  6. package/dist/cjs/llm/anthropic/utils/message_outputs.cjs.map +1 -1
  7. package/dist/cjs/messages/anthropicToolCache.cjs +48 -15
  8. package/dist/cjs/messages/anthropicToolCache.cjs.map +1 -1
  9. package/dist/cjs/messages/format.cjs +97 -14
  10. package/dist/cjs/messages/format.cjs.map +1 -1
  11. package/dist/cjs/tools/local/LocalExecutionEngine.cjs +14 -16
  12. package/dist/cjs/tools/local/LocalExecutionEngine.cjs.map +1 -1
  13. package/dist/esm/llm/anthropic/index.mjs +43 -54
  14. package/dist/esm/llm/anthropic/index.mjs.map +1 -1
  15. package/dist/esm/llm/anthropic/utils/message_inputs.mjs +33 -21
  16. package/dist/esm/llm/anthropic/utils/message_inputs.mjs.map +1 -1
  17. package/dist/esm/llm/anthropic/utils/message_outputs.mjs +0 -4
  18. package/dist/esm/llm/anthropic/utils/message_outputs.mjs.map +1 -1
  19. package/dist/esm/messages/anthropicToolCache.mjs +48 -15
  20. package/dist/esm/messages/anthropicToolCache.mjs.map +1 -1
  21. package/dist/esm/messages/format.mjs +97 -14
  22. package/dist/esm/messages/format.mjs.map +1 -1
  23. package/dist/esm/tools/local/LocalExecutionEngine.mjs +14 -16
  24. package/dist/esm/tools/local/LocalExecutionEngine.mjs.map +1 -1
  25. package/dist/types/llm/anthropic/index.d.ts +1 -9
  26. package/dist/types/messages/anthropicToolCache.d.ts +5 -5
  27. package/package.json +1 -1
  28. package/src/llm/anthropic/index.ts +55 -64
  29. package/src/llm/anthropic/llm.spec.ts +585 -0
  30. package/src/llm/anthropic/utils/message_inputs.ts +36 -21
  31. package/src/llm/anthropic/utils/message_outputs.ts +0 -4
  32. package/src/llm/anthropic/utils/server-tool-inputs.test.ts +95 -13
  33. package/src/messages/__tests__/anthropicToolCache.test.ts +46 -0
  34. package/src/messages/anthropicToolCache.ts +70 -25
  35. package/src/messages/format.ts +117 -18
  36. package/src/messages/formatAgentMessages.test.ts +202 -1
  37. package/src/specs/summarization.test.ts +3 -3
  38. package/src/tools/__tests__/LocalExecutionRoots.test.ts +8 -0
  39. package/src/tools/local/LocalExecutionEngine.ts +55 -54
  40. package/src/types/diff.d.ts +15 -0
@@ -173,6 +173,51 @@ function extractReasoningContent(part) {
173
173
  }
174
174
  return '';
175
175
  }
176
+ function parseServerToolInput(args) {
177
+ if (typeof args === 'string') {
178
+ try {
179
+ const parsed = JSON.parse(args);
180
+ return parsed != null &&
181
+ typeof parsed === 'object' &&
182
+ !Array.isArray(parsed)
183
+ ? parsed
184
+ : {};
185
+ }
186
+ catch {
187
+ return {};
188
+ }
189
+ }
190
+ return args != null && typeof args === 'object' ? args : {};
191
+ }
192
+ function getTextContent(part) {
193
+ const { text } = part;
194
+ return typeof text === 'string' ? text : '';
195
+ }
196
+ function hasMeaningfulAssistantContent(part) {
197
+ if (part.type === _enum.ContentTypes.TEXT) {
198
+ return getTextContent(part).trim().length > 0;
199
+ }
200
+ if (part.type === _enum.ContentTypes.TOOL_CALL ||
201
+ part.type === _enum.ContentTypes.ERROR ||
202
+ part.type === _enum.ContentTypes.AGENT_UPDATE ||
203
+ part.type === _enum.ContentTypes.SUMMARY) {
204
+ return false;
205
+ }
206
+ if (part.type === _enum.ContentTypes.THINK ||
207
+ part.type === _enum.ContentTypes.THINKING ||
208
+ part.type === _enum.ContentTypes.REASONING ||
209
+ part.type === _enum.ContentTypes.REASONING_CONTENT ||
210
+ part.type === 'redacted_thinking') {
211
+ return extractReasoningContent(part).trim().length > 0;
212
+ }
213
+ return part.type != null && part.type !== '';
214
+ }
215
+ function getToolUseId(part) {
216
+ if (!('tool_use_id' in part) || typeof part.tool_use_id !== 'string') {
217
+ return undefined;
218
+ }
219
+ return part.tool_use_id;
220
+ }
176
221
  /**
177
222
  * Helper function to format an assistant message
178
223
  * @param message The message to format
@@ -185,6 +230,8 @@ function formatAssistantMessage(message, options) {
185
230
  let lastAIMessage = null;
186
231
  let hasReasoning = false;
187
232
  let pendingReasoningContent = '';
233
+ const emittedServerToolUseIds = new Set();
234
+ const pendingServerToolUses = new Map();
188
235
  const shouldPreserveReasoningContent = options?.preserveReasoningContent === true;
189
236
  const takePendingReasoningContent = () => {
190
237
  if (!shouldPreserveReasoningContent || !pendingReasoningContent) {
@@ -213,11 +260,29 @@ function formatAssistantMessage(message, options) {
213
260
  ? `${aiMessage.additional_kwargs.reasoning_content}${reasoningContent}`
214
261
  : reasoningContent;
215
262
  };
263
+ const flushPendingServerToolUse = (toolUseId) => {
264
+ for (const [id, content] of pendingServerToolUses) {
265
+ pendingServerToolUses.delete(id);
266
+ if (id === toolUseId) {
267
+ currentContent.push(content);
268
+ emittedServerToolUseIds.add(id);
269
+ return;
270
+ }
271
+ }
272
+ };
216
273
  if (Array.isArray(message.content)) {
217
- for (const part of message.content) {
274
+ const contentParts = message.content;
275
+ for (const part of contentParts) {
218
276
  if (part == null) {
219
277
  continue;
220
278
  }
279
+ const toolUseId = getToolUseId(part);
280
+ if (toolUseId != null) {
281
+ flushPendingServerToolUse(toolUseId);
282
+ }
283
+ else if (hasMeaningfulAssistantContent(part)) {
284
+ pendingServerToolUses.clear();
285
+ }
221
286
  if (part.type === _enum.ContentTypes.TEXT && part.tool_call_ids) {
222
287
  /*
223
288
  If there's pending content, it needs to be aggregated as a single string to prepare for tool calls.
@@ -226,19 +291,18 @@ function formatAssistantMessage(message, options) {
226
291
  if (currentContent.length > 0) {
227
292
  let content = currentContent.reduce((acc, curr) => {
228
293
  if (curr.type === _enum.ContentTypes.TEXT) {
229
- return `${acc}${String(curr[_enum.ContentTypes.TEXT] ?? '')}\n`;
294
+ return `${acc}${getTextContent(curr)}\n`;
230
295
  }
231
296
  return acc;
232
297
  }, '');
233
- content =
234
- `${content}\n${part[_enum.ContentTypes.TEXT] ?? part.text ?? ''}`.trim();
298
+ content = `${content}\n${getTextContent(part)}`.trim();
235
299
  lastAIMessage = createAIMessage(content);
236
300
  formattedMessages.push(lastAIMessage);
237
301
  currentContent = [];
238
302
  continue;
239
303
  }
240
304
  // Create a new AIMessage with this text and prepare for tool calls
241
- lastAIMessage = createAIMessage(part.text != null ? part.text : '');
305
+ lastAIMessage = createAIMessage(getTextContent(part));
242
306
  formattedMessages.push(lastAIMessage);
243
307
  }
244
308
  else if (part.type === _enum.ContentTypes.TOOL_CALL) {
@@ -253,6 +317,21 @@ function formatAssistantMessage(message, options) {
253
317
  (_tool_call.name === '' && (output == null || output === ''))) {
254
318
  continue;
255
319
  }
320
+ if (options?.provider === _enum.Providers.ANTHROPIC &&
321
+ typeof _tool_call.id === 'string' &&
322
+ _tool_call.id.startsWith(_enum.Constants.ANTHROPIC_SERVER_TOOL_PREFIX)) {
323
+ if (emittedServerToolUseIds.has(_tool_call.id) ||
324
+ pendingServerToolUses.has(_tool_call.id)) {
325
+ continue;
326
+ }
327
+ pendingServerToolUses.set(_tool_call.id, {
328
+ type: 'server_tool_use',
329
+ id: _tool_call.id,
330
+ name: _tool_call.name,
331
+ input: parseServerToolInput(_args),
332
+ });
333
+ continue;
334
+ }
256
335
  if (!lastAIMessage) {
257
336
  // "Heal" the payload by creating an AIMessage to precede the tool call
258
337
  lastAIMessage = createAIMessage('');
@@ -300,23 +379,26 @@ function formatAssistantMessage(message, options) {
300
379
  continue;
301
380
  }
302
381
  else {
303
- if (part.type === _enum.ContentTypes.TEXT &&
304
- !String(part.text ?? '').trim()) {
382
+ if (part.type === _enum.ContentTypes.TEXT && !getTextContent(part).trim()) {
305
383
  continue;
306
384
  }
307
385
  currentContent.push(part);
308
386
  }
309
387
  }
388
+ for (const content of pendingServerToolUses.values()) {
389
+ currentContent.push(content);
390
+ }
310
391
  }
311
392
  if (hasReasoning && currentContent.length > 0) {
312
- const content = currentContent
313
- .reduce((acc, curr) => {
314
- if (curr.type === _enum.ContentTypes.TEXT) {
315
- return `${acc}${String(curr[_enum.ContentTypes.TEXT] ?? '')}\n`;
393
+ let content = '';
394
+ for (const part of currentContent) {
395
+ if (part.type !== _enum.ContentTypes.TEXT) {
396
+ formattedMessages.push(createAIMessage(langchain.toLangChainContent(currentContent)));
397
+ return formattedMessages;
316
398
  }
317
- return acc;
318
- }, '')
319
- .trim();
399
+ content += `${getTextContent(part)}\n`;
400
+ }
401
+ content = content.trim();
320
402
  if (content) {
321
403
  formattedMessages.push(createAIMessage(content));
322
404
  }
@@ -826,6 +908,7 @@ skills, options) => {
826
908
  }
827
909
  const formattedMessages = formatAssistantMessage(processedMessage, {
828
910
  preserveReasoningContent: options?.provider === _enum.Providers.DEEPSEEK,
911
+ provider: options?.provider,
829
912
  });
830
913
  if (sourceMessageId != null && sourceMessageId !== '') {
831
914
  for (const formattedMessage of formattedMessages) {