@librechat/agents 3.2.61 → 3.2.62

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 (116) hide show
  1. package/dist/cjs/common/enum.cjs +2 -0
  2. package/dist/cjs/common/enum.cjs.map +1 -1
  3. package/dist/cjs/hooks/HookRegistry.cjs +32 -0
  4. package/dist/cjs/hooks/HookRegistry.cjs.map +1 -1
  5. package/dist/cjs/hooks/executeHooks.cjs +6 -0
  6. package/dist/cjs/hooks/executeHooks.cjs.map +1 -1
  7. package/dist/cjs/hooks/index.cjs +12 -0
  8. package/dist/cjs/hooks/index.cjs.map +1 -0
  9. package/dist/cjs/hooks/types.cjs.map +1 -1
  10. package/dist/cjs/langfuse.cjs +62 -1
  11. package/dist/cjs/langfuse.cjs.map +1 -1
  12. package/dist/cjs/llm/bedrock/index.cjs +266 -43
  13. package/dist/cjs/llm/bedrock/index.cjs.map +1 -1
  14. package/dist/cjs/llm/openai/index.cjs +274 -7
  15. package/dist/cjs/llm/openai/index.cjs.map +1 -1
  16. package/dist/cjs/llm/openai/streamMetadata.cjs +69 -0
  17. package/dist/cjs/llm/openai/streamMetadata.cjs.map +1 -0
  18. package/dist/cjs/llm/openrouter/index.cjs +5 -6
  19. package/dist/cjs/llm/openrouter/index.cjs.map +1 -1
  20. package/dist/cjs/main.cjs +6 -1
  21. package/dist/cjs/messages/format.cjs +61 -0
  22. package/dist/cjs/messages/format.cjs.map +1 -1
  23. package/dist/cjs/messages/prune.cjs +31 -19
  24. package/dist/cjs/messages/prune.cjs.map +1 -1
  25. package/dist/cjs/stream.cjs +13 -3
  26. package/dist/cjs/stream.cjs.map +1 -1
  27. package/dist/cjs/tools/ToolNode.cjs +75 -10
  28. package/dist/cjs/tools/ToolNode.cjs.map +1 -1
  29. package/dist/cjs/tools/subagent/SubagentExecutor.cjs +1 -0
  30. package/dist/cjs/tools/subagent/SubagentExecutor.cjs.map +1 -1
  31. package/dist/cjs/utils/tokens.cjs +111 -0
  32. package/dist/cjs/utils/tokens.cjs.map +1 -1
  33. package/dist/esm/common/enum.mjs +2 -0
  34. package/dist/esm/common/enum.mjs.map +1 -1
  35. package/dist/esm/hooks/HookRegistry.mjs +32 -0
  36. package/dist/esm/hooks/HookRegistry.mjs.map +1 -1
  37. package/dist/esm/hooks/executeHooks.mjs +6 -0
  38. package/dist/esm/hooks/executeHooks.mjs.map +1 -1
  39. package/dist/esm/hooks/index.mjs +12 -1
  40. package/dist/esm/hooks/index.mjs.map +1 -0
  41. package/dist/esm/hooks/types.mjs.map +1 -1
  42. package/dist/esm/langfuse.mjs +62 -1
  43. package/dist/esm/langfuse.mjs.map +1 -1
  44. package/dist/esm/llm/bedrock/index.mjs +266 -43
  45. package/dist/esm/llm/bedrock/index.mjs.map +1 -1
  46. package/dist/esm/llm/openai/index.mjs +274 -7
  47. package/dist/esm/llm/openai/index.mjs.map +1 -1
  48. package/dist/esm/llm/openai/streamMetadata.mjs +69 -0
  49. package/dist/esm/llm/openai/streamMetadata.mjs.map +1 -0
  50. package/dist/esm/llm/openrouter/index.mjs +5 -6
  51. package/dist/esm/llm/openrouter/index.mjs.map +1 -1
  52. package/dist/esm/main.mjs +3 -3
  53. package/dist/esm/messages/format.mjs +61 -0
  54. package/dist/esm/messages/format.mjs.map +1 -1
  55. package/dist/esm/messages/prune.mjs +31 -19
  56. package/dist/esm/messages/prune.mjs.map +1 -1
  57. package/dist/esm/stream.mjs +13 -3
  58. package/dist/esm/stream.mjs.map +1 -1
  59. package/dist/esm/tools/ToolNode.mjs +75 -10
  60. package/dist/esm/tools/ToolNode.mjs.map +1 -1
  61. package/dist/esm/tools/subagent/SubagentExecutor.mjs +1 -0
  62. package/dist/esm/tools/subagent/SubagentExecutor.mjs.map +1 -1
  63. package/dist/esm/utils/tokens.mjs +108 -1
  64. package/dist/esm/utils/tokens.mjs.map +1 -1
  65. package/dist/types/common/enum.d.ts +3 -1
  66. package/dist/types/hooks/HookRegistry.d.ts +10 -0
  67. package/dist/types/hooks/index.d.ts +7 -0
  68. package/dist/types/hooks/types.d.ts +18 -1
  69. package/dist/types/llm/bedrock/index.d.ts +5 -0
  70. package/dist/types/llm/openai/index.d.ts +19 -0
  71. package/dist/types/llm/openai/streamMetadata.d.ts +16 -0
  72. package/dist/types/messages/prune.d.ts +4 -3
  73. package/dist/types/tools/ToolNode.d.ts +1 -0
  74. package/dist/types/types/llm.d.ts +12 -2
  75. package/dist/types/types/tools.d.ts +1 -1
  76. package/dist/types/utils/tokens.d.ts +30 -0
  77. package/package.json +6 -6
  78. package/src/__tests__/stream.eagerEventExecution.test.ts +100 -2
  79. package/src/common/enum.ts +2 -0
  80. package/src/hooks/HookRegistry.ts +45 -0
  81. package/src/hooks/__tests__/HookRegistry.test.ts +48 -0
  82. package/src/hooks/__tests__/executeHooks.test.ts +85 -2
  83. package/src/hooks/executeHooks.ts +15 -0
  84. package/src/hooks/index.ts +7 -0
  85. package/src/hooks/types.ts +18 -1
  86. package/src/langfuse.ts +134 -1
  87. package/src/llm/bedrock/index.ts +434 -83
  88. package/src/llm/bedrock/streamSealDispatch.test.ts +97 -0
  89. package/src/llm/custom-chat-models.smoke.test.ts +7 -0
  90. package/src/llm/openai/index.ts +604 -6
  91. package/src/llm/openai/managedRequests.test.ts +182 -0
  92. package/src/llm/openai/streamMetadata.spec.ts +86 -0
  93. package/src/llm/openai/streamMetadata.ts +95 -0
  94. package/src/llm/openai/streamMetadataDedup.spec.ts +166 -0
  95. package/src/llm/openrouter/index.ts +9 -5
  96. package/src/messages/format.ts +96 -3
  97. package/src/messages/formatAgentMessages.steer.test.ts +326 -0
  98. package/src/messages/labelContentByAgent.test.ts +75 -0
  99. package/src/messages/prune.ts +56 -30
  100. package/src/specs/anthropic.simple.test.ts +4 -2
  101. package/src/specs/cache.simple.test.ts +17 -4
  102. package/src/specs/langfuse-callbacks.test.ts +61 -0
  103. package/src/specs/openai.simple.test.ts +4 -2
  104. package/src/specs/spec.utils.ts +12 -0
  105. package/src/specs/summarization.test.ts +9 -13
  106. package/src/specs/token-accounting-pipeline.test.ts +130 -4
  107. package/src/specs/tokens.test.ts +214 -0
  108. package/src/stream.ts +22 -3
  109. package/src/tools/ToolNode.ts +107 -12
  110. package/src/tools/__tests__/ToolNode.eagerEventExecution.test.ts +554 -0
  111. package/src/tools/__tests__/ToolNode.onResultCompletion.test.ts +49 -2
  112. package/src/tools/__tests__/hitl.test.ts +112 -0
  113. package/src/tools/subagent/SubagentExecutor.ts +2 -0
  114. package/src/types/llm.ts +12 -2
  115. package/src/types/tools.ts +1 -1
  116. package/src/utils/tokens.ts +181 -3
@@ -1,5 +1,6 @@
1
1
  import { OPENAI_CHAT_SEQUENTIAL_STREAMED_TOOL_CALL_ADAPTER, STREAMED_TOOL_CALL_ADAPTER_METADATA_KEY } from "../../tools/streamedToolCallSeals.mjs";
2
2
  import { _convertMessagesToOpenAIParams, isReasoningModel } from "./utils/index.mjs";
3
+ import { dropRepeatedScalarMetadata } from "./streamMetadata.mjs";
3
4
  import { AIMessage, AIMessageChunk, isAIMessage } from "@langchain/core/messages";
4
5
  import { AzureChatOpenAI, AzureChatOpenAICompletions, AzureChatOpenAIResponses, ChatOpenAI, ChatOpenAICompletions, ChatOpenAIResponses, OpenAIClient, getEndpoint, getHeadersWithUserAgent } from "@langchain/openai";
5
6
  import { AzureOpenAI } from "openai";
@@ -35,6 +36,166 @@ function normalizeHeaders(headers) {
35
36
  });
36
37
  return Object.fromEntries(output.entries());
37
38
  }
39
+ const CACHE_WRITE_METADATA_KEY = "__librechat_cache_write_tokens";
40
+ function applyManagedRequestParams(params, fields) {
41
+ return {
42
+ ...params,
43
+ ...fields.promptCacheExplicit === true && { prompt_cache_options: {
44
+ mode: "explicit",
45
+ ttl: "30m"
46
+ } },
47
+ ...fields.safetyIdentifier != null && { safety_identifier: fields.safetyIdentifier }
48
+ };
49
+ }
50
+ function isCacheableChatPart(part) {
51
+ if (typeof part !== "object" || part == null || !("type" in part)) return false;
52
+ return part.type === "text" || part.type === "image_url" || part.type === "input_audio" || part.type === "file" || part.type === "refusal";
53
+ }
54
+ function canAddChatBreakpoint(message) {
55
+ if (message.role !== "system" && message.role !== "developer" && message.role !== "user" && message.role !== "assistant" && message.role !== "tool") return false;
56
+ if (typeof message.content === "string") return message.content.length > 0;
57
+ return Array.isArray(message.content) && message.content.some((part) => isCacheableChatPart(part));
58
+ }
59
+ function addChatBreakpoint(message) {
60
+ if (!canAddChatBreakpoint(message)) return message;
61
+ if (typeof message.content === "string") return {
62
+ ...message,
63
+ content: [{
64
+ type: "text",
65
+ text: message.content,
66
+ prompt_cache_breakpoint: { mode: "explicit" }
67
+ }]
68
+ };
69
+ if (!Array.isArray(message.content) || message.content.length === 0) return message;
70
+ const content = [...message.content];
71
+ let index = content.length - 1;
72
+ while (index >= 0 && !isCacheableChatPart(content[index])) index--;
73
+ if (index < 0) return message;
74
+ content[index] = {
75
+ ...content[index],
76
+ prompt_cache_breakpoint: { mode: "explicit" }
77
+ };
78
+ return {
79
+ ...message,
80
+ content
81
+ };
82
+ }
83
+ function selectCacheBreakpointIndexes(roles, cacheable) {
84
+ let instructionIndex = -1;
85
+ let latestUserIndex = -1;
86
+ for (let index = 0; index < roles.length; index++) {
87
+ const role = roles[index];
88
+ if ((role === "system" || role === "developer") && cacheable[index]) instructionIndex = index;
89
+ if (role === "user") latestUserIndex = index;
90
+ }
91
+ const indexes = /* @__PURE__ */ new Set();
92
+ if (instructionIndex >= 0) indexes.add(instructionIndex);
93
+ for (let index = latestUserIndex - 1; index >= 0; index--) if (cacheable[index]) {
94
+ indexes.add(index);
95
+ break;
96
+ }
97
+ return [...indexes];
98
+ }
99
+ /** @internal */
100
+ function addChatCacheBreakpoints(messages) {
101
+ const indexes = new Set(selectCacheBreakpointIndexes(messages.map((message) => message.role), messages.map((message) => canAddChatBreakpoint(message))));
102
+ return messages.map((message, index) => indexes.has(index) ? addChatBreakpoint(message) : message);
103
+ }
104
+ function isResponseMessage(item) {
105
+ return item.type === "message";
106
+ }
107
+ /** Only `input_text`/`input_image`/`input_file` accept a Responses breakpoint;
108
+ * `output_text`/`refusal` (replayed assistant blocks) are rejected with a 400. */
109
+ function isCacheableResponsePart(part) {
110
+ if (typeof part !== "object" || part == null || !("type" in part)) return false;
111
+ return part.type === "input_text" || part.type === "input_image" || part.type === "input_file";
112
+ }
113
+ function addResponseBreakpoint(item) {
114
+ if (!isResponseMessage(item)) return item;
115
+ const rawContent = item.content;
116
+ if (typeof rawContent === "string") {
117
+ if (rawContent.length === 0) return item;
118
+ return {
119
+ ...item,
120
+ content: [{
121
+ type: "input_text",
122
+ text: rawContent,
123
+ prompt_cache_breakpoint: { mode: "explicit" }
124
+ }]
125
+ };
126
+ }
127
+ if (!Array.isArray(rawContent) || rawContent.length === 0) return item;
128
+ const content = [...rawContent];
129
+ let index = content.length - 1;
130
+ while (index >= 0 && !isCacheableResponsePart(content[index])) index--;
131
+ if (index < 0) return item;
132
+ content[index] = {
133
+ ...content[index],
134
+ prompt_cache_breakpoint: { mode: "explicit" }
135
+ };
136
+ return {
137
+ ...item,
138
+ content
139
+ };
140
+ }
141
+ /** @internal */
142
+ function addResponseCacheBreakpoints(input) {
143
+ if (!Array.isArray(input)) return input;
144
+ const indexes = new Set(selectCacheBreakpointIndexes(input.map((item) => isResponseMessage(item) ? item.role : void 0), input.map((item) => {
145
+ if (!isResponseMessage(item)) return false;
146
+ /** Only input roles take a Responses breakpoint. Assistant/tool turns
147
+ * carry output content (string or output_text) that the API rejects
148
+ * under an input marker, so they're never eligible. */
149
+ if (item.role !== "system" && item.role !== "developer" && item.role !== "user") return false;
150
+ const content = item.content;
151
+ return typeof content === "string" ? content.length > 0 : Array.isArray(content) && content.some((part) => isCacheableResponsePart(part));
152
+ })));
153
+ return input.map((item, index) => indexes.has(index) ? addResponseBreakpoint(item) : item);
154
+ }
155
+ /** @internal */
156
+ function shouldIncludeEncryptedReasoning(model, params) {
157
+ const reasoningContext = params.reasoning?.context;
158
+ return /^gpt-5\.6(?:-|$)/i.test(model) && (params.store === false || reasoningContext !== "current_turn");
159
+ }
160
+ function getCacheWriteTokens(message) {
161
+ const responseMetadata = message.response_metadata;
162
+ const reported = responseMetadata.usage?.input_tokens_details.cache_write_tokens;
163
+ if (reported != null) return reported;
164
+ const serialized = responseMetadata.metadata?.[CACHE_WRITE_METADATA_KEY];
165
+ if (serialized == null) return;
166
+ const parsed = Number(serialized);
167
+ return Number.isFinite(parsed) ? parsed : void 0;
168
+ }
169
+ function attachCacheWriteUsage(message) {
170
+ const cacheWriteTokens = getCacheWriteTokens(message);
171
+ if (cacheWriteTokens == null || !isAIMessage(message) || message.usage_metadata == null) return;
172
+ message.usage_metadata.input_token_details = {
173
+ ...message.usage_metadata.input_token_details,
174
+ cache_creation: cacheWriteTokens
175
+ };
176
+ const responseMetadata = message.response_metadata;
177
+ if (responseMetadata.metadata?.[CACHE_WRITE_METADATA_KEY] == null) return;
178
+ const metadata = { ...responseMetadata.metadata };
179
+ delete metadata[CACHE_WRITE_METADATA_KEY];
180
+ message.response_metadata = {
181
+ ...message.response_metadata,
182
+ metadata
183
+ };
184
+ }
185
+ function attachCacheWriteMetadata(response) {
186
+ const cacheWriteTokens = response.usage?.input_tokens_details.cache_write_tokens;
187
+ if (cacheWriteTokens == null) return response;
188
+ return {
189
+ ...response,
190
+ metadata: {
191
+ ...response.metadata ?? {},
192
+ [CACHE_WRITE_METADATA_KEY]: String(cacheWriteTokens)
193
+ }
194
+ };
195
+ }
196
+ function isResponsesStream(result) {
197
+ return Symbol.asyncIterator in result;
198
+ }
38
199
  function createUsageMetadata(usage) {
39
200
  const usageMetadata = {
40
201
  input_tokens: usage?.prompt_tokens ?? 0,
@@ -207,7 +368,7 @@ function getStreamChunkTokenIndices(chunk) {
207
368
  completion
208
369
  };
209
370
  }
210
- async function* delayStreamChunks(chunks, delay, signal, runManager) {
371
+ async function* delayStreamChunks(chunks, delay, signal, runManager, seenScalarMetadata) {
211
372
  let lastYieldedAt;
212
373
  for await (const chunk of chunks) {
213
374
  const outputChunks = delay != null && delay > 0 ? splitTextGenerationChunk(chunk) : [chunk];
@@ -220,6 +381,7 @@ async function* delayStreamChunks(chunks, delay, signal, runManager) {
220
381
  }
221
382
  signal?.throwIfAborted();
222
383
  lastYieldedAt = Date.now();
384
+ if (seenScalarMetadata != null) dropRepeatedScalarMetadata(outputChunk, seenScalarMetadata);
223
385
  await emitStreamChunkCallback(outputChunk, runManager);
224
386
  signal?.throwIfAborted();
225
387
  yield outputChunk;
@@ -342,11 +504,21 @@ var LibreChatOpenAICompletions = class extends ChatOpenAICompletions {
342
504
  includeReasoningContent;
343
505
  includeReasoningDetails;
344
506
  convertReasoningDetailsToContent;
507
+ promptCacheExplicit;
508
+ safetyIdentifier;
345
509
  constructor(fields) {
346
510
  super(fields);
347
511
  this.includeReasoningContent = fields?.includeReasoningContent;
348
512
  this.includeReasoningDetails = fields?.includeReasoningDetails;
349
513
  this.convertReasoningDetailsToContent = fields?.convertReasoningDetailsToContent;
514
+ this.promptCacheExplicit = fields?.promptCacheExplicit;
515
+ this.safetyIdentifier = fields?.safety_identifier;
516
+ }
517
+ invocationParams(options, extra) {
518
+ return applyManagedRequestParams(super.invocationParams(options, extra), {
519
+ promptCacheExplicit: this.promptCacheExplicit,
520
+ safetyIdentifier: this.safetyIdentifier
521
+ });
350
522
  }
351
523
  _getReasoningParams(options) {
352
524
  return getReasoningParams(this.reasoning, options);
@@ -355,7 +527,10 @@ var LibreChatOpenAICompletions = class extends ChatOpenAICompletions {
355
527
  return getCustomOpenAIClientOptions(this, options);
356
528
  }
357
529
  async completionWithRetry(request, requestOptions) {
358
- return completionWithFilteredOpenAIStream(request, requestOptions, super.completionWithRetry.bind(this));
530
+ return completionWithFilteredOpenAIStream(this.promptCacheExplicit === true ? {
531
+ ...request,
532
+ messages: addChatCacheBreakpoints(request.messages)
533
+ } : request, requestOptions, super.completionWithRetry.bind(this));
359
534
  }
360
535
  _convertCompletionsDeltaToBaseMessageChunk(delta, rawResponse, defaultRole) {
361
536
  const message = attachLibreChatDeltaFields(super._convertCompletionsDeltaToBaseMessageChunk(delta, rawResponse, defaultRole), delta);
@@ -535,6 +710,40 @@ var LibreChatOpenAICompletions = class extends ChatOpenAICompletions {
535
710
  }
536
711
  };
537
712
  var LibreChatOpenAIResponses = class extends ChatOpenAIResponses {
713
+ promptCacheExplicit;
714
+ safetyIdentifier;
715
+ constructor(fields) {
716
+ super(fields);
717
+ this.promptCacheExplicit = fields?.promptCacheExplicit;
718
+ this.safetyIdentifier = fields?.safety_identifier;
719
+ }
720
+ invocationParams(options) {
721
+ const params = applyManagedRequestParams(super.invocationParams(options), {
722
+ promptCacheExplicit: this.promptCacheExplicit,
723
+ safetyIdentifier: this.safetyIdentifier
724
+ });
725
+ if (shouldIncludeEncryptedReasoning(this.model, params)) params.include = [...new Set([...params.include ?? [], "reasoning.encrypted_content"])];
726
+ return params;
727
+ }
728
+ async completionWithRetry(request, requestOptions) {
729
+ const managedRequest = {
730
+ ...request,
731
+ input: this.promptCacheExplicit === true ? addResponseCacheBreakpoints(request.input) : request.input
732
+ };
733
+ const result = await super.completionWithRetry(managedRequest, requestOptions);
734
+ return isResponsesStream(result) ? result : attachCacheWriteMetadata(result);
735
+ }
736
+ async _generate(messages, options, runManager) {
737
+ const result = await super._generate(messages, options, runManager);
738
+ for (const generation of result.generations) attachCacheWriteUsage(generation.message);
739
+ return result;
740
+ }
741
+ async *_streamResponseChunks(messages, options, runManager) {
742
+ for await (const chunk of super._streamResponseChunks(messages, options, runManager)) {
743
+ attachCacheWriteUsage(chunk.message);
744
+ yield chunk;
745
+ }
746
+ }
538
747
  _getReasoningParams(options) {
539
748
  return getReasoningParams(this.reasoning, options);
540
749
  }
@@ -543,6 +752,19 @@ var LibreChatOpenAIResponses = class extends ChatOpenAIResponses {
543
752
  }
544
753
  };
545
754
  var LibreChatAzureOpenAICompletions = class extends AzureChatOpenAICompletions {
755
+ promptCacheExplicit;
756
+ safetyIdentifier;
757
+ constructor(fields) {
758
+ super(fields);
759
+ this.promptCacheExplicit = fields?.promptCacheExplicit;
760
+ this.safetyIdentifier = fields?.safety_identifier;
761
+ }
762
+ invocationParams(options, extra) {
763
+ return applyManagedRequestParams(super.invocationParams(options, extra), {
764
+ promptCacheExplicit: this.promptCacheExplicit,
765
+ safetyIdentifier: this.safetyIdentifier
766
+ });
767
+ }
546
768
  _getReasoningParams(options) {
547
769
  return getGatedReasoningParams(this.model, this.reasoning, options);
548
770
  }
@@ -601,10 +823,47 @@ var LibreChatAzureOpenAICompletions = class extends AzureChatOpenAICompletions {
601
823
  return requestOptions;
602
824
  }
603
825
  async completionWithRetry(request, requestOptions) {
604
- return completionWithFilteredOpenAIStream(request, requestOptions, super.completionWithRetry.bind(this));
826
+ return completionWithFilteredOpenAIStream(this.promptCacheExplicit === true ? {
827
+ ...request,
828
+ messages: addChatCacheBreakpoints(request.messages)
829
+ } : request, requestOptions, super.completionWithRetry.bind(this));
605
830
  }
606
831
  };
607
832
  var LibreChatAzureOpenAIResponses = class extends AzureChatOpenAIResponses {
833
+ promptCacheExplicit;
834
+ safetyIdentifier;
835
+ constructor(fields) {
836
+ super(fields);
837
+ this.promptCacheExplicit = fields?.promptCacheExplicit;
838
+ this.safetyIdentifier = fields?.safety_identifier;
839
+ }
840
+ invocationParams(options) {
841
+ const params = applyManagedRequestParams(super.invocationParams(options), {
842
+ promptCacheExplicit: this.promptCacheExplicit,
843
+ safetyIdentifier: this.safetyIdentifier
844
+ });
845
+ if (shouldIncludeEncryptedReasoning(this.model, params)) params.include = [...new Set([...params.include ?? [], "reasoning.encrypted_content"])];
846
+ return params;
847
+ }
848
+ async completionWithRetry(request, requestOptions) {
849
+ const managedRequest = {
850
+ ...request,
851
+ input: this.promptCacheExplicit === true ? addResponseCacheBreakpoints(request.input) : request.input
852
+ };
853
+ const result = await super.completionWithRetry(managedRequest, requestOptions);
854
+ return isResponsesStream(result) ? result : attachCacheWriteMetadata(result);
855
+ }
856
+ async _generate(messages, options, runManager) {
857
+ const result = await super._generate(messages, options, runManager);
858
+ for (const generation of result.generations) attachCacheWriteUsage(generation.message);
859
+ return result;
860
+ }
861
+ async *_streamResponseChunks(messages, options, runManager) {
862
+ for await (const chunk of super._streamResponseChunks(messages, options, runManager)) {
863
+ attachCacheWriteUsage(chunk.message);
864
+ yield chunk;
865
+ }
866
+ }
608
867
  _getReasoningParams(options) {
609
868
  return getGatedReasoningParams(this.model, this.reasoning, options);
610
869
  }
@@ -703,7 +962,15 @@ var ChatOpenAI$1 = class extends ChatOpenAI {
703
962
  return this.getReasoningParams(options);
704
963
  }
705
964
  async *_streamResponseChunks(messages, options, runManager) {
706
- yield* delayStreamChunks(super._streamResponseChunks(messages, options, void 0), this._lc_stream_delay, options.signal, runManager);
965
+ yield* delayStreamChunks(super._streamResponseChunks(messages, options, void 0), this._lc_stream_delay, options.signal, runManager, /* @__PURE__ */ new Map());
966
+ }
967
+ /**
968
+ * Raw variant that skips scalar-metadata de-duplication. Used by subclasses
969
+ * (e.g. `ChatOpenRouter`) that read `finish_reason` as a control signal and
970
+ * must de-duplicate only after their own finalization.
971
+ */
972
+ _streamRawResponseChunks(messages, options, runManager) {
973
+ return delayStreamChunks(super._streamResponseChunks(messages, options, void 0), this._lc_stream_delay, options.signal, runManager);
707
974
  }
708
975
  };
709
976
  var AzureChatOpenAI$1 = class extends AzureChatOpenAI {
@@ -777,7 +1044,7 @@ var AzureChatOpenAI$1 = class extends AzureChatOpenAI {
777
1044
  return requestOptions;
778
1045
  }
779
1046
  async *_streamResponseChunks(messages, options, runManager) {
780
- yield* delayStreamChunks(super._streamResponseChunks(messages, options, void 0), this._lc_stream_delay, options.signal, runManager);
1047
+ yield* delayStreamChunks(super._streamResponseChunks(messages, options, void 0), this._lc_stream_delay, options.signal, runManager, /* @__PURE__ */ new Map());
781
1048
  }
782
1049
  };
783
1050
  var ChatDeepSeek$1 = class extends ChatDeepSeek {
@@ -848,7 +1115,7 @@ var ChatDeepSeek$1 = class extends ChatDeepSeek {
848
1115
  };
849
1116
  }
850
1117
  async *_streamResponseChunks(messages, options, runManager) {
851
- yield* delayStreamChunks(this._streamResponseChunksWithReasoning(messages, options, void 0), this._lc_stream_delay, options.signal, runManager);
1118
+ yield* delayStreamChunks(this._streamResponseChunksWithReasoning(messages, options, void 0), this._lc_stream_delay, options.signal, runManager, /* @__PURE__ */ new Map());
852
1119
  }
853
1120
  /** Parses raw `<think>` fallback tags across chunks and emits sanitized DeepSeek stream chunks. */
854
1121
  async *_streamResponseChunksWithReasoning(messages, options, runManager) {
@@ -1081,7 +1348,7 @@ var ChatXAI$1 = class extends ChatXAI {
1081
1348
  };
1082
1349
  }
1083
1350
  async *_streamResponseChunks(messages, options, runManager) {
1084
- yield* delayStreamChunks(super._streamResponseChunks(messages, options, void 0), this._lc_stream_delay, options.signal, runManager);
1351
+ yield* delayStreamChunks(super._streamResponseChunks(messages, options, void 0), this._lc_stream_delay, options.signal, runManager, /* @__PURE__ */ new Map());
1085
1352
  }
1086
1353
  };
1087
1354
  //#endregion