@librechat/agents 3.3.3 → 3.3.5

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 (274) hide show
  1. package/dist/cjs/agents/AgentContext.cjs +2 -1
  2. package/dist/cjs/agents/AgentContext.cjs.map +1 -1
  3. package/dist/cjs/common/constants.cjs +21 -0
  4. package/dist/cjs/common/constants.cjs.map +1 -1
  5. package/dist/cjs/events.cjs +10 -1
  6. package/dist/cjs/events.cjs.map +1 -1
  7. package/dist/cjs/graphs/Graph.cjs +793 -111
  8. package/dist/cjs/graphs/Graph.cjs.map +1 -1
  9. package/dist/cjs/graphs/MultiAgentGraph.cjs +3 -1
  10. package/dist/cjs/graphs/MultiAgentGraph.cjs.map +1 -1
  11. package/dist/cjs/hooks/HookRegistry.cjs +25 -0
  12. package/dist/cjs/hooks/HookRegistry.cjs.map +1 -1
  13. package/dist/cjs/hooks/index.cjs +12 -0
  14. package/dist/cjs/hooks/index.cjs.map +1 -1
  15. package/dist/cjs/hooks/types.cjs +1 -0
  16. package/dist/cjs/hooks/types.cjs.map +1 -1
  17. package/dist/cjs/langfuse.cjs +8 -0
  18. package/dist/cjs/langfuse.cjs.map +1 -1
  19. package/dist/cjs/llm/anthropic/utils/message_inputs.cjs +79 -6
  20. package/dist/cjs/llm/anthropic/utils/message_inputs.cjs.map +1 -1
  21. package/dist/cjs/llm/bedrock/utils/message_inputs.cjs +38 -13
  22. package/dist/cjs/llm/bedrock/utils/message_inputs.cjs.map +1 -1
  23. package/dist/cjs/llm/google/utils/common.cjs +19 -7
  24. package/dist/cjs/llm/google/utils/common.cjs.map +1 -1
  25. package/dist/cjs/llm/invoke.cjs +331 -5
  26. package/dist/cjs/llm/invoke.cjs.map +1 -1
  27. package/dist/cjs/llm/openai/index.cjs +41 -5
  28. package/dist/cjs/llm/openai/index.cjs.map +1 -1
  29. package/dist/cjs/llm/openai/utils/index.cjs +25 -4
  30. package/dist/cjs/llm/openai/utils/index.cjs.map +1 -1
  31. package/dist/cjs/llm/openrouter/index.cjs +4 -1
  32. package/dist/cjs/llm/openrouter/index.cjs.map +1 -1
  33. package/dist/cjs/llm/preempt.cjs +132 -0
  34. package/dist/cjs/llm/preempt.cjs.map +1 -0
  35. package/dist/cjs/main.cjs +36 -5
  36. package/dist/cjs/messages/alternation.cjs +92 -0
  37. package/dist/cjs/messages/alternation.cjs.map +1 -0
  38. package/dist/cjs/messages/cache.cjs +54 -33
  39. package/dist/cjs/messages/cache.cjs.map +1 -1
  40. package/dist/cjs/messages/contextPruning.cjs +17 -43
  41. package/dist/cjs/messages/contextPruning.cjs.map +1 -1
  42. package/dist/cjs/messages/core.cjs +315 -23
  43. package/dist/cjs/messages/core.cjs.map +1 -1
  44. package/dist/cjs/messages/format.cjs +290 -60
  45. package/dist/cjs/messages/format.cjs.map +1 -1
  46. package/dist/cjs/messages/handoffCue.cjs +63 -0
  47. package/dist/cjs/messages/handoffCue.cjs.map +1 -0
  48. package/dist/cjs/messages/index.cjs +4 -1
  49. package/dist/cjs/messages/injected.cjs +60 -0
  50. package/dist/cjs/messages/injected.cjs.map +1 -0
  51. package/dist/cjs/messages/prune.cjs +503 -127
  52. package/dist/cjs/messages/prune.cjs.map +1 -1
  53. package/dist/cjs/run.cjs +80 -7
  54. package/dist/cjs/run.cjs.map +1 -1
  55. package/dist/cjs/session/handlers.cjs +18 -9
  56. package/dist/cjs/session/handlers.cjs.map +1 -1
  57. package/dist/cjs/stream.cjs +35 -10
  58. package/dist/cjs/stream.cjs.map +1 -1
  59. package/dist/cjs/summarization/node.cjs +28 -14
  60. package/dist/cjs/summarization/node.cjs.map +1 -1
  61. package/dist/cjs/tools/ToolNode.cjs +56 -63
  62. package/dist/cjs/tools/ToolNode.cjs.map +1 -1
  63. package/dist/cjs/tools/search/keenable-scraper.cjs +90 -0
  64. package/dist/cjs/tools/search/keenable-scraper.cjs.map +1 -0
  65. package/dist/cjs/tools/search/tool.cjs +9 -1
  66. package/dist/cjs/tools/search/tool.cjs.map +1 -1
  67. package/dist/cjs/tools/toolOutputReferences.cjs +6 -0
  68. package/dist/cjs/tools/toolOutputReferences.cjs.map +1 -1
  69. package/dist/cjs/utils/index.cjs +1 -1
  70. package/dist/cjs/utils/llm.cjs +1 -1
  71. package/dist/cjs/utils/llm.cjs.map +1 -1
  72. package/dist/cjs/utils/tokens.cjs +307 -20
  73. package/dist/cjs/utils/tokens.cjs.map +1 -1
  74. package/dist/cjs/utils/toolContent.cjs +1514 -0
  75. package/dist/cjs/utils/toolContent.cjs.map +1 -0
  76. package/dist/esm/agents/AgentContext.mjs +2 -1
  77. package/dist/esm/agents/AgentContext.mjs.map +1 -1
  78. package/dist/esm/common/constants.mjs +19 -1
  79. package/dist/esm/common/constants.mjs.map +1 -1
  80. package/dist/esm/events.mjs +10 -1
  81. package/dist/esm/events.mjs.map +1 -1
  82. package/dist/esm/graphs/Graph.mjs +798 -116
  83. package/dist/esm/graphs/Graph.mjs.map +1 -1
  84. package/dist/esm/graphs/MultiAgentGraph.mjs +3 -1
  85. package/dist/esm/graphs/MultiAgentGraph.mjs.map +1 -1
  86. package/dist/esm/hooks/HookRegistry.mjs +25 -0
  87. package/dist/esm/hooks/HookRegistry.mjs.map +1 -1
  88. package/dist/esm/hooks/index.mjs +12 -1
  89. package/dist/esm/hooks/index.mjs.map +1 -1
  90. package/dist/esm/hooks/types.mjs +1 -0
  91. package/dist/esm/hooks/types.mjs.map +1 -1
  92. package/dist/esm/langfuse.mjs +9 -1
  93. package/dist/esm/langfuse.mjs.map +1 -1
  94. package/dist/esm/llm/anthropic/utils/message_inputs.mjs +79 -6
  95. package/dist/esm/llm/anthropic/utils/message_inputs.mjs.map +1 -1
  96. package/dist/esm/llm/bedrock/utils/message_inputs.mjs +38 -13
  97. package/dist/esm/llm/bedrock/utils/message_inputs.mjs.map +1 -1
  98. package/dist/esm/llm/google/utils/common.mjs +19 -7
  99. package/dist/esm/llm/google/utils/common.mjs.map +1 -1
  100. package/dist/esm/llm/invoke.mjs +332 -8
  101. package/dist/esm/llm/invoke.mjs.map +1 -1
  102. package/dist/esm/llm/openai/index.mjs +41 -5
  103. package/dist/esm/llm/openai/index.mjs.map +1 -1
  104. package/dist/esm/llm/openai/utils/index.mjs +25 -4
  105. package/dist/esm/llm/openai/utils/index.mjs.map +1 -1
  106. package/dist/esm/llm/openrouter/index.mjs +4 -1
  107. package/dist/esm/llm/openrouter/index.mjs.map +1 -1
  108. package/dist/esm/llm/preempt.mjs +131 -0
  109. package/dist/esm/llm/preempt.mjs.map +1 -0
  110. package/dist/esm/main.mjs +14 -11
  111. package/dist/esm/messages/alternation.mjs +91 -0
  112. package/dist/esm/messages/alternation.mjs.map +1 -0
  113. package/dist/esm/messages/cache.mjs +55 -34
  114. package/dist/esm/messages/cache.mjs.map +1 -1
  115. package/dist/esm/messages/contextPruning.mjs +17 -43
  116. package/dist/esm/messages/contextPruning.mjs.map +1 -1
  117. package/dist/esm/messages/core.mjs +305 -24
  118. package/dist/esm/messages/core.mjs.map +1 -1
  119. package/dist/esm/messages/format.mjs +290 -61
  120. package/dist/esm/messages/format.mjs.map +1 -1
  121. package/dist/esm/messages/handoffCue.mjs +61 -0
  122. package/dist/esm/messages/handoffCue.mjs.map +1 -0
  123. package/dist/esm/messages/index.mjs +4 -1
  124. package/dist/esm/messages/injected.mjs +60 -0
  125. package/dist/esm/messages/injected.mjs.map +1 -0
  126. package/dist/esm/messages/prune.mjs +500 -128
  127. package/dist/esm/messages/prune.mjs.map +1 -1
  128. package/dist/esm/run.mjs +80 -7
  129. package/dist/esm/run.mjs.map +1 -1
  130. package/dist/esm/session/handlers.mjs +19 -10
  131. package/dist/esm/session/handlers.mjs.map +1 -1
  132. package/dist/esm/stream.mjs +34 -11
  133. package/dist/esm/stream.mjs.map +1 -1
  134. package/dist/esm/summarization/node.mjs +28 -14
  135. package/dist/esm/summarization/node.mjs.map +1 -1
  136. package/dist/esm/tools/ToolNode.mjs +56 -63
  137. package/dist/esm/tools/ToolNode.mjs.map +1 -1
  138. package/dist/esm/tools/search/keenable-scraper.mjs +88 -0
  139. package/dist/esm/tools/search/keenable-scraper.mjs.map +1 -0
  140. package/dist/esm/tools/search/tool.mjs +9 -1
  141. package/dist/esm/tools/search/tool.mjs.map +1 -1
  142. package/dist/esm/tools/toolOutputReferences.mjs +6 -0
  143. package/dist/esm/tools/toolOutputReferences.mjs.map +1 -1
  144. package/dist/esm/utils/index.mjs +1 -1
  145. package/dist/esm/utils/llm.mjs +1 -1
  146. package/dist/esm/utils/llm.mjs.map +1 -1
  147. package/dist/esm/utils/tokens.mjs +307 -21
  148. package/dist/esm/utils/tokens.mjs.map +1 -1
  149. package/dist/esm/utils/toolContent.mjs +1503 -0
  150. package/dist/esm/utils/toolContent.mjs.map +1 -0
  151. package/dist/types/common/constants.d.ts +18 -0
  152. package/dist/types/graphs/Graph.d.ts +139 -2
  153. package/dist/types/hooks/HookRegistry.d.ts +15 -0
  154. package/dist/types/hooks/index.d.ts +12 -1
  155. package/dist/types/hooks/types.d.ts +45 -6
  156. package/dist/types/index.d.ts +1 -1
  157. package/dist/types/llm/invoke.d.ts +34 -1
  158. package/dist/types/llm/openai/index.d.ts +4 -0
  159. package/dist/types/llm/openai/utils/index.d.ts +2 -0
  160. package/dist/types/llm/openrouter/index.d.ts +5 -0
  161. package/dist/types/llm/preempt.d.ts +40 -0
  162. package/dist/types/messages/alternation.d.ts +23 -0
  163. package/dist/types/messages/contextPruning.d.ts +2 -1
  164. package/dist/types/messages/core.d.ts +36 -0
  165. package/dist/types/messages/format.d.ts +6 -0
  166. package/dist/types/messages/handoffCue.d.ts +40 -0
  167. package/dist/types/messages/index.d.ts +3 -0
  168. package/dist/types/messages/injected.d.ts +3 -0
  169. package/dist/types/messages/prune.d.ts +16 -12
  170. package/dist/types/run.d.ts +7 -0
  171. package/dist/types/stream.d.ts +14 -0
  172. package/dist/types/tools/ToolNode.d.ts +0 -7
  173. package/dist/types/tools/search/keenable-scraper.d.ts +15 -0
  174. package/dist/types/tools/search/types.d.ts +31 -2
  175. package/dist/types/types/graph.d.ts +8 -1
  176. package/dist/types/types/run.d.ts +65 -0
  177. package/dist/types/types/stream.d.ts +1 -26
  178. package/dist/types/utils/tokens.d.ts +7 -0
  179. package/dist/types/utils/toolContent.d.ts +107 -0
  180. package/package.json +1 -1
  181. package/src/__tests__/stream.eagerEventExecution.test.ts +74 -0
  182. package/src/agents/AgentContext.ts +1 -0
  183. package/src/{splitStream.test.ts → aggregator.test.ts} +59 -666
  184. package/src/common/constants.ts +21 -0
  185. package/src/events.ts +15 -1
  186. package/src/graphs/Graph.ts +1261 -198
  187. package/src/graphs/MultiAgentGraph.ts +6 -1
  188. package/src/graphs/__tests__/Graph.contextOverflow.test.ts +1139 -4
  189. package/src/graphs/__tests__/Graph.preemptSignal.test.ts +126 -0
  190. package/src/hooks/HookRegistry.ts +40 -0
  191. package/src/hooks/__tests__/preemptBoundary.test.ts +152 -0
  192. package/src/hooks/index.ts +16 -2
  193. package/src/hooks/types.ts +47 -3
  194. package/src/index.ts +1 -1
  195. package/src/langfuse.ts +26 -1
  196. package/src/llm/anthropic/utils/message_inputs.ts +158 -12
  197. package/src/llm/anthropic/utils/tool-id-normalization.test.ts +109 -0
  198. package/src/llm/bedrock/utils/message_inputs.test.ts +131 -1
  199. package/src/llm/bedrock/utils/message_inputs.ts +100 -24
  200. package/src/llm/bedrock/utils/toolResultCachePoint.test.ts +15 -0
  201. package/src/llm/google/utils/common.test.ts +75 -9
  202. package/src/llm/google/utils/common.ts +34 -3
  203. package/src/llm/invoke.alternation.test.ts +87 -0
  204. package/src/llm/invoke.handoffCue.test.ts +112 -0
  205. package/src/llm/invoke.test.ts +488 -0
  206. package/src/llm/invoke.ts +611 -8
  207. package/src/llm/openai/index.ts +106 -6
  208. package/src/llm/openai/utils/index.ts +109 -53
  209. package/src/llm/openai/utils/messages.test.ts +330 -1
  210. package/src/llm/openrouter/index.ts +12 -2
  211. package/src/llm/openrouter/reasoning.test.ts +314 -0
  212. package/src/llm/preempt.test.ts +323 -0
  213. package/src/llm/preempt.ts +178 -0
  214. package/src/messages/__tests__/observationMasking.test.ts +93 -2
  215. package/src/messages/alternation.test.ts +212 -0
  216. package/src/messages/alternation.ts +112 -0
  217. package/src/messages/cache.tail.test.ts +193 -0
  218. package/src/messages/cache.test.ts +113 -0
  219. package/src/messages/cache.ts +92 -60
  220. package/src/messages/contextPruning.test.ts +184 -0
  221. package/src/messages/contextPruning.ts +49 -42
  222. package/src/messages/core.ts +653 -40
  223. package/src/messages/ensureThinkingBlock.test.ts +49 -1
  224. package/src/messages/foldToollessToolBlocks.test.ts +163 -5
  225. package/src/messages/format.ts +454 -91
  226. package/src/messages/formatAgentMessages.steer.test.ts +267 -0
  227. package/src/messages/formatAgentMessages.test.ts +604 -14
  228. package/src/messages/formatAgentMessages.tools.test.ts +103 -0
  229. package/src/messages/handoffCue.test.ts +96 -0
  230. package/src/messages/handoffCue.ts +78 -0
  231. package/src/messages/index.ts +3 -0
  232. package/src/messages/injected.test.ts +90 -0
  233. package/src/messages/injected.ts +74 -0
  234. package/src/messages/prune.ts +996 -183
  235. package/src/run.ts +91 -6
  236. package/src/scripts/preempt-probe.ts +330 -0
  237. package/src/scripts/preempt-scenarios.ts +388 -0
  238. package/src/session/handlers.ts +32 -12
  239. package/src/specs/handoffCue.test.ts +165 -0
  240. package/src/specs/langfuse-callbacks.test.ts +352 -2
  241. package/src/specs/preemptSeal.test.ts +309 -0
  242. package/src/specs/prune.test.ts +1083 -6
  243. package/src/specs/summarization-unit.test.ts +105 -0
  244. package/src/specs/tokens.test.ts +609 -32
  245. package/src/stream.dispatch.test.ts +63 -0
  246. package/src/stream.ts +59 -23
  247. package/src/summarization/__tests__/node.test.ts +77 -0
  248. package/src/summarization/node.ts +46 -18
  249. package/src/tools/ToolNode.ts +150 -99
  250. package/src/tools/__tests__/ToolNode.onResultCompletion.test.ts +148 -1
  251. package/src/tools/__tests__/ToolNode.outputReferences.test.ts +112 -3
  252. package/src/tools/__tests__/annotateMessagesForLLM.test.ts +32 -0
  253. package/src/tools/__tests__/directToolHooks.test.ts +255 -7
  254. package/src/tools/search/keenable-scraper.test.ts +153 -0
  255. package/src/tools/search/keenable-scraper.ts +137 -0
  256. package/src/tools/search/tool.ts +13 -2
  257. package/src/tools/search/types.ts +50 -3
  258. package/src/tools/toolOutputReferences.ts +6 -0
  259. package/src/types/graph.ts +12 -1
  260. package/src/types/run.ts +67 -0
  261. package/src/types/stream.ts +1 -42
  262. package/src/utils/llm.test.ts +18 -0
  263. package/src/utils/llm.ts +4 -1
  264. package/src/utils/tokens.ts +586 -31
  265. package/src/utils/toolContent.test.ts +1432 -0
  266. package/src/utils/toolContent.ts +2707 -0
  267. package/dist/cjs/splitStream.cjs +0 -151
  268. package/dist/cjs/splitStream.cjs.map +0 -1
  269. package/dist/esm/splitStream.mjs +0 -150
  270. package/dist/esm/splitStream.mjs.map +0 -1
  271. package/dist/types/mockStream.d.ts +0 -32
  272. package/dist/types/splitStream.d.ts +0 -37
  273. package/src/mockStream.ts +0 -99
  274. package/src/splitStream.ts +0 -234
@@ -29,16 +29,28 @@ import type {
29
29
  BaseMessageChunk,
30
30
  UsageMetadata,
31
31
  } from '@langchain/core/messages';
32
+ import type { ChatModelStreamEvent } from '@langchain/core/language_models/event';
32
33
  import type { BindToolsInput } from '@langchain/core/language_models/chat_models';
33
34
  import type { ChatGeneration, ChatResult } from '@langchain/core/outputs';
34
35
  import type { ChatXAIInput } from '@langchain/xai';
35
36
  import type * as t from '@langchain/openai';
36
37
  import type { SeenScalarMetadata } from './streamMetadata';
37
38
  import type { HeaderValue, HeadersLike } from './types';
39
+ import type { PromptCacheTtl } from '@/messages/cache';
40
+ import {
41
+ buildAnthropicCacheControl,
42
+ resolvePromptCacheTtl,
43
+ stripAnthropicCacheControl,
44
+ stripBedrockCacheControl,
45
+ } from '@/messages/cache';
38
46
  import {
39
47
  STREAMED_TOOL_CALL_ADAPTER_METADATA_KEY,
40
48
  OPENAI_CHAT_SEQUENTIAL_STREAMED_TOOL_CALL_ADAPTER,
41
49
  } from '@/tools/streamedToolCallSeals';
50
+ import {
51
+ projectOpenAIResponsesToolMessageContent,
52
+ projectToolStreamContentForProvider,
53
+ } from '@/messages/core';
42
54
  import { isReasoningModel, _convertMessagesToOpenAIParams } from './utils';
43
55
  import { dropRepeatedScalarMetadata } from './streamMetadata';
44
56
 
@@ -102,6 +114,9 @@ type LibreChatOpenAIFields = t.ChatOpenAIFields & {
102
114
  includeReasoningContent?: boolean;
103
115
  includeReasoningDetails?: boolean;
104
116
  convertReasoningDetailsToContent?: boolean;
117
+ preserveToolCacheControl?: boolean;
118
+ responsesPromptCache?: boolean;
119
+ responsesPromptCacheTtl?: PromptCacheTtl;
105
120
  promptCacheExplicit?: boolean;
106
121
  safety_identifier?: string;
107
122
  };
@@ -168,6 +183,40 @@ type OpenAIManagedRequestParams = {
168
183
  };
169
184
  safety_identifier?: string;
170
185
  };
186
+
187
+ function stripResponsesToolCacheControl<T>(tools: T): T {
188
+ if (!Array.isArray(tools)) {
189
+ return tools;
190
+ }
191
+
192
+ return tools.map((tool) => {
193
+ if (tool == null || typeof tool !== 'object') {
194
+ return tool;
195
+ }
196
+ const clone = { ...(tool as Record<string, unknown>) };
197
+ delete clone.cache_control;
198
+ if (
199
+ clone.extras != null &&
200
+ typeof clone.extras === 'object' &&
201
+ !Array.isArray(clone.extras)
202
+ ) {
203
+ clone.extras = { ...(clone.extras as Record<string, unknown>) };
204
+ delete (clone.extras as Record<string, unknown>).cache_control;
205
+ }
206
+ return clone;
207
+ }) as T;
208
+ }
209
+
210
+ function projectOpenAIResponsesProviderMessages(
211
+ messages: BaseMessage[]
212
+ ): BaseMessage[] {
213
+ return projectOpenAIResponsesToolMessageContent(
214
+ stripAnthropicCacheControl(
215
+ stripBedrockCacheControl(projectToolStreamContentForProvider(messages))
216
+ )
217
+ );
218
+ }
219
+
171
220
  type ResponsesRequest =
172
221
  | OpenAIClient.Responses.ResponseCreateParamsStreaming
173
222
  | OpenAIClient.Responses.ResponseCreateParamsNonStreaming;
@@ -1116,6 +1165,7 @@ class LibreChatOpenAICompletions extends OriginalChatOpenAICompletions {
1116
1165
  private includeReasoningContent?: boolean;
1117
1166
  private includeReasoningDetails?: boolean;
1118
1167
  private convertReasoningDetailsToContent?: boolean;
1168
+ private preserveToolCacheControl?: boolean;
1119
1169
  private promptCacheExplicit?: boolean;
1120
1170
  private safetyIdentifier?: string;
1121
1171
 
@@ -1125,6 +1175,7 @@ class LibreChatOpenAICompletions extends OriginalChatOpenAICompletions {
1125
1175
  this.includeReasoningDetails = fields?.includeReasoningDetails;
1126
1176
  this.convertReasoningDetailsToContent =
1127
1177
  fields?.convertReasoningDetailsToContent;
1178
+ this.preserveToolCacheControl = fields?.preserveToolCacheControl;
1128
1179
  this.promptCacheExplicit = fields?.promptCacheExplicit;
1129
1180
  this.safetyIdentifier = fields?.safety_identifier;
1130
1181
  }
@@ -1225,6 +1276,7 @@ class LibreChatOpenAICompletions extends OriginalChatOpenAICompletions {
1225
1276
  includeReasoningContent: this.includeReasoningContent,
1226
1277
  includeReasoningDetails: this.includeReasoningDetails,
1227
1278
  convertReasoningDetailsToContent: this.convertReasoningDetailsToContent,
1279
+ preserveToolCacheControl: this.preserveToolCacheControl,
1228
1280
  }
1229
1281
  );
1230
1282
 
@@ -1393,6 +1445,7 @@ class LibreChatOpenAICompletions extends OriginalChatOpenAICompletions {
1393
1445
  includeReasoningContent: this.includeReasoningContent,
1394
1446
  includeReasoningDetails: this.includeReasoningDetails,
1395
1447
  convertReasoningDetailsToContent: this.convertReasoningDetailsToContent,
1448
+ preserveToolCacheControl: this.preserveToolCacheControl,
1396
1449
  });
1397
1450
 
1398
1451
  const params = {
@@ -1533,21 +1586,52 @@ class LibreChatOpenAICompletions extends OriginalChatOpenAICompletions {
1533
1586
 
1534
1587
  class LibreChatOpenAIResponses extends OriginalChatOpenAIResponses {
1535
1588
  private promptCacheExplicit?: boolean;
1589
+ private responsesPromptCache?: boolean;
1590
+ private responsesPromptCacheTtl?: PromptCacheTtl;
1536
1591
  private safetyIdentifier?: string;
1537
1592
 
1538
1593
  constructor(fields?: LibreChatOpenAIFields) {
1539
1594
  super(fields);
1540
1595
  this.promptCacheExplicit = fields?.promptCacheExplicit;
1596
+ this.responsesPromptCache = fields?.responsesPromptCache;
1597
+ this.responsesPromptCacheTtl = fields?.responsesPromptCacheTtl;
1541
1598
  this.safetyIdentifier = fields?.safety_identifier;
1542
1599
  }
1543
1600
 
1544
1601
  invocationParams(
1545
1602
  options?: this['ParsedCallOptions']
1546
1603
  ): ReturnType<OriginalChatOpenAIResponses['invocationParams']> {
1547
- const params = applyManagedRequestParams(super.invocationParams(options), {
1548
- promptCacheExplicit: this.promptCacheExplicit,
1549
- safetyIdentifier: this.safetyIdentifier,
1550
- });
1604
+ const cacheOptions = options as
1605
+ | {
1606
+ promptCache?: boolean;
1607
+ promptCacheTtl?: PromptCacheTtl;
1608
+ }
1609
+ | undefined;
1610
+ const promptCache = cacheOptions?.promptCache ?? this.responsesPromptCache;
1611
+ const cacheControl =
1612
+ promptCache === true
1613
+ ? buildAnthropicCacheControl(
1614
+ resolvePromptCacheTtl(
1615
+ cacheOptions?.promptCacheTtl ?? this.responsesPromptCacheTtl
1616
+ )
1617
+ )
1618
+ : undefined;
1619
+ const baseParams = applyManagedRequestParams(
1620
+ super.invocationParams(options),
1621
+ {
1622
+ promptCacheExplicit: this.promptCacheExplicit,
1623
+ safetyIdentifier: this.safetyIdentifier,
1624
+ }
1625
+ );
1626
+ const params = {
1627
+ ...baseParams,
1628
+ ...(baseParams.tools != null && {
1629
+ tools: stripResponsesToolCacheControl(baseParams.tools),
1630
+ }),
1631
+ ...(cacheControl != null && {
1632
+ cache_control: cacheControl,
1633
+ }),
1634
+ };
1551
1635
  if (shouldIncludeEncryptedReasoning(this.model, params)) {
1552
1636
  params.include = [
1553
1637
  ...new Set([
@@ -1592,7 +1676,11 @@ class LibreChatOpenAIResponses extends OriginalChatOpenAIResponses {
1592
1676
  options: this['ParsedCallOptions'],
1593
1677
  runManager?: CallbackManagerForLLMRun
1594
1678
  ): Promise<ChatResult> {
1595
- const result = await super._generate(messages, options, runManager);
1679
+ const result = await super._generate(
1680
+ projectOpenAIResponsesProviderMessages(messages),
1681
+ options,
1682
+ runManager
1683
+ );
1596
1684
  for (const generation of result.generations) {
1597
1685
  attachCacheWriteUsage(generation.message);
1598
1686
  }
@@ -1605,7 +1693,7 @@ class LibreChatOpenAIResponses extends OriginalChatOpenAIResponses {
1605
1693
  runManager?: CallbackManagerForLLMRun
1606
1694
  ): AsyncGenerator<ChatGenerationChunk> {
1607
1695
  for await (const chunk of super._streamResponseChunks(
1608
- messages,
1696
+ projectOpenAIResponsesProviderMessages(messages),
1609
1697
  options,
1610
1698
  runManager
1611
1699
  )) {
@@ -1614,6 +1702,18 @@ class LibreChatOpenAIResponses extends OriginalChatOpenAIResponses {
1614
1702
  }
1615
1703
  }
1616
1704
 
1705
+ async *_streamChatModelEvents(
1706
+ messages: BaseMessage[],
1707
+ options: this['ParsedCallOptions'],
1708
+ runManager?: CallbackManagerForLLMRun
1709
+ ): AsyncGenerator<ChatModelStreamEvent> {
1710
+ yield* super._streamChatModelEvents(
1711
+ projectOpenAIResponsesProviderMessages(messages),
1712
+ options,
1713
+ runManager
1714
+ );
1715
+ }
1716
+
1617
1717
  protected _getReasoningParams(
1618
1718
  options?: this['ParsedCallOptions']
1619
1719
  ): OpenAIClient.Reasoning | undefined {
@@ -3,7 +3,6 @@
3
3
  import { type OpenAI as OpenAIClient } from 'openai';
4
4
  import { ChatGenerationChunk } from '@langchain/core/outputs';
5
5
  import {
6
- convertLangChainToolCallToOpenAI,
7
6
  makeInvalidToolCall,
8
7
  parseToolCall,
9
8
  } from '@langchain/core/output_parsers/openai_tools';
@@ -37,11 +36,23 @@ import type {
37
36
  ChatOpenAIReasoningSummary,
38
37
  } from '@langchain/openai';
39
38
  import type { ToolCall, ToolCallChunk } from '@langchain/core/messages/tool';
39
+ import {
40
+ getBoundedCacheControlledTextToolContent,
41
+ getComputerCallOutputScreenshot,
42
+ isComputerCallOutputMessage,
43
+ serializeStructuredValueBounded,
44
+ } from '@/utils/toolContent';
40
45
  import {
41
46
  STREAMED_TOOL_CALL_SEAL_METADATA_KEY,
42
47
  STREAMED_TOOL_CALL_ADAPTER_METADATA_KEY,
43
48
  OPENAI_RESPONSES_STREAMED_TOOL_CALL_ADAPTER,
44
49
  } from '@/tools/streamedToolCallSeals';
50
+ import {
51
+ calculateMaxToolCallInputChars,
52
+ projectToolCallInputs,
53
+ serializeToolCallInput,
54
+ } from '@/messages/prune';
55
+ import { HARD_MAX_TOOL_RESULT_CHARS } from '@/utils/truncation';
45
56
  import { toLangChainContent } from '@/messages/langchain';
46
57
 
47
58
  export type { OpenAICallOptions, OpenAIChatInput };
@@ -83,6 +94,32 @@ type OpenAIRoleEnum =
83
94
  type OpenAICompletionParam =
84
95
  OpenAIClient.Chat.Completions.ChatCompletionMessageParam;
85
96
 
97
+ const MAX_PROVIDER_TOOL_CALL_INPUT_CHARS = calculateMaxToolCallInputChars();
98
+
99
+ function convertLangChainToolCallToBoundedOpenAI(toolCall: ToolCall): {
100
+ id: string;
101
+ type: 'function';
102
+ function: {
103
+ name: string;
104
+ arguments: string;
105
+ };
106
+ } {
107
+ if (toolCall.id == null) {
108
+ throw new Error('All OpenAI tool calls must have an "id" field.');
109
+ }
110
+ return {
111
+ id: toolCall.id,
112
+ type: 'function',
113
+ function: {
114
+ name: toolCall.name,
115
+ arguments: serializeToolCallInput(
116
+ toolCall.args,
117
+ MAX_PROVIDER_TOOL_CALL_INPUT_CHARS
118
+ ),
119
+ },
120
+ };
121
+ }
122
+
86
123
  function extractGenericMessageCustomRole(message: ChatMessage) {
87
124
  if (
88
125
  message.role !== 'system' &&
@@ -300,6 +337,8 @@ export interface ConvertMessagesOptions {
300
337
  includeReasoningDetails?: boolean;
301
338
  /** Convert reasoning_details to content blocks for Claude (requires content array format) */
302
339
  convertReasoningDetailsToContent?: boolean;
340
+ /** Preserve OpenRouter's canonical cache-decorated tool text block. */
341
+ preserveToolCacheControl?: boolean;
303
342
  }
304
343
 
305
344
  // Used in LangSmith, export is important here
@@ -308,9 +347,13 @@ export function _convertMessagesToOpenAIParams(
308
347
  model?: string,
309
348
  options?: ConvertMessagesOptions
310
349
  ): OpenAICompletionParam[] {
350
+ const projectedMessages = projectToolCallInputs(
351
+ messages,
352
+ MAX_PROVIDER_TOOL_CALL_INPUT_CHARS
353
+ );
311
354
  let hasReasoningToolCallContext = false;
312
355
  // TODO: Function messages do not support array content, fix cast
313
- return messages.flatMap((message) => {
356
+ return projectedMessages.flatMap((message) => {
314
357
  let role = messageToOpenAIRole(message);
315
358
  if (role === 'system' && isReasoningModel(model)) {
316
359
  role = 'developer';
@@ -318,22 +361,40 @@ export function _convertMessagesToOpenAIParams(
318
361
 
319
362
  let hasAnthropicThinkingBlock: boolean = false;
320
363
 
321
- const content =
322
- typeof message.content === 'string'
323
- ? message.content
324
- : message.content.map((m) => {
325
- if ('type' in m && m.type === 'thinking') {
326
- hasAnthropicThinkingBlock = true;
327
- return m;
328
- }
329
- if (isDataContentBlock(m)) {
330
- return convertToProviderContentBlock(
331
- m,
332
- completionsApiContentBlockConverter
333
- );
334
- }
364
+ let content: unknown;
365
+ if (
366
+ role === 'tool' &&
367
+ typeof message.content !== 'string' &&
368
+ !isComputerCallOutputMessage(message)
369
+ ) {
370
+ content =
371
+ options?.preserveToolCacheControl === true
372
+ ? getBoundedCacheControlledTextToolContent(
373
+ message.content,
374
+ HARD_MAX_TOOL_RESULT_CHARS
375
+ )
376
+ : undefined;
377
+ content ??= serializeStructuredValueBounded(
378
+ message.content,
379
+ HARD_MAX_TOOL_RESULT_CHARS
380
+ ).content;
381
+ } else if (typeof message.content === 'string') {
382
+ content = message.content;
383
+ } else {
384
+ content = message.content.map((m) => {
385
+ if ('type' in m && m.type === 'thinking') {
386
+ hasAnthropicThinkingBlock = true;
335
387
  return m;
336
- });
388
+ }
389
+ if (isDataContentBlock(m)) {
390
+ return convertToProviderContentBlock(
391
+ m,
392
+ completionsApiContentBlockConverter
393
+ );
394
+ }
395
+ return m;
396
+ });
397
+ }
337
398
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
338
399
  const completionParam: Record<string, any> = {
339
400
  role,
@@ -351,7 +412,7 @@ export function _convertMessagesToOpenAIParams(
351
412
  if (isAIMessage(message) && !!message.tool_calls?.length) {
352
413
  messageHasToolCalls = true;
353
414
  completionParam.tool_calls = message.tool_calls.map(
354
- convertLangChainToolCallToOpenAI
415
+ convertLangChainToolCallToBoundedOpenAI
355
416
  );
356
417
  completionParam.content = hasAnthropicThinkingBlock ? content : '';
357
418
  if (
@@ -527,7 +588,11 @@ export function _convertMessagesToOpenAIResponsesParams(
527
588
  model?: string,
528
589
  zdrEnabled?: boolean
529
590
  ): ResponsesInputItem[] {
530
- return messages.flatMap(
591
+ const projectedMessages = projectToolCallInputs(
592
+ messages,
593
+ MAX_PROVIDER_TOOL_CALL_INPUT_CHARS
594
+ );
595
+ return projectedMessages.flatMap(
531
596
  (lcMsg): ResponsesInputItem | ResponsesInputItem[] => {
532
597
  const additional_kwargs =
533
598
  lcMsg.additional_kwargs as BaseMessageFields['additional_kwargs'] & {
@@ -552,44 +617,29 @@ export function _convertMessagesToOpenAIResponsesParams(
552
617
 
553
618
  // Handle computer call output
554
619
  if (additional_kwargs.type === 'computer_call_output') {
555
- const output = (() => {
556
- if (typeof toolMessage.content === 'string') {
557
- return {
558
- type: 'computer_screenshot' as const,
559
- image_url: toolMessage.content,
560
- };
561
- }
562
-
563
- if (Array.isArray(toolMessage.content)) {
564
- const oaiScreenshot = toolMessage.content.find(
565
- (i) => i.type === 'computer_screenshot'
566
- ) as { type: 'computer_screenshot'; image_url: string };
567
-
568
- if (oaiScreenshot) return oaiScreenshot;
569
-
570
- const lcImage = toolMessage.content.find(
571
- (i) => i.type === 'image_url'
572
- ) as MessageContentImageUrl;
573
-
574
- if (lcImage) {
575
- return {
576
- type: 'computer_screenshot' as const,
577
- image_url:
578
- typeof lcImage.image_url === 'string'
579
- ? lcImage.image_url
580
- : lcImage.image_url.url,
581
- };
582
- }
583
- }
584
-
620
+ const screenshot = getComputerCallOutputScreenshot(
621
+ toolMessage.content
622
+ );
623
+ if (screenshot == null) {
585
624
  throw new Error('Invalid computer call output');
586
- })();
625
+ }
587
626
 
588
- return {
627
+ const output: OpenAIClient.Responses.ResponseComputerToolCallOutputScreenshot =
628
+ 'image_url' in screenshot
629
+ ? {
630
+ type: 'computer_screenshot',
631
+ image_url: screenshot.image_url,
632
+ }
633
+ : {
634
+ type: 'computer_screenshot',
635
+ file_id: screenshot.file_id,
636
+ };
637
+ const computerCallOutput: ResponsesInputItem = {
589
638
  type: 'computer_call_output',
590
639
  output,
591
640
  call_id: toolMessage.tool_call_id,
592
641
  };
642
+ return computerCallOutput;
593
643
  }
594
644
 
595
645
  return {
@@ -598,7 +648,10 @@ export function _convertMessagesToOpenAIResponsesParams(
598
648
  id: toolMessage.id?.startsWith('fc_') ? toolMessage.id : undefined,
599
649
  output:
600
650
  typeof toolMessage.content !== 'string'
601
- ? JSON.stringify(toolMessage.content)
651
+ ? serializeStructuredValueBounded(
652
+ toolMessage.content,
653
+ HARD_MAX_TOOL_RESULT_CHARS
654
+ ).content
602
655
  : toolMessage.content,
603
656
  };
604
657
  }
@@ -682,7 +735,10 @@ export function _convertMessagesToOpenAIResponsesParams(
682
735
  (toolCall): ResponsesInputItem => ({
683
736
  type: 'function_call',
684
737
  name: toolCall.name,
685
- arguments: JSON.stringify(toolCall.args),
738
+ arguments: serializeToolCallInput(
739
+ toolCall.args,
740
+ MAX_PROVIDER_TOOL_CALL_INPUT_CHARS
741
+ ),
686
742
  call_id: toolCall.id!,
687
743
  ...(zdrEnabled ? { id: functionCallIds?.[toolCall.id!] } : {}),
688
744
  })