@librechat/agents 3.2.68 → 3.3.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.
Files changed (159) hide show
  1. package/dist/cjs/agents/AgentContext.cjs +117 -3
  2. package/dist/cjs/agents/AgentContext.cjs.map +1 -1
  3. package/dist/cjs/common/enum.cjs +4 -0
  4. package/dist/cjs/common/enum.cjs.map +1 -1
  5. package/dist/cjs/graphs/Graph.cjs +243 -49
  6. package/dist/cjs/graphs/Graph.cjs.map +1 -1
  7. package/dist/cjs/graphs/MultiAgentGraph.cjs +103 -28
  8. package/dist/cjs/graphs/MultiAgentGraph.cjs.map +1 -1
  9. package/dist/cjs/langfuseToolOutputTracing.cjs +4 -0
  10. package/dist/cjs/langfuseToolOutputTracing.cjs.map +1 -1
  11. package/dist/cjs/llm/bedrock/index.cjs +11 -2
  12. package/dist/cjs/llm/bedrock/index.cjs.map +1 -1
  13. package/dist/cjs/llm/contextOverflowRecovery.cjs +132 -0
  14. package/dist/cjs/llm/contextOverflowRecovery.cjs.map +1 -0
  15. package/dist/cjs/llm/google/index.cjs +1 -1
  16. package/dist/cjs/llm/invoke.cjs +60 -3
  17. package/dist/cjs/llm/invoke.cjs.map +1 -1
  18. package/dist/cjs/llm/openai/index.cjs +1 -1
  19. package/dist/cjs/main.cjs +21 -7
  20. package/dist/cjs/messages/format.cjs +136 -4
  21. package/dist/cjs/messages/format.cjs.map +1 -1
  22. package/dist/cjs/messages/prune.cjs +16 -5
  23. package/dist/cjs/messages/prune.cjs.map +1 -1
  24. package/dist/cjs/messages/recency.cjs +2 -0
  25. package/dist/cjs/messages/recency.cjs.map +1 -1
  26. package/dist/cjs/prompts/activityLabel.cjs +101 -0
  27. package/dist/cjs/prompts/activityLabel.cjs.map +1 -0
  28. package/dist/cjs/run.cjs +177 -3
  29. package/dist/cjs/run.cjs.map +1 -1
  30. package/dist/cjs/stream.cjs +205 -49
  31. package/dist/cjs/stream.cjs.map +1 -1
  32. package/dist/cjs/summarization/node.cjs +55 -7
  33. package/dist/cjs/summarization/node.cjs.map +1 -1
  34. package/dist/cjs/tools/BashExecutor.cjs +2 -2
  35. package/dist/cjs/tools/BashExecutor.cjs.map +1 -1
  36. package/dist/cjs/tools/BashProgrammaticToolCalling.cjs +2 -2
  37. package/dist/cjs/tools/BashProgrammaticToolCalling.cjs.map +1 -1
  38. package/dist/cjs/tools/CodeExecutor.cjs +69 -8
  39. package/dist/cjs/tools/CodeExecutor.cjs.map +1 -1
  40. package/dist/cjs/tools/ProgrammaticToolCalling.cjs +19 -15
  41. package/dist/cjs/tools/ProgrammaticToolCalling.cjs.map +1 -1
  42. package/dist/cjs/tools/ToolNode.cjs +32 -3
  43. package/dist/cjs/tools/ToolNode.cjs.map +1 -1
  44. package/dist/cjs/tools/subagent/SubagentExecutor.cjs +1 -1
  45. package/dist/cjs/utils/errors.cjs +317 -52
  46. package/dist/cjs/utils/errors.cjs.map +1 -1
  47. package/dist/esm/agents/AgentContext.mjs +118 -4
  48. package/dist/esm/agents/AgentContext.mjs.map +1 -1
  49. package/dist/esm/common/enum.mjs +4 -0
  50. package/dist/esm/common/enum.mjs.map +1 -1
  51. package/dist/esm/graphs/Graph.mjs +242 -48
  52. package/dist/esm/graphs/Graph.mjs.map +1 -1
  53. package/dist/esm/graphs/MultiAgentGraph.mjs +103 -28
  54. package/dist/esm/graphs/MultiAgentGraph.mjs.map +1 -1
  55. package/dist/esm/langfuseToolOutputTracing.mjs +4 -1
  56. package/dist/esm/langfuseToolOutputTracing.mjs.map +1 -1
  57. package/dist/esm/llm/bedrock/index.mjs +11 -2
  58. package/dist/esm/llm/bedrock/index.mjs.map +1 -1
  59. package/dist/esm/llm/contextOverflowRecovery.mjs +130 -0
  60. package/dist/esm/llm/contextOverflowRecovery.mjs.map +1 -0
  61. package/dist/esm/llm/google/index.mjs +1 -1
  62. package/dist/esm/llm/invoke.mjs +59 -4
  63. package/dist/esm/llm/invoke.mjs.map +1 -1
  64. package/dist/esm/llm/openai/index.mjs +1 -1
  65. package/dist/esm/main.mjs +10 -10
  66. package/dist/esm/messages/format.mjs +136 -5
  67. package/dist/esm/messages/format.mjs.map +1 -1
  68. package/dist/esm/messages/prune.mjs +14 -6
  69. package/dist/esm/messages/prune.mjs.map +1 -1
  70. package/dist/esm/messages/recency.mjs +2 -1
  71. package/dist/esm/messages/recency.mjs.map +1 -1
  72. package/dist/esm/prompts/activityLabel.mjs +100 -0
  73. package/dist/esm/prompts/activityLabel.mjs.map +1 -0
  74. package/dist/esm/run.mjs +178 -4
  75. package/dist/esm/run.mjs.map +1 -1
  76. package/dist/esm/stream.mjs +205 -49
  77. package/dist/esm/stream.mjs.map +1 -1
  78. package/dist/esm/summarization/node.mjs +55 -7
  79. package/dist/esm/summarization/node.mjs.map +1 -1
  80. package/dist/esm/tools/BashExecutor.mjs +3 -3
  81. package/dist/esm/tools/BashExecutor.mjs.map +1 -1
  82. package/dist/esm/tools/BashProgrammaticToolCalling.mjs +3 -3
  83. package/dist/esm/tools/BashProgrammaticToolCalling.mjs.map +1 -1
  84. package/dist/esm/tools/CodeExecutor.mjs +62 -9
  85. package/dist/esm/tools/CodeExecutor.mjs.map +1 -1
  86. package/dist/esm/tools/ProgrammaticToolCalling.mjs +20 -16
  87. package/dist/esm/tools/ProgrammaticToolCalling.mjs.map +1 -1
  88. package/dist/esm/tools/ToolNode.mjs +32 -3
  89. package/dist/esm/tools/ToolNode.mjs.map +1 -1
  90. package/dist/esm/tools/subagent/SubagentExecutor.mjs +1 -1
  91. package/dist/esm/utils/errors.mjs +317 -53
  92. package/dist/esm/utils/errors.mjs.map +1 -1
  93. package/dist/types/agents/AgentContext.d.ts +62 -3
  94. package/dist/types/common/enum.d.ts +5 -1
  95. package/dist/types/graphs/Graph.d.ts +16 -2
  96. package/dist/types/graphs/MultiAgentGraph.d.ts +1 -0
  97. package/dist/types/langfuseToolOutputTracing.d.ts +4 -0
  98. package/dist/types/llm/contextOverflowRecovery.d.ts +85 -0
  99. package/dist/types/llm/invoke.d.ts +35 -6
  100. package/dist/types/messages/format.d.ts +22 -0
  101. package/dist/types/messages/prune.d.ts +10 -2
  102. package/dist/types/messages/recency.d.ts +1 -0
  103. package/dist/types/prompts/activityLabel.d.ts +31 -0
  104. package/dist/types/run.d.ts +16 -0
  105. package/dist/types/tools/CodeExecutor.d.ts +14 -1
  106. package/dist/types/types/activityLabel.d.ts +53 -0
  107. package/dist/types/types/index.d.ts +1 -0
  108. package/dist/types/types/llm.d.ts +7 -4
  109. package/dist/types/types/stream.d.ts +7 -4
  110. package/dist/types/types/summarize.d.ts +22 -0
  111. package/dist/types/utils/__tests__/fixtures/contextOverflowSignatures.d.ts +40 -0
  112. package/dist/types/utils/errors.d.ts +65 -16
  113. package/dist/types/utils/redactSecrets.d.ts +3 -0
  114. package/package.json +7 -8
  115. package/src/agents/AgentContext.ts +188 -7
  116. package/src/agents/__tests__/AgentContext.overflow.test.ts +205 -0
  117. package/src/common/enum.ts +4 -0
  118. package/src/graphs/Graph.ts +409 -58
  119. package/src/graphs/MultiAgentGraph.ts +184 -46
  120. package/src/graphs/__tests__/Graph.contextOverflow.test.ts +631 -0
  121. package/src/langfuseToolOutputTracing.ts +4 -1
  122. package/src/llm/__tests__/contextOverflowRecovery.test.ts +401 -0
  123. package/src/llm/__tests__/fallbackOverflow.test.ts +287 -0
  124. package/src/llm/bedrock/index.ts +25 -12
  125. package/src/llm/contextOverflowRecovery.ts +292 -0
  126. package/src/llm/invoke.ts +119 -4
  127. package/src/messages/foldToollessToolBlocks.test.ts +438 -0
  128. package/src/messages/format.ts +233 -5
  129. package/src/messages/prune.ts +24 -11
  130. package/src/messages/recency.ts +3 -1
  131. package/src/prompts/activityLabel.ts +177 -0
  132. package/src/run.ts +322 -3
  133. package/src/scripts/context-overflow-probe.ts +997 -0
  134. package/src/specs/activity-label-prompt.test.ts +128 -0
  135. package/src/specs/activity-label-trace-seed.test.ts +47 -0
  136. package/src/specs/agent-handoffs.test.ts +903 -1
  137. package/src/specs/bedrock-toolless.live.test.ts +123 -0
  138. package/src/specs/context-overflow-recovery.live.test.ts +213 -0
  139. package/src/splitStream.test.ts +882 -0
  140. package/src/stream.ts +315 -51
  141. package/src/summarization/__tests__/aggregator.test.ts +83 -0
  142. package/src/summarization/__tests__/node.test.ts +139 -0
  143. package/src/summarization/node.ts +99 -14
  144. package/src/tools/BashExecutor.ts +4 -2
  145. package/src/tools/BashProgrammaticToolCalling.ts +4 -7
  146. package/src/tools/CodeExecutor.ts +119 -8
  147. package/src/tools/ProgrammaticToolCalling.ts +29 -27
  148. package/src/tools/ToolNode.ts +50 -8
  149. package/src/tools/__tests__/CodeApiAuthHeaders.test.ts +297 -3
  150. package/src/types/activityLabel.ts +55 -0
  151. package/src/types/index.ts +1 -0
  152. package/src/types/llm.ts +8 -1
  153. package/src/types/stream.ts +7 -4
  154. package/src/types/summarize.ts +22 -0
  155. package/src/utils/__tests__/errors.test.ts +270 -0
  156. package/src/utils/__tests__/fixtures/contextOverflowSignatures.ts +336 -0
  157. package/src/utils/__tests__/redactSecrets.test.ts +56 -0
  158. package/src/utils/errors.ts +484 -66
  159. package/src/utils/redactSecrets.ts +61 -0
package/src/run.ts CHANGED
@@ -1,9 +1,9 @@
1
1
  // src/run.ts
2
- import { HumanMessage } from '@langchain/core/messages';
3
2
  import { PromptTemplate } from '@langchain/core/prompts';
4
3
  import { RunnableLambda } from '@langchain/core/runnables';
5
4
  import { AzureChatOpenAI, ChatOpenAI } from '@langchain/openai';
6
5
  import { BaseCallbackHandler } from '@langchain/core/callbacks/base';
6
+ import { HumanMessage, SystemMessage } from '@langchain/core/messages';
7
7
  import {
8
8
  Command,
9
9
  INTERRUPT,
@@ -26,10 +26,19 @@ import {
26
26
  isLangfuseCallbackHandler,
27
27
  withLangfuseAttributes,
28
28
  } from '@/langfuse';
29
+ import {
30
+ hasToolOutputTracingConfig,
31
+ resolveLangfuseConfig,
32
+ resolveToolOutputTracingConfig,
33
+ } from '@/langfuseConfig';
29
34
  import {
30
35
  resolveLangfuseRuntimeScope,
31
36
  withLangfuseRuntimeScope,
32
37
  } from '@/langfuseRuntimeScope';
38
+ import {
39
+ ACTIVITY_LABEL_PROMPT,
40
+ buildActivityLabelPrompt,
41
+ } from '@/prompts/activityLabel';
33
42
  import {
34
43
  appendCallbacks,
35
44
  findCallback,
@@ -43,7 +52,7 @@ import { createTokenCounter, encodingForModel } from '@/utils/tokens';
43
52
  import { initializeLangfuseTracing } from './instrumentation';
44
53
  import { GraphEvents, Callback, TitleMethod } from '@/common';
45
54
  import { MultiAgentGraph } from '@/graphs/MultiAgentGraph';
46
- import { resolveLangfuseConfig } from '@/langfuseConfig';
55
+ import { getTraceIdSeed } from '@/langfuseRuntimeContext';
47
56
  import { StandardGraph } from '@/graphs/Graph';
48
57
  import { initializeModel } from '@/llm/init';
49
58
  import { HandlerRegistry } from '@/events';
@@ -153,6 +162,10 @@ export class Run<_T extends t.BaseGraphState> {
153
162
  * lets callers assert the type they expect.
154
163
  */
155
164
  private _interrupt: t.RunInterruptResult<unknown> | undefined;
165
+ /** Per-run sequence for batch-unique activity-label trace-seed fallbacks. */
166
+ private activityLabelSeq = 0;
167
+ /** Distinguishes sibling forks started from the same explicit checkpoint. */
168
+ private checkpointForkSeq = 0;
156
169
  private _haltedReason: string | undefined;
157
170
 
158
171
  private constructor(config: Partial<t.RunConfig>) {
@@ -708,7 +721,28 @@ export class Run<_T extends t.BaseGraphState> {
708
721
  * boundary.
709
722
  */
710
723
  if (!isResume) {
711
- graph.resetValues(streamOptions?.keepContent);
724
+ const checkpointThreadId =
725
+ typeof config.configurable?.thread_id === 'string'
726
+ ? config.configurable.thread_id
727
+ : undefined;
728
+ const checkpointNamespace =
729
+ typeof config.configurable?.checkpoint_ns === 'string'
730
+ ? config.configurable.checkpoint_ns
731
+ : '';
732
+ const checkpointId =
733
+ typeof config.configurable?.checkpoint_id === 'string'
734
+ ? config.configurable.checkpoint_id
735
+ : '';
736
+ const checkpointScope =
737
+ checkpointThreadId == null
738
+ ? undefined
739
+ : JSON.stringify([
740
+ checkpointThreadId,
741
+ checkpointNamespace,
742
+ checkpointId,
743
+ checkpointId === '' ? 0 : ++this.checkpointForkSeq,
744
+ ]);
745
+ graph.resetValues(streamOptions?.keepContent, checkpointScope);
712
746
  }
713
747
  this._interrupt = undefined;
714
748
  this._haltedReason = undefined;
@@ -1478,6 +1512,291 @@ export class Run<_T extends t.BaseGraphState> {
1478
1512
  await disposeLangfuseHandler(titleLangfuseHandler);
1479
1513
  }
1480
1514
  }
1515
+
1516
+ /**
1517
+ * Generates a short activity label for a completed tool/reasoning block
1518
+ * using a fast model. Mirrors `generateTitle`'s Langfuse wiring so the
1519
+ * call is traced under the conversation's session (sessionId from
1520
+ * `chainOptions.configurable.thread_id`) with its own tags — never as an
1521
+ * orphan trace. The payload contains no human messages by design: intent
1522
+ * comes from `lastAssistantText`, content from reasoning excerpts and
1523
+ * tool entries.
1524
+ */
1525
+ async generateActivityLabel({
1526
+ provider,
1527
+ clientOptions,
1528
+ entries,
1529
+ thinkingExcerpts,
1530
+ lastAssistantText,
1531
+ prompt,
1532
+ charLimit = 600,
1533
+ chainOptions,
1534
+ traceSeed,
1535
+ agentId,
1536
+ }: t.RunActivityLabelOptions): Promise<{ label?: string }> {
1537
+ if (
1538
+ entries.length === 0 &&
1539
+ !(thinkingExcerpts && thinkingExcerpts.length > 0)
1540
+ ) {
1541
+ return {};
1542
+ }
1543
+ const labelSeq = ++this.activityLabelSeq;
1544
+
1545
+ /** Resolve the LABELED agent's context: its Langfuse overlay carries the
1546
+ * trace metadata and the tool-output redaction policy that must govern
1547
+ * this label. */
1548
+ const requestedContext =
1549
+ this.Graph == null || agentId == null
1550
+ ? undefined
1551
+ : this.Graph.agentContexts.get(agentId);
1552
+ /** Fail closed: an explicit but unknown/stale `agentId` must NOT silently
1553
+ * fall back to the default agent, whose redaction policy may be weaker
1554
+ * than the labeled agent's. Skip generation entirely instead. */
1555
+ if (agentId != null && requestedContext == null) {
1556
+ return {};
1557
+ }
1558
+ const labelContext =
1559
+ this.Graph == null
1560
+ ? undefined
1561
+ : (requestedContext ??
1562
+ this.Graph.agentContexts.get(this.Graph.defaultAgentId));
1563
+ const traceMetadata = createLangfuseTraceMetadata({
1564
+ messageId: 'activity-label-' + this.id,
1565
+ agentName: labelContext?.name,
1566
+ });
1567
+ const labelRunName = getLangfuseTraceName(
1568
+ traceMetadata,
1569
+ 'LibreChat Activity Label'
1570
+ );
1571
+
1572
+ /** Shallow-cloned: activity labels run once per tool batch, and writing
1573
+ * the Langfuse handler back onto a host-reused `chainOptions` would
1574
+ * accumulate duplicate callbacks across batches. */
1575
+ const labelChainOptions = {
1576
+ ...(chainOptions ?? {}),
1577
+ } as Partial<RunnableConfig> & {
1578
+ configurable?: Record<string, unknown>;
1579
+ };
1580
+ const labelUserId =
1581
+ typeof labelChainOptions.configurable?.user_id === 'string'
1582
+ ? (labelChainOptions.configurable.user_id as string)
1583
+ : undefined;
1584
+ const labelSessionId =
1585
+ typeof labelChainOptions.configurable?.thread_id === 'string'
1586
+ ? (labelChainOptions.configurable.thread_id as string)
1587
+ : undefined;
1588
+ const labelLangfuseConfig = resolveLangfuseConfig(
1589
+ this.langfuse,
1590
+ labelContext?.langfuse
1591
+ );
1592
+ initializeLangfuseTracing(labelLangfuseConfig);
1593
+ /** Seed policy, threading two constraints:
1594
+ * 1. `runWithLangfuseRuntimeContext` SPREADS the surrounding context, so
1595
+ * an absent seed INHERITS the parent run's and collapses every label
1596
+ * into that trace. When a parent seed is active we must override it
1597
+ * with a per-label one.
1598
+ * 2. Without deterministic tracing there is no parent seed, and forcing
1599
+ * one here would make label trace ids deterministic when neither
1600
+ * `processStream` nor `generateTitle` are — so leave it unset.
1601
+ * Seeded when determinism is opted into OR a parent seed is live;
1602
+ * otherwise unseeded, matching the other generation paths. */
1603
+ const inheritedTraceSeed = getTraceIdSeed();
1604
+ const labelTraceSeed =
1605
+ labelLangfuseConfig?.deterministicTraceId === true ||
1606
+ inheritedTraceSeed != null
1607
+ ? (traceSeed ?? `activity-label-${this.id}-${labelSeq}`)
1608
+ : undefined;
1609
+ const labelRuntimeScope = resolveLangfuseRuntimeScope({
1610
+ runLangfuse: this.langfuse,
1611
+ langfuseOverlay: labelContext?.langfuse,
1612
+ traceIdSeed: labelTraceSeed,
1613
+ });
1614
+ /** Handler only when a session id resolved from
1615
+ * `chainOptions.configurable.thread_id`: without it the label call has
1616
+ * no conversation identity, and tracing it would create an orphan
1617
+ * trace outside any session — worse than not tracing at all. */
1618
+ /** Declared then conditionally assigned (title precedent): a ternary
1619
+ * around the object literal makes eslint's indent rule and prettier
1620
+ * disagree, and both gate CI. */
1621
+ let labelLangfuseHandler: CallbackEntry | undefined;
1622
+ if (labelSessionId != null) {
1623
+ labelLangfuseHandler = createLangfuseHandler({
1624
+ langfuse: labelLangfuseConfig,
1625
+ userId: labelUserId,
1626
+ sessionId: labelSessionId,
1627
+ traceMetadata,
1628
+ tags: ['librechat', 'activity-label'],
1629
+ traceIdSeed:
1630
+ labelLangfuseConfig?.deterministicTraceId === true
1631
+ ? labelTraceSeed
1632
+ : undefined,
1633
+ });
1634
+ }
1635
+ if (labelLangfuseHandler != null) {
1636
+ labelChainOptions.callbacks = appendCallbacks(
1637
+ labelChainOptions.callbacks,
1638
+ [labelLangfuseHandler]
1639
+ );
1640
+ }
1641
+
1642
+ /** The label prompt becomes Langfuse generation input, so the resolved
1643
+ * tool-output redaction policy (global disable / redactedToolNames)
1644
+ * applies to it exactly as to structured tool observations. */
1645
+ let redaction = hasToolOutputTracingConfig(
1646
+ this.langfuse,
1647
+ labelContext?.langfuse
1648
+ )
1649
+ ? resolveToolOutputTracingConfig(this.langfuse, labelContext?.langfuse)
1650
+ : undefined;
1651
+ /** Multi-agent graph with no `agentId`: the caller did not say WHICH
1652
+ * agent ran this batch, so resolving from the default agent could trace
1653
+ * raw output that a stricter sibling's policy forbids. Fold every
1654
+ * agent's policy into the strictest one instead of guessing. */
1655
+ const agentContexts = this.Graph?.agentContexts;
1656
+ if (agentId == null && agentContexts != null && agentContexts.size > 1) {
1657
+ for (const context of agentContexts.values()) {
1658
+ if (!hasToolOutputTracingConfig(this.langfuse, context.langfuse)) {
1659
+ continue;
1660
+ }
1661
+ const candidate = resolveToolOutputTracingConfig(
1662
+ this.langfuse,
1663
+ context.langfuse
1664
+ );
1665
+ if (redaction == null) {
1666
+ redaction = candidate;
1667
+ continue;
1668
+ }
1669
+ redaction = {
1670
+ enabled: redaction.enabled === false ? false : candidate.enabled,
1671
+ redactedToolNames: new Set([
1672
+ ...redaction.redactedToolNames,
1673
+ ...candidate.redactedToolNames,
1674
+ ]),
1675
+ redactedToolNameMatchMode:
1676
+ redaction.redactedToolNameMatchMode === 'partial' ||
1677
+ candidate.redactedToolNameMatchMode === 'partial'
1678
+ ? 'partial'
1679
+ : 'exact',
1680
+ redactionText: redaction.redactionText,
1681
+ };
1682
+ }
1683
+ }
1684
+ /** An active redaction policy suppresses free-form reasoning/intent, so
1685
+ * a reasoning-only block has nothing describable left — skip the model
1686
+ * call rather than paying for a label built from the prompt alone. */
1687
+ const freeFormSuppressed =
1688
+ redaction != null &&
1689
+ (redaction.enabled === false || redaction.redactedToolNames.size > 0);
1690
+ if (entries.length === 0 && freeFormSuppressed) {
1691
+ return {};
1692
+ }
1693
+ const userPrompt = buildActivityLabelPrompt({
1694
+ entries,
1695
+ charLimit,
1696
+ thinkingExcerpts,
1697
+ lastAssistantText,
1698
+ redaction,
1699
+ });
1700
+
1701
+ const model = initializeModel({
1702
+ provider,
1703
+ clientOptions: {
1704
+ ...(clientOptions ?? {}),
1705
+ streaming: false,
1706
+ } as t.ClientOptions,
1707
+ }) as t.ChatModelInstance;
1708
+
1709
+ /** Distinct run id per label call: callback/tracing integrations key
1710
+ * in-flight runs by it, so reusing the parent run's id would collide
1711
+ * across successive (or concurrent) label batches. */
1712
+ const labelRunId = `${this.id}-activity-${labelSeq}`;
1713
+ const invokeConfig = Object.assign({}, labelChainOptions, {
1714
+ run_id: labelRunId,
1715
+ runId: labelRunId,
1716
+ runName: labelChainOptions.runName ?? labelRunName,
1717
+ }) as Partial<RunnableConfig>;
1718
+
1719
+ const invokeLabel = (
1720
+ runtimeConfig: Partial<RunnableConfig>
1721
+ ): Promise<unknown> =>
1722
+ withLangfuseAttributes(
1723
+ {
1724
+ langfuse: labelLangfuseConfig,
1725
+ userId: labelUserId,
1726
+ sessionId: labelSessionId,
1727
+ traceName: runtimeConfig.runName ?? labelRunName,
1728
+ traceMetadata,
1729
+ tags: ['librechat', 'activity-label'],
1730
+ },
1731
+ () =>
1732
+ model.invoke(
1733
+ [
1734
+ new SystemMessage(prompt ?? ACTIVITY_LABEL_PROMPT),
1735
+ new HumanMessage(userPrompt),
1736
+ ],
1737
+ runtimeConfig
1738
+ )
1739
+ );
1740
+
1741
+ const extractLabel = (response: unknown): string => {
1742
+ const content = (response as { content?: unknown } | null)?.content;
1743
+ let text = '';
1744
+ if (typeof content === 'string') {
1745
+ text = content;
1746
+ } else if (Array.isArray(content)) {
1747
+ text = content
1748
+ .map((block) =>
1749
+ typeof block === 'string'
1750
+ ? block
1751
+ : ((block as { text?: string }).text ?? '')
1752
+ )
1753
+ .join('');
1754
+ }
1755
+ return text.trim().replace(/^["']|["']$/g, '');
1756
+ };
1757
+
1758
+ try {
1759
+ let response: unknown;
1760
+ try {
1761
+ response = await withLangfuseRuntimeScope(labelRuntimeScope, () =>
1762
+ invokeLabel(invokeConfig)
1763
+ );
1764
+ } catch (error) {
1765
+ /** Retry ONLY recognized callback/tracer failures (the EventStream
1766
+ * tracer class of errors the stripped-callbacks fallback exists
1767
+ * for). Aborts and provider failures rethrow — retrying those
1768
+ * doubles traffic/cost and can restart cancelled requests. */
1769
+ const aborted =
1770
+ (labelChainOptions as { signal?: AbortSignal }).signal?.aborted ===
1771
+ true || (error as Error | null)?.name === 'AbortError';
1772
+ const callbackFailure = /callback|tracer|event.?stream/i.test(
1773
+ String(
1774
+ (error as Error | null)?.stack ??
1775
+ (error as Error | null)?.message ??
1776
+ ''
1777
+ )
1778
+ );
1779
+ if (aborted || !callbackFailure) {
1780
+ throw error;
1781
+ }
1782
+ const langfuseHandler = findCallback(
1783
+ invokeConfig.callbacks,
1784
+ isLangfuseCallbackHandler
1785
+ );
1786
+ const { callbacks: _cb, ...rest } = invokeConfig;
1787
+ const safeConfig = Object.assign({}, rest, {
1788
+ callbacks: langfuseHandler ? [langfuseHandler] : [],
1789
+ });
1790
+ response = await withLangfuseRuntimeScope(labelRuntimeScope, () =>
1791
+ invokeLabel(safeConfig as Partial<RunnableConfig>)
1792
+ );
1793
+ }
1794
+ const label = extractLabel(response);
1795
+ return label.length > 0 ? { label } : {};
1796
+ } finally {
1797
+ await disposeLangfuseHandler(labelLangfuseHandler);
1798
+ }
1799
+ }
1481
1800
  }
1482
1801
 
1483
1802
  function findLastMessageOfType(