@librechat/agents 3.1.53 → 3.1.54

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.
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
- var messages = require('@langchain/core/messages');
4
3
  var outputs = require('@langchain/core/outputs');
4
+ var messages = require('@langchain/core/messages');
5
5
 
6
6
  /**
7
7
  * Utility functions for converting Bedrock Converse responses to LangChain messages.
@@ -127,18 +127,29 @@ function handleConverseStreamContentBlockStart(contentBlockStart) {
127
127
  * Handle a metadata event from Bedrock Converse stream.
128
128
  */
129
129
  function handleConverseStreamMetadata(metadata, extra) {
130
- const inputTokens = metadata.usage?.inputTokens ?? 0;
131
- const outputTokens = metadata.usage?.outputTokens ?? 0;
130
+ const usage = metadata.usage;
131
+ const inputTokens = usage?.inputTokens ?? 0;
132
+ const outputTokens = usage?.outputTokens ?? 0;
133
+ const cacheRead = usage?.cacheReadInputTokens;
134
+ const cacheWrite = usage?.cacheWriteInputTokens;
132
135
  const usage_metadata = {
133
136
  input_tokens: inputTokens,
134
137
  output_tokens: outputTokens,
135
- total_tokens: metadata.usage?.totalTokens ?? inputTokens + outputTokens,
138
+ total_tokens: usage?.totalTokens ?? inputTokens + outputTokens,
136
139
  };
140
+ if (cacheRead != null || cacheWrite != null) {
141
+ usage_metadata.input_token_details = {
142
+ cache_read: cacheRead ?? 0,
143
+ cache_creation: cacheWrite ?? 0,
144
+ };
145
+ }
137
146
  return new outputs.ChatGenerationChunk({
138
147
  text: '',
139
148
  message: new messages.AIMessageChunk({
140
149
  content: '',
141
- usage_metadata: extra.streamUsage ? usage_metadata : undefined,
150
+ usage_metadata: extra.streamUsage
151
+ ? usage_metadata
152
+ : undefined,
142
153
  response_metadata: {
143
154
  // Use the same key as returned from the Converse API
144
155
  metadata,
@@ -1 +1 @@
1
- {"version":3,"file":"message_outputs.cjs","sources":["../../../../../src/llm/bedrock/utils/message_outputs.ts"],"sourcesContent":["/**\n * Utility functions for converting Bedrock Converse responses to LangChain messages.\n * Ported from @langchain/aws common.js\n */\nimport { AIMessage, AIMessageChunk } from '@langchain/core/messages';\nimport { ChatGenerationChunk } from '@langchain/core/outputs';\nimport type {\n BedrockMessage,\n ConverseResponse,\n ContentBlockDeltaEvent,\n ConverseStreamMetadataEvent,\n ContentBlockStartEvent,\n ReasoningContentBlock,\n ReasoningContentBlockDelta,\n MessageContentReasoningBlock,\n MessageContentReasoningBlockReasoningTextPartial,\n MessageContentReasoningBlockRedacted,\n} from '../types';\n\n/**\n * Convert a Bedrock reasoning block delta to a LangChain partial reasoning block.\n */\nexport function bedrockReasoningDeltaToLangchainPartialReasoningBlock(\n reasoningContent: ReasoningContentBlockDelta\n):\n | MessageContentReasoningBlockReasoningTextPartial\n | MessageContentReasoningBlockRedacted {\n const { text, redactedContent, signature } =\n reasoningContent as ReasoningContentBlockDelta & {\n text?: string;\n redactedContent?: Uint8Array;\n signature?: string;\n };\n\n if (typeof text === 'string') {\n return {\n type: 'reasoning_content',\n reasoningText: { text },\n };\n }\n if (signature != null) {\n return {\n type: 'reasoning_content',\n reasoningText: { signature },\n };\n }\n if (redactedContent != null) {\n return {\n type: 'reasoning_content',\n redactedContent: Buffer.from(redactedContent).toString('base64'),\n };\n }\n throw new Error('Invalid reasoning content');\n}\n\n/**\n * Convert a Bedrock reasoning block to a LangChain reasoning block.\n */\nexport function bedrockReasoningBlockToLangchainReasoningBlock(\n reasoningContent: ReasoningContentBlock\n): MessageContentReasoningBlock {\n const { reasoningText, redactedContent } =\n reasoningContent as ReasoningContentBlock & {\n reasoningText?: { text?: string; signature?: string };\n redactedContent?: Uint8Array;\n };\n\n if (reasoningText != null) {\n return {\n type: 'reasoning_content',\n reasoningText: reasoningText,\n };\n }\n if (redactedContent != null) {\n return {\n type: 'reasoning_content',\n redactedContent: Buffer.from(redactedContent).toString('base64'),\n };\n }\n throw new Error('Invalid reasoning content');\n}\n\n/**\n * Convert a Bedrock Converse message to a LangChain message.\n */\nexport function convertConverseMessageToLangChainMessage(\n message: BedrockMessage,\n responseMetadata: Omit<ConverseResponse, 'output'>\n): AIMessage {\n if (message.content == null) {\n throw new Error('No message content found in response.');\n }\n if (message.role !== 'assistant') {\n throw new Error(\n `Unsupported message role received in ChatBedrockConverse response: ${message.role}`\n );\n }\n\n let requestId: string | undefined;\n if (\n '$metadata' in responseMetadata &&\n responseMetadata.$metadata != null &&\n typeof responseMetadata.$metadata === 'object' &&\n 'requestId' in responseMetadata.$metadata\n ) {\n requestId = responseMetadata.$metadata.requestId as string;\n }\n\n let tokenUsage:\n | { input_tokens: number; output_tokens: number; total_tokens: number }\n | undefined;\n if (responseMetadata.usage != null) {\n const input_tokens = responseMetadata.usage.inputTokens ?? 0;\n const output_tokens = responseMetadata.usage.outputTokens ?? 0;\n tokenUsage = {\n input_tokens,\n output_tokens,\n total_tokens:\n responseMetadata.usage.totalTokens ?? input_tokens + output_tokens,\n };\n }\n\n if (\n message.content.length === 1 &&\n 'text' in message.content[0] &&\n typeof message.content[0].text === 'string'\n ) {\n return new AIMessage({\n content: message.content[0].text,\n response_metadata: responseMetadata,\n usage_metadata: tokenUsage,\n id: requestId,\n });\n } else {\n const toolCalls: Array<{\n id?: string;\n name: string;\n args: Record<string, unknown>;\n type: 'tool_call';\n }> = [];\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const content: any[] = [];\n\n message.content.forEach((c) => {\n if (\n 'toolUse' in c &&\n c.toolUse != null &&\n c.toolUse.name != null &&\n c.toolUse.name !== '' &&\n c.toolUse.input != null &&\n typeof c.toolUse.input === 'object'\n ) {\n toolCalls.push({\n id: c.toolUse.toolUseId,\n name: c.toolUse.name,\n args: c.toolUse.input as Record<string, unknown>,\n type: 'tool_call',\n });\n } else if ('text' in c && typeof c.text === 'string') {\n content.push({ type: 'text', text: c.text });\n } else if ('reasoningContent' in c && c.reasoningContent != null) {\n content.push(\n bedrockReasoningBlockToLangchainReasoningBlock(c.reasoningContent)\n );\n } else {\n content.push(c);\n }\n });\n\n return new AIMessage({\n content: content.length ? content : '',\n tool_calls: toolCalls.length ? toolCalls : undefined,\n response_metadata: responseMetadata,\n usage_metadata: tokenUsage,\n id: requestId,\n });\n }\n}\n\n/**\n * Handle a content block delta event from Bedrock Converse stream.\n */\nexport function handleConverseStreamContentBlockDelta(\n contentBlockDelta: ContentBlockDeltaEvent\n): ChatGenerationChunk {\n if (contentBlockDelta.delta == null) {\n throw new Error('No delta found in content block.');\n }\n\n if (typeof contentBlockDelta.delta.text === 'string') {\n return new ChatGenerationChunk({\n text: contentBlockDelta.delta.text,\n message: new AIMessageChunk({\n content: contentBlockDelta.delta.text,\n response_metadata: {\n contentBlockIndex: contentBlockDelta.contentBlockIndex,\n },\n }),\n });\n } else if (contentBlockDelta.delta.toolUse != null) {\n const index = contentBlockDelta.contentBlockIndex;\n return new ChatGenerationChunk({\n text: '',\n message: new AIMessageChunk({\n content: '',\n tool_call_chunks: [\n {\n args: contentBlockDelta.delta.toolUse.input as string,\n index,\n type: 'tool_call_chunk',\n },\n ],\n response_metadata: {\n contentBlockIndex: contentBlockDelta.contentBlockIndex,\n },\n }),\n });\n } else if (contentBlockDelta.delta.reasoningContent != null) {\n const reasoningBlock =\n bedrockReasoningDeltaToLangchainPartialReasoningBlock(\n contentBlockDelta.delta.reasoningContent\n );\n let reasoningText = '';\n if ('reasoningText' in reasoningBlock) {\n reasoningText = reasoningBlock.reasoningText.text ?? '';\n } else if ('redactedContent' in reasoningBlock) {\n reasoningText = reasoningBlock.redactedContent;\n }\n return new ChatGenerationChunk({\n text: '',\n message: new AIMessageChunk({\n content: [reasoningBlock],\n additional_kwargs: {\n // Set reasoning_content for stream handler to detect reasoning mode\n reasoning_content: reasoningText,\n },\n response_metadata: {\n contentBlockIndex: contentBlockDelta.contentBlockIndex,\n },\n }),\n });\n } else {\n throw new Error(\n `Unsupported content block type(s): ${JSON.stringify(contentBlockDelta.delta, null, 2)}`\n );\n }\n}\n\n/**\n * Handle a content block start event from Bedrock Converse stream.\n */\nexport function handleConverseStreamContentBlockStart(\n contentBlockStart: ContentBlockStartEvent\n): ChatGenerationChunk | null {\n const index = contentBlockStart.contentBlockIndex;\n\n if (contentBlockStart.start?.toolUse != null) {\n return new ChatGenerationChunk({\n text: '',\n message: new AIMessageChunk({\n content: '',\n tool_call_chunks: [\n {\n name: contentBlockStart.start.toolUse.name,\n id: contentBlockStart.start.toolUse.toolUseId,\n index,\n type: 'tool_call_chunk',\n },\n ],\n response_metadata: {\n contentBlockIndex: index,\n },\n }),\n });\n }\n\n // Return null for non-tool content block starts (text blocks don't need special handling)\n return null;\n}\n\n/**\n * Handle a metadata event from Bedrock Converse stream.\n */\nexport function handleConverseStreamMetadata(\n metadata: ConverseStreamMetadataEvent,\n extra: { streamUsage: boolean }\n): ChatGenerationChunk {\n const inputTokens = metadata.usage?.inputTokens ?? 0;\n const outputTokens = metadata.usage?.outputTokens ?? 0;\n const usage_metadata = {\n input_tokens: inputTokens,\n output_tokens: outputTokens,\n total_tokens: metadata.usage?.totalTokens ?? inputTokens + outputTokens,\n };\n\n return new ChatGenerationChunk({\n text: '',\n message: new AIMessageChunk({\n content: '',\n usage_metadata: extra.streamUsage ? usage_metadata : undefined,\n response_metadata: {\n // Use the same key as returned from the Converse API\n metadata,\n },\n }),\n });\n}\n"],"names":["ChatGenerationChunk","AIMessageChunk"],"mappings":"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"}
1
+ {"version":3,"file":"message_outputs.cjs","sources":["../../../../../src/llm/bedrock/utils/message_outputs.ts"],"sourcesContent":["/**\n * Utility functions for converting Bedrock Converse responses to LangChain messages.\n * Ported from @langchain/aws common.js\n */\nimport { ChatGenerationChunk } from '@langchain/core/outputs';\nimport { AIMessage, AIMessageChunk } from '@langchain/core/messages';\nimport type { UsageMetadata } from '@langchain/core/messages';\nimport type {\n BedrockMessage,\n ConverseResponse,\n ContentBlockDeltaEvent,\n ConverseStreamMetadataEvent,\n ContentBlockStartEvent,\n ReasoningContentBlock,\n ReasoningContentBlockDelta,\n MessageContentReasoningBlock,\n MessageContentReasoningBlockReasoningTextPartial,\n MessageContentReasoningBlockRedacted,\n} from '../types';\n\n/**\n * Convert a Bedrock reasoning block delta to a LangChain partial reasoning block.\n */\nexport function bedrockReasoningDeltaToLangchainPartialReasoningBlock(\n reasoningContent: ReasoningContentBlockDelta\n):\n | MessageContentReasoningBlockReasoningTextPartial\n | MessageContentReasoningBlockRedacted {\n const { text, redactedContent, signature } =\n reasoningContent as ReasoningContentBlockDelta & {\n text?: string;\n redactedContent?: Uint8Array;\n signature?: string;\n };\n\n if (typeof text === 'string') {\n return {\n type: 'reasoning_content',\n reasoningText: { text },\n };\n }\n if (signature != null) {\n return {\n type: 'reasoning_content',\n reasoningText: { signature },\n };\n }\n if (redactedContent != null) {\n return {\n type: 'reasoning_content',\n redactedContent: Buffer.from(redactedContent).toString('base64'),\n };\n }\n throw new Error('Invalid reasoning content');\n}\n\n/**\n * Convert a Bedrock reasoning block to a LangChain reasoning block.\n */\nexport function bedrockReasoningBlockToLangchainReasoningBlock(\n reasoningContent: ReasoningContentBlock\n): MessageContentReasoningBlock {\n const { reasoningText, redactedContent } =\n reasoningContent as ReasoningContentBlock & {\n reasoningText?: { text?: string; signature?: string };\n redactedContent?: Uint8Array;\n };\n\n if (reasoningText != null) {\n return {\n type: 'reasoning_content',\n reasoningText: reasoningText,\n };\n }\n if (redactedContent != null) {\n return {\n type: 'reasoning_content',\n redactedContent: Buffer.from(redactedContent).toString('base64'),\n };\n }\n throw new Error('Invalid reasoning content');\n}\n\n/**\n * Convert a Bedrock Converse message to a LangChain message.\n */\nexport function convertConverseMessageToLangChainMessage(\n message: BedrockMessage,\n responseMetadata: Omit<ConverseResponse, 'output'>\n): AIMessage {\n if (message.content == null) {\n throw new Error('No message content found in response.');\n }\n if (message.role !== 'assistant') {\n throw new Error(\n `Unsupported message role received in ChatBedrockConverse response: ${message.role}`\n );\n }\n\n let requestId: string | undefined;\n if (\n '$metadata' in responseMetadata &&\n responseMetadata.$metadata != null &&\n typeof responseMetadata.$metadata === 'object' &&\n 'requestId' in responseMetadata.$metadata\n ) {\n requestId = responseMetadata.$metadata.requestId as string;\n }\n\n let tokenUsage:\n | {\n input_tokens: number;\n output_tokens: number;\n total_tokens: number;\n input_token_details?: {\n cache_read: number;\n cache_creation: number;\n };\n }\n | undefined;\n if (responseMetadata.usage != null) {\n const usage = responseMetadata.usage as NonNullable<\n typeof responseMetadata.usage\n > & {\n cacheReadInputTokens?: number;\n cacheWriteInputTokens?: number;\n };\n const input_tokens = usage.inputTokens ?? 0;\n const output_tokens = usage.outputTokens ?? 0;\n const cacheRead = usage.cacheReadInputTokens;\n const cacheWrite = usage.cacheWriteInputTokens;\n tokenUsage = {\n input_tokens,\n output_tokens,\n total_tokens: usage.totalTokens ?? input_tokens + output_tokens,\n };\n if (cacheRead != null || cacheWrite != null) {\n tokenUsage.input_token_details = {\n cache_read: cacheRead ?? 0,\n cache_creation: cacheWrite ?? 0,\n };\n }\n }\n\n if (\n message.content.length === 1 &&\n 'text' in message.content[0] &&\n typeof message.content[0].text === 'string'\n ) {\n return new AIMessage({\n content: message.content[0].text,\n response_metadata: responseMetadata,\n usage_metadata: tokenUsage,\n id: requestId,\n });\n } else {\n const toolCalls: Array<{\n id?: string;\n name: string;\n args: Record<string, unknown>;\n type: 'tool_call';\n }> = [];\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const content: any[] = [];\n\n message.content.forEach((c) => {\n if (\n 'toolUse' in c &&\n c.toolUse != null &&\n c.toolUse.name != null &&\n c.toolUse.name !== '' &&\n c.toolUse.input != null &&\n typeof c.toolUse.input === 'object'\n ) {\n toolCalls.push({\n id: c.toolUse.toolUseId,\n name: c.toolUse.name,\n args: c.toolUse.input as Record<string, unknown>,\n type: 'tool_call',\n });\n } else if ('text' in c && typeof c.text === 'string') {\n content.push({ type: 'text', text: c.text });\n } else if ('reasoningContent' in c && c.reasoningContent != null) {\n content.push(\n bedrockReasoningBlockToLangchainReasoningBlock(c.reasoningContent)\n );\n } else {\n content.push(c);\n }\n });\n\n return new AIMessage({\n content: content.length ? content : '',\n tool_calls: toolCalls.length ? toolCalls : undefined,\n response_metadata: responseMetadata,\n usage_metadata: tokenUsage,\n id: requestId,\n });\n }\n}\n\n/**\n * Handle a content block delta event from Bedrock Converse stream.\n */\nexport function handleConverseStreamContentBlockDelta(\n contentBlockDelta: ContentBlockDeltaEvent\n): ChatGenerationChunk {\n if (contentBlockDelta.delta == null) {\n throw new Error('No delta found in content block.');\n }\n\n if (typeof contentBlockDelta.delta.text === 'string') {\n return new ChatGenerationChunk({\n text: contentBlockDelta.delta.text,\n message: new AIMessageChunk({\n content: contentBlockDelta.delta.text,\n response_metadata: {\n contentBlockIndex: contentBlockDelta.contentBlockIndex,\n },\n }),\n });\n } else if (contentBlockDelta.delta.toolUse != null) {\n const index = contentBlockDelta.contentBlockIndex;\n return new ChatGenerationChunk({\n text: '',\n message: new AIMessageChunk({\n content: '',\n tool_call_chunks: [\n {\n args: contentBlockDelta.delta.toolUse.input as string,\n index,\n type: 'tool_call_chunk',\n },\n ],\n response_metadata: {\n contentBlockIndex: contentBlockDelta.contentBlockIndex,\n },\n }),\n });\n } else if (contentBlockDelta.delta.reasoningContent != null) {\n const reasoningBlock =\n bedrockReasoningDeltaToLangchainPartialReasoningBlock(\n contentBlockDelta.delta.reasoningContent\n );\n let reasoningText = '';\n if ('reasoningText' in reasoningBlock) {\n reasoningText = reasoningBlock.reasoningText.text ?? '';\n } else if ('redactedContent' in reasoningBlock) {\n reasoningText = reasoningBlock.redactedContent;\n }\n return new ChatGenerationChunk({\n text: '',\n message: new AIMessageChunk({\n content: [reasoningBlock],\n additional_kwargs: {\n // Set reasoning_content for stream handler to detect reasoning mode\n reasoning_content: reasoningText,\n },\n response_metadata: {\n contentBlockIndex: contentBlockDelta.contentBlockIndex,\n },\n }),\n });\n } else {\n throw new Error(\n `Unsupported content block type(s): ${JSON.stringify(contentBlockDelta.delta, null, 2)}`\n );\n }\n}\n\n/**\n * Handle a content block start event from Bedrock Converse stream.\n */\nexport function handleConverseStreamContentBlockStart(\n contentBlockStart: ContentBlockStartEvent\n): ChatGenerationChunk | null {\n const index = contentBlockStart.contentBlockIndex;\n\n if (contentBlockStart.start?.toolUse != null) {\n return new ChatGenerationChunk({\n text: '',\n message: new AIMessageChunk({\n content: '',\n tool_call_chunks: [\n {\n name: contentBlockStart.start.toolUse.name,\n id: contentBlockStart.start.toolUse.toolUseId,\n index,\n type: 'tool_call_chunk',\n },\n ],\n response_metadata: {\n contentBlockIndex: index,\n },\n }),\n });\n }\n\n // Return null for non-tool content block starts (text blocks don't need special handling)\n return null;\n}\n\n/**\n * Handle a metadata event from Bedrock Converse stream.\n */\nexport function handleConverseStreamMetadata(\n metadata: ConverseStreamMetadataEvent,\n extra: { streamUsage: boolean }\n): ChatGenerationChunk {\n const usage = metadata.usage as\n | (NonNullable<ConverseStreamMetadataEvent['usage']> & {\n cacheReadInputTokens?: number;\n cacheWriteInputTokens?: number;\n })\n | undefined;\n const inputTokens = usage?.inputTokens ?? 0;\n const outputTokens = usage?.outputTokens ?? 0;\n const cacheRead = usage?.cacheReadInputTokens;\n const cacheWrite = usage?.cacheWriteInputTokens;\n\n const usage_metadata: Record<string, unknown> = {\n input_tokens: inputTokens,\n output_tokens: outputTokens,\n total_tokens: usage?.totalTokens ?? inputTokens + outputTokens,\n };\n\n if (cacheRead != null || cacheWrite != null) {\n usage_metadata.input_token_details = {\n cache_read: cacheRead ?? 0,\n cache_creation: cacheWrite ?? 0,\n };\n }\n\n return new ChatGenerationChunk({\n text: '',\n message: new AIMessageChunk({\n content: '',\n usage_metadata: extra.streamUsage\n ? (usage_metadata as UsageMetadata)\n : undefined,\n response_metadata: {\n // Use the same key as returned from the Converse API\n metadata,\n },\n }),\n });\n}\n"],"names":["ChatGenerationChunk","AIMessageChunk"],"mappings":"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"}
@@ -1,5 +1,5 @@
1
- import { AIMessageChunk } from '@langchain/core/messages';
2
1
  import { ChatGenerationChunk } from '@langchain/core/outputs';
2
+ import { AIMessageChunk } from '@langchain/core/messages';
3
3
 
4
4
  /**
5
5
  * Utility functions for converting Bedrock Converse responses to LangChain messages.
@@ -125,18 +125,29 @@ function handleConverseStreamContentBlockStart(contentBlockStart) {
125
125
  * Handle a metadata event from Bedrock Converse stream.
126
126
  */
127
127
  function handleConverseStreamMetadata(metadata, extra) {
128
- const inputTokens = metadata.usage?.inputTokens ?? 0;
129
- const outputTokens = metadata.usage?.outputTokens ?? 0;
128
+ const usage = metadata.usage;
129
+ const inputTokens = usage?.inputTokens ?? 0;
130
+ const outputTokens = usage?.outputTokens ?? 0;
131
+ const cacheRead = usage?.cacheReadInputTokens;
132
+ const cacheWrite = usage?.cacheWriteInputTokens;
130
133
  const usage_metadata = {
131
134
  input_tokens: inputTokens,
132
135
  output_tokens: outputTokens,
133
- total_tokens: metadata.usage?.totalTokens ?? inputTokens + outputTokens,
136
+ total_tokens: usage?.totalTokens ?? inputTokens + outputTokens,
134
137
  };
138
+ if (cacheRead != null || cacheWrite != null) {
139
+ usage_metadata.input_token_details = {
140
+ cache_read: cacheRead ?? 0,
141
+ cache_creation: cacheWrite ?? 0,
142
+ };
143
+ }
135
144
  return new ChatGenerationChunk({
136
145
  text: '',
137
146
  message: new AIMessageChunk({
138
147
  content: '',
139
- usage_metadata: extra.streamUsage ? usage_metadata : undefined,
148
+ usage_metadata: extra.streamUsage
149
+ ? usage_metadata
150
+ : undefined,
140
151
  response_metadata: {
141
152
  // Use the same key as returned from the Converse API
142
153
  metadata,
@@ -1 +1 @@
1
- {"version":3,"file":"message_outputs.mjs","sources":["../../../../../src/llm/bedrock/utils/message_outputs.ts"],"sourcesContent":["/**\n * Utility functions for converting Bedrock Converse responses to LangChain messages.\n * Ported from @langchain/aws common.js\n */\nimport { AIMessage, AIMessageChunk } from '@langchain/core/messages';\nimport { ChatGenerationChunk } from '@langchain/core/outputs';\nimport type {\n BedrockMessage,\n ConverseResponse,\n ContentBlockDeltaEvent,\n ConverseStreamMetadataEvent,\n ContentBlockStartEvent,\n ReasoningContentBlock,\n ReasoningContentBlockDelta,\n MessageContentReasoningBlock,\n MessageContentReasoningBlockReasoningTextPartial,\n MessageContentReasoningBlockRedacted,\n} from '../types';\n\n/**\n * Convert a Bedrock reasoning block delta to a LangChain partial reasoning block.\n */\nexport function bedrockReasoningDeltaToLangchainPartialReasoningBlock(\n reasoningContent: ReasoningContentBlockDelta\n):\n | MessageContentReasoningBlockReasoningTextPartial\n | MessageContentReasoningBlockRedacted {\n const { text, redactedContent, signature } =\n reasoningContent as ReasoningContentBlockDelta & {\n text?: string;\n redactedContent?: Uint8Array;\n signature?: string;\n };\n\n if (typeof text === 'string') {\n return {\n type: 'reasoning_content',\n reasoningText: { text },\n };\n }\n if (signature != null) {\n return {\n type: 'reasoning_content',\n reasoningText: { signature },\n };\n }\n if (redactedContent != null) {\n return {\n type: 'reasoning_content',\n redactedContent: Buffer.from(redactedContent).toString('base64'),\n };\n }\n throw new Error('Invalid reasoning content');\n}\n\n/**\n * Convert a Bedrock reasoning block to a LangChain reasoning block.\n */\nexport function bedrockReasoningBlockToLangchainReasoningBlock(\n reasoningContent: ReasoningContentBlock\n): MessageContentReasoningBlock {\n const { reasoningText, redactedContent } =\n reasoningContent as ReasoningContentBlock & {\n reasoningText?: { text?: string; signature?: string };\n redactedContent?: Uint8Array;\n };\n\n if (reasoningText != null) {\n return {\n type: 'reasoning_content',\n reasoningText: reasoningText,\n };\n }\n if (redactedContent != null) {\n return {\n type: 'reasoning_content',\n redactedContent: Buffer.from(redactedContent).toString('base64'),\n };\n }\n throw new Error('Invalid reasoning content');\n}\n\n/**\n * Convert a Bedrock Converse message to a LangChain message.\n */\nexport function convertConverseMessageToLangChainMessage(\n message: BedrockMessage,\n responseMetadata: Omit<ConverseResponse, 'output'>\n): AIMessage {\n if (message.content == null) {\n throw new Error('No message content found in response.');\n }\n if (message.role !== 'assistant') {\n throw new Error(\n `Unsupported message role received in ChatBedrockConverse response: ${message.role}`\n );\n }\n\n let requestId: string | undefined;\n if (\n '$metadata' in responseMetadata &&\n responseMetadata.$metadata != null &&\n typeof responseMetadata.$metadata === 'object' &&\n 'requestId' in responseMetadata.$metadata\n ) {\n requestId = responseMetadata.$metadata.requestId as string;\n }\n\n let tokenUsage:\n | { input_tokens: number; output_tokens: number; total_tokens: number }\n | undefined;\n if (responseMetadata.usage != null) {\n const input_tokens = responseMetadata.usage.inputTokens ?? 0;\n const output_tokens = responseMetadata.usage.outputTokens ?? 0;\n tokenUsage = {\n input_tokens,\n output_tokens,\n total_tokens:\n responseMetadata.usage.totalTokens ?? input_tokens + output_tokens,\n };\n }\n\n if (\n message.content.length === 1 &&\n 'text' in message.content[0] &&\n typeof message.content[0].text === 'string'\n ) {\n return new AIMessage({\n content: message.content[0].text,\n response_metadata: responseMetadata,\n usage_metadata: tokenUsage,\n id: requestId,\n });\n } else {\n const toolCalls: Array<{\n id?: string;\n name: string;\n args: Record<string, unknown>;\n type: 'tool_call';\n }> = [];\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const content: any[] = [];\n\n message.content.forEach((c) => {\n if (\n 'toolUse' in c &&\n c.toolUse != null &&\n c.toolUse.name != null &&\n c.toolUse.name !== '' &&\n c.toolUse.input != null &&\n typeof c.toolUse.input === 'object'\n ) {\n toolCalls.push({\n id: c.toolUse.toolUseId,\n name: c.toolUse.name,\n args: c.toolUse.input as Record<string, unknown>,\n type: 'tool_call',\n });\n } else if ('text' in c && typeof c.text === 'string') {\n content.push({ type: 'text', text: c.text });\n } else if ('reasoningContent' in c && c.reasoningContent != null) {\n content.push(\n bedrockReasoningBlockToLangchainReasoningBlock(c.reasoningContent)\n );\n } else {\n content.push(c);\n }\n });\n\n return new AIMessage({\n content: content.length ? content : '',\n tool_calls: toolCalls.length ? toolCalls : undefined,\n response_metadata: responseMetadata,\n usage_metadata: tokenUsage,\n id: requestId,\n });\n }\n}\n\n/**\n * Handle a content block delta event from Bedrock Converse stream.\n */\nexport function handleConverseStreamContentBlockDelta(\n contentBlockDelta: ContentBlockDeltaEvent\n): ChatGenerationChunk {\n if (contentBlockDelta.delta == null) {\n throw new Error('No delta found in content block.');\n }\n\n if (typeof contentBlockDelta.delta.text === 'string') {\n return new ChatGenerationChunk({\n text: contentBlockDelta.delta.text,\n message: new AIMessageChunk({\n content: contentBlockDelta.delta.text,\n response_metadata: {\n contentBlockIndex: contentBlockDelta.contentBlockIndex,\n },\n }),\n });\n } else if (contentBlockDelta.delta.toolUse != null) {\n const index = contentBlockDelta.contentBlockIndex;\n return new ChatGenerationChunk({\n text: '',\n message: new AIMessageChunk({\n content: '',\n tool_call_chunks: [\n {\n args: contentBlockDelta.delta.toolUse.input as string,\n index,\n type: 'tool_call_chunk',\n },\n ],\n response_metadata: {\n contentBlockIndex: contentBlockDelta.contentBlockIndex,\n },\n }),\n });\n } else if (contentBlockDelta.delta.reasoningContent != null) {\n const reasoningBlock =\n bedrockReasoningDeltaToLangchainPartialReasoningBlock(\n contentBlockDelta.delta.reasoningContent\n );\n let reasoningText = '';\n if ('reasoningText' in reasoningBlock) {\n reasoningText = reasoningBlock.reasoningText.text ?? '';\n } else if ('redactedContent' in reasoningBlock) {\n reasoningText = reasoningBlock.redactedContent;\n }\n return new ChatGenerationChunk({\n text: '',\n message: new AIMessageChunk({\n content: [reasoningBlock],\n additional_kwargs: {\n // Set reasoning_content for stream handler to detect reasoning mode\n reasoning_content: reasoningText,\n },\n response_metadata: {\n contentBlockIndex: contentBlockDelta.contentBlockIndex,\n },\n }),\n });\n } else {\n throw new Error(\n `Unsupported content block type(s): ${JSON.stringify(contentBlockDelta.delta, null, 2)}`\n );\n }\n}\n\n/**\n * Handle a content block start event from Bedrock Converse stream.\n */\nexport function handleConverseStreamContentBlockStart(\n contentBlockStart: ContentBlockStartEvent\n): ChatGenerationChunk | null {\n const index = contentBlockStart.contentBlockIndex;\n\n if (contentBlockStart.start?.toolUse != null) {\n return new ChatGenerationChunk({\n text: '',\n message: new AIMessageChunk({\n content: '',\n tool_call_chunks: [\n {\n name: contentBlockStart.start.toolUse.name,\n id: contentBlockStart.start.toolUse.toolUseId,\n index,\n type: 'tool_call_chunk',\n },\n ],\n response_metadata: {\n contentBlockIndex: index,\n },\n }),\n });\n }\n\n // Return null for non-tool content block starts (text blocks don't need special handling)\n return null;\n}\n\n/**\n * Handle a metadata event from Bedrock Converse stream.\n */\nexport function handleConverseStreamMetadata(\n metadata: ConverseStreamMetadataEvent,\n extra: { streamUsage: boolean }\n): ChatGenerationChunk {\n const inputTokens = metadata.usage?.inputTokens ?? 0;\n const outputTokens = metadata.usage?.outputTokens ?? 0;\n const usage_metadata = {\n input_tokens: inputTokens,\n output_tokens: outputTokens,\n total_tokens: metadata.usage?.totalTokens ?? inputTokens + outputTokens,\n };\n\n return new ChatGenerationChunk({\n text: '',\n message: new AIMessageChunk({\n content: '',\n usage_metadata: extra.streamUsage ? usage_metadata : undefined,\n response_metadata: {\n // Use the same key as returned from the Converse API\n metadata,\n },\n }),\n });\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"message_outputs.mjs","sources":["../../../../../src/llm/bedrock/utils/message_outputs.ts"],"sourcesContent":["/**\n * Utility functions for converting Bedrock Converse responses to LangChain messages.\n * Ported from @langchain/aws common.js\n */\nimport { ChatGenerationChunk } from '@langchain/core/outputs';\nimport { AIMessage, AIMessageChunk } from '@langchain/core/messages';\nimport type { UsageMetadata } from '@langchain/core/messages';\nimport type {\n BedrockMessage,\n ConverseResponse,\n ContentBlockDeltaEvent,\n ConverseStreamMetadataEvent,\n ContentBlockStartEvent,\n ReasoningContentBlock,\n ReasoningContentBlockDelta,\n MessageContentReasoningBlock,\n MessageContentReasoningBlockReasoningTextPartial,\n MessageContentReasoningBlockRedacted,\n} from '../types';\n\n/**\n * Convert a Bedrock reasoning block delta to a LangChain partial reasoning block.\n */\nexport function bedrockReasoningDeltaToLangchainPartialReasoningBlock(\n reasoningContent: ReasoningContentBlockDelta\n):\n | MessageContentReasoningBlockReasoningTextPartial\n | MessageContentReasoningBlockRedacted {\n const { text, redactedContent, signature } =\n reasoningContent as ReasoningContentBlockDelta & {\n text?: string;\n redactedContent?: Uint8Array;\n signature?: string;\n };\n\n if (typeof text === 'string') {\n return {\n type: 'reasoning_content',\n reasoningText: { text },\n };\n }\n if (signature != null) {\n return {\n type: 'reasoning_content',\n reasoningText: { signature },\n };\n }\n if (redactedContent != null) {\n return {\n type: 'reasoning_content',\n redactedContent: Buffer.from(redactedContent).toString('base64'),\n };\n }\n throw new Error('Invalid reasoning content');\n}\n\n/**\n * Convert a Bedrock reasoning block to a LangChain reasoning block.\n */\nexport function bedrockReasoningBlockToLangchainReasoningBlock(\n reasoningContent: ReasoningContentBlock\n): MessageContentReasoningBlock {\n const { reasoningText, redactedContent } =\n reasoningContent as ReasoningContentBlock & {\n reasoningText?: { text?: string; signature?: string };\n redactedContent?: Uint8Array;\n };\n\n if (reasoningText != null) {\n return {\n type: 'reasoning_content',\n reasoningText: reasoningText,\n };\n }\n if (redactedContent != null) {\n return {\n type: 'reasoning_content',\n redactedContent: Buffer.from(redactedContent).toString('base64'),\n };\n }\n throw new Error('Invalid reasoning content');\n}\n\n/**\n * Convert a Bedrock Converse message to a LangChain message.\n */\nexport function convertConverseMessageToLangChainMessage(\n message: BedrockMessage,\n responseMetadata: Omit<ConverseResponse, 'output'>\n): AIMessage {\n if (message.content == null) {\n throw new Error('No message content found in response.');\n }\n if (message.role !== 'assistant') {\n throw new Error(\n `Unsupported message role received in ChatBedrockConverse response: ${message.role}`\n );\n }\n\n let requestId: string | undefined;\n if (\n '$metadata' in responseMetadata &&\n responseMetadata.$metadata != null &&\n typeof responseMetadata.$metadata === 'object' &&\n 'requestId' in responseMetadata.$metadata\n ) {\n requestId = responseMetadata.$metadata.requestId as string;\n }\n\n let tokenUsage:\n | {\n input_tokens: number;\n output_tokens: number;\n total_tokens: number;\n input_token_details?: {\n cache_read: number;\n cache_creation: number;\n };\n }\n | undefined;\n if (responseMetadata.usage != null) {\n const usage = responseMetadata.usage as NonNullable<\n typeof responseMetadata.usage\n > & {\n cacheReadInputTokens?: number;\n cacheWriteInputTokens?: number;\n };\n const input_tokens = usage.inputTokens ?? 0;\n const output_tokens = usage.outputTokens ?? 0;\n const cacheRead = usage.cacheReadInputTokens;\n const cacheWrite = usage.cacheWriteInputTokens;\n tokenUsage = {\n input_tokens,\n output_tokens,\n total_tokens: usage.totalTokens ?? input_tokens + output_tokens,\n };\n if (cacheRead != null || cacheWrite != null) {\n tokenUsage.input_token_details = {\n cache_read: cacheRead ?? 0,\n cache_creation: cacheWrite ?? 0,\n };\n }\n }\n\n if (\n message.content.length === 1 &&\n 'text' in message.content[0] &&\n typeof message.content[0].text === 'string'\n ) {\n return new AIMessage({\n content: message.content[0].text,\n response_metadata: responseMetadata,\n usage_metadata: tokenUsage,\n id: requestId,\n });\n } else {\n const toolCalls: Array<{\n id?: string;\n name: string;\n args: Record<string, unknown>;\n type: 'tool_call';\n }> = [];\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const content: any[] = [];\n\n message.content.forEach((c) => {\n if (\n 'toolUse' in c &&\n c.toolUse != null &&\n c.toolUse.name != null &&\n c.toolUse.name !== '' &&\n c.toolUse.input != null &&\n typeof c.toolUse.input === 'object'\n ) {\n toolCalls.push({\n id: c.toolUse.toolUseId,\n name: c.toolUse.name,\n args: c.toolUse.input as Record<string, unknown>,\n type: 'tool_call',\n });\n } else if ('text' in c && typeof c.text === 'string') {\n content.push({ type: 'text', text: c.text });\n } else if ('reasoningContent' in c && c.reasoningContent != null) {\n content.push(\n bedrockReasoningBlockToLangchainReasoningBlock(c.reasoningContent)\n );\n } else {\n content.push(c);\n }\n });\n\n return new AIMessage({\n content: content.length ? content : '',\n tool_calls: toolCalls.length ? toolCalls : undefined,\n response_metadata: responseMetadata,\n usage_metadata: tokenUsage,\n id: requestId,\n });\n }\n}\n\n/**\n * Handle a content block delta event from Bedrock Converse stream.\n */\nexport function handleConverseStreamContentBlockDelta(\n contentBlockDelta: ContentBlockDeltaEvent\n): ChatGenerationChunk {\n if (contentBlockDelta.delta == null) {\n throw new Error('No delta found in content block.');\n }\n\n if (typeof contentBlockDelta.delta.text === 'string') {\n return new ChatGenerationChunk({\n text: contentBlockDelta.delta.text,\n message: new AIMessageChunk({\n content: contentBlockDelta.delta.text,\n response_metadata: {\n contentBlockIndex: contentBlockDelta.contentBlockIndex,\n },\n }),\n });\n } else if (contentBlockDelta.delta.toolUse != null) {\n const index = contentBlockDelta.contentBlockIndex;\n return new ChatGenerationChunk({\n text: '',\n message: new AIMessageChunk({\n content: '',\n tool_call_chunks: [\n {\n args: contentBlockDelta.delta.toolUse.input as string,\n index,\n type: 'tool_call_chunk',\n },\n ],\n response_metadata: {\n contentBlockIndex: contentBlockDelta.contentBlockIndex,\n },\n }),\n });\n } else if (contentBlockDelta.delta.reasoningContent != null) {\n const reasoningBlock =\n bedrockReasoningDeltaToLangchainPartialReasoningBlock(\n contentBlockDelta.delta.reasoningContent\n );\n let reasoningText = '';\n if ('reasoningText' in reasoningBlock) {\n reasoningText = reasoningBlock.reasoningText.text ?? '';\n } else if ('redactedContent' in reasoningBlock) {\n reasoningText = reasoningBlock.redactedContent;\n }\n return new ChatGenerationChunk({\n text: '',\n message: new AIMessageChunk({\n content: [reasoningBlock],\n additional_kwargs: {\n // Set reasoning_content for stream handler to detect reasoning mode\n reasoning_content: reasoningText,\n },\n response_metadata: {\n contentBlockIndex: contentBlockDelta.contentBlockIndex,\n },\n }),\n });\n } else {\n throw new Error(\n `Unsupported content block type(s): ${JSON.stringify(contentBlockDelta.delta, null, 2)}`\n );\n }\n}\n\n/**\n * Handle a content block start event from Bedrock Converse stream.\n */\nexport function handleConverseStreamContentBlockStart(\n contentBlockStart: ContentBlockStartEvent\n): ChatGenerationChunk | null {\n const index = contentBlockStart.contentBlockIndex;\n\n if (contentBlockStart.start?.toolUse != null) {\n return new ChatGenerationChunk({\n text: '',\n message: new AIMessageChunk({\n content: '',\n tool_call_chunks: [\n {\n name: contentBlockStart.start.toolUse.name,\n id: contentBlockStart.start.toolUse.toolUseId,\n index,\n type: 'tool_call_chunk',\n },\n ],\n response_metadata: {\n contentBlockIndex: index,\n },\n }),\n });\n }\n\n // Return null for non-tool content block starts (text blocks don't need special handling)\n return null;\n}\n\n/**\n * Handle a metadata event from Bedrock Converse stream.\n */\nexport function handleConverseStreamMetadata(\n metadata: ConverseStreamMetadataEvent,\n extra: { streamUsage: boolean }\n): ChatGenerationChunk {\n const usage = metadata.usage as\n | (NonNullable<ConverseStreamMetadataEvent['usage']> & {\n cacheReadInputTokens?: number;\n cacheWriteInputTokens?: number;\n })\n | undefined;\n const inputTokens = usage?.inputTokens ?? 0;\n const outputTokens = usage?.outputTokens ?? 0;\n const cacheRead = usage?.cacheReadInputTokens;\n const cacheWrite = usage?.cacheWriteInputTokens;\n\n const usage_metadata: Record<string, unknown> = {\n input_tokens: inputTokens,\n output_tokens: outputTokens,\n total_tokens: usage?.totalTokens ?? inputTokens + outputTokens,\n };\n\n if (cacheRead != null || cacheWrite != null) {\n usage_metadata.input_token_details = {\n cache_read: cacheRead ?? 0,\n cache_creation: cacheWrite ?? 0,\n };\n }\n\n return new ChatGenerationChunk({\n text: '',\n message: new AIMessageChunk({\n content: '',\n usage_metadata: extra.streamUsage\n ? (usage_metadata as UsageMetadata)\n : undefined,\n response_metadata: {\n // Use the same key as returned from the Converse API\n metadata,\n },\n }),\n });\n}\n"],"names":[],"mappings":"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"}
@@ -2,8 +2,8 @@
2
2
  * Utility functions for converting Bedrock Converse responses to LangChain messages.
3
3
  * Ported from @langchain/aws common.js
4
4
  */
5
- import { AIMessage } from '@langchain/core/messages';
6
5
  import { ChatGenerationChunk } from '@langchain/core/outputs';
6
+ import { AIMessage } from '@langchain/core/messages';
7
7
  import type { BedrockMessage, ConverseResponse, ContentBlockDeltaEvent, ConverseStreamMetadataEvent, ContentBlockStartEvent, ReasoningContentBlock, ReasoningContentBlockDelta, MessageContentReasoningBlock, MessageContentReasoningBlockReasoningTextPartial, MessageContentReasoningBlockRedacted } from '../types';
8
8
  /**
9
9
  * Convert a Bedrock reasoning block delta to a LangChain partial reasoning block.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@librechat/agents",
3
- "version": "3.1.53",
3
+ "version": "3.1.54",
4
4
  "main": "./dist/cjs/main.cjs",
5
5
  "module": "./dist/esm/main.mjs",
6
6
  "types": "./dist/types/index.d.ts",
@@ -5,16 +5,24 @@ config();
5
5
  import { expect, test, describe, jest } from '@jest/globals';
6
6
  import {
7
7
  AIMessage,
8
- AIMessageChunk,
8
+ ToolMessage,
9
9
  HumanMessage,
10
10
  SystemMessage,
11
- ToolMessage,
11
+ AIMessageChunk,
12
12
  } from '@langchain/core/messages';
13
13
  import { concat } from '@langchain/core/utils/stream';
14
14
  import { ChatGenerationChunk } from '@langchain/core/outputs';
15
- import { BedrockRuntimeClient } from '@aws-sdk/client-bedrock-runtime';
15
+ import {
16
+ BedrockRuntimeClient,
17
+ ConverseCommand,
18
+ } from '@aws-sdk/client-bedrock-runtime';
19
+ import type { ConverseResponse } from '@aws-sdk/client-bedrock-runtime';
20
+ import {
21
+ convertConverseMessageToLangChainMessage,
22
+ handleConverseStreamMetadata,
23
+ convertToConverseMessages,
24
+ } from './utils';
16
25
  import { CustomChatBedrockConverse, ServiceTierType } from './index';
17
- import { convertToConverseMessages } from './utils';
18
26
 
19
27
  jest.setTimeout(120000);
20
28
 
@@ -429,6 +437,164 @@ describe('CustomChatBedrockConverse', () => {
429
437
  });
430
438
  });
431
439
 
440
+ describe('handleConverseStreamMetadata - cache token extraction', () => {
441
+ test('should extract cacheReadInputTokens and cacheWriteInputTokens into input_token_details', () => {
442
+ const metadata = {
443
+ usage: {
444
+ inputTokens: 13,
445
+ outputTokens: 5,
446
+ totalTokens: 10849,
447
+ cacheReadInputTokens: 10831,
448
+ cacheWriteInputTokens: 0,
449
+ },
450
+ metrics: { latencyMs: 1000 },
451
+ };
452
+
453
+ const chunk = handleConverseStreamMetadata(metadata, {
454
+ streamUsage: true,
455
+ });
456
+ const msg = chunk.message as AIMessageChunk;
457
+
458
+ expect(msg.usage_metadata).toEqual({
459
+ input_tokens: 13,
460
+ output_tokens: 5,
461
+ total_tokens: 10849,
462
+ input_token_details: {
463
+ cache_read: 10831,
464
+ cache_creation: 0,
465
+ },
466
+ });
467
+ });
468
+
469
+ test('should not include input_token_details when no cache tokens present', () => {
470
+ const metadata = {
471
+ usage: {
472
+ inputTokens: 100,
473
+ outputTokens: 50,
474
+ totalTokens: 150,
475
+ },
476
+ metrics: { latencyMs: 500 },
477
+ };
478
+
479
+ const chunk = handleConverseStreamMetadata(metadata, {
480
+ streamUsage: true,
481
+ });
482
+ const msg = chunk.message as AIMessageChunk;
483
+
484
+ expect(msg.usage_metadata).toEqual({
485
+ input_tokens: 100,
486
+ output_tokens: 50,
487
+ total_tokens: 150,
488
+ });
489
+ expect(msg.usage_metadata?.input_token_details).toBeUndefined();
490
+ });
491
+
492
+ test('should include input_token_details when only cacheWriteInputTokens is present', () => {
493
+ const metadata = {
494
+ usage: {
495
+ inputTokens: 50,
496
+ outputTokens: 10,
497
+ totalTokens: 10060,
498
+ cacheWriteInputTokens: 10000,
499
+ },
500
+ metrics: { latencyMs: 800 },
501
+ };
502
+
503
+ const chunk = handleConverseStreamMetadata(metadata, {
504
+ streamUsage: true,
505
+ });
506
+ const msg = chunk.message as AIMessageChunk;
507
+
508
+ expect(msg.usage_metadata?.input_token_details).toEqual({
509
+ cache_read: 0,
510
+ cache_creation: 10000,
511
+ });
512
+ });
513
+
514
+ test('should return undefined usage_metadata when streamUsage is false', () => {
515
+ const metadata = {
516
+ usage: {
517
+ inputTokens: 13,
518
+ outputTokens: 5,
519
+ totalTokens: 10849,
520
+ cacheReadInputTokens: 10831,
521
+ cacheWriteInputTokens: 0,
522
+ },
523
+ metrics: { latencyMs: 1000 },
524
+ };
525
+
526
+ const chunk = handleConverseStreamMetadata(metadata, {
527
+ streamUsage: false,
528
+ });
529
+ const msg = chunk.message as AIMessageChunk;
530
+
531
+ expect(msg.usage_metadata).toBeUndefined();
532
+ });
533
+ });
534
+
535
+ describe('convertConverseMessageToLangChainMessage - cache token extraction', () => {
536
+ const makeResponseMetadata = (
537
+ usage: Record<string, number>
538
+ ): Omit<ConverseResponse, 'output'> =>
539
+ ({
540
+ usage,
541
+ stopReason: 'end_turn',
542
+ metrics: undefined,
543
+ $metadata: { requestId: 'test-id' },
544
+ }) as unknown as Omit<ConverseResponse, 'output'>;
545
+
546
+ test('should extract cache tokens in non-streaming response', () => {
547
+ const message = {
548
+ role: 'assistant' as const,
549
+ content: [{ text: 'Hello!' }],
550
+ };
551
+
552
+ const result = convertConverseMessageToLangChainMessage(
553
+ message,
554
+ makeResponseMetadata({
555
+ inputTokens: 20,
556
+ outputTokens: 5,
557
+ totalTokens: 10856,
558
+ cacheReadInputTokens: 10831,
559
+ cacheWriteInputTokens: 0,
560
+ })
561
+ );
562
+
563
+ expect(result.usage_metadata).toEqual({
564
+ input_tokens: 20,
565
+ output_tokens: 5,
566
+ total_tokens: 10856,
567
+ input_token_details: {
568
+ cache_read: 10831,
569
+ cache_creation: 0,
570
+ },
571
+ });
572
+ });
573
+
574
+ test('should not include input_token_details when no cache tokens in non-streaming response', () => {
575
+ const message = {
576
+ role: 'assistant' as const,
577
+ content: [{ text: 'Hello!' }],
578
+ };
579
+
580
+ const result = convertConverseMessageToLangChainMessage(
581
+ message,
582
+ makeResponseMetadata({
583
+ inputTokens: 100,
584
+ outputTokens: 50,
585
+ totalTokens: 150,
586
+ })
587
+ );
588
+
589
+ expect(result.usage_metadata).toEqual({
590
+ input_tokens: 100,
591
+ output_tokens: 50,
592
+ total_tokens: 150,
593
+ });
594
+ expect(result.usage_metadata?.input_token_details).toBeUndefined();
595
+ });
596
+ });
597
+
432
598
  describe('convertToConverseMessages', () => {
433
599
  test('should convert basic messages', () => {
434
600
  const { converseMessages, converseSystem } = convertToConverseMessages([
@@ -647,4 +813,67 @@ describe.skip('Integration tests', () => {
647
813
  expect(reasoningBlocks.length).toBeGreaterThanOrEqual(0);
648
814
  }
649
815
  });
816
+
817
+ test('cache tokens should populate input_token_details', async () => {
818
+ const client = new BedrockRuntimeClient({
819
+ region: integrationArgs.region,
820
+ credentials: integrationArgs.credentials,
821
+ });
822
+
823
+ // Large system prompt (>1024 tokens) to meet Bedrock's minimum cache threshold
824
+ const largeSystemPrompt = [
825
+ 'You are an expert assistant.',
826
+ ...Array(200).fill(
827
+ 'This is padding content to exceed the minimum token threshold for Bedrock prompt caching. '
828
+ ),
829
+ 'When answering, be brief and direct.',
830
+ ].join(' ');
831
+
832
+ const systemBlocks = [
833
+ { text: largeSystemPrompt },
834
+ { cachePoint: { type: 'default' as const } },
835
+ ];
836
+
837
+ const converseArgs = {
838
+ modelId: 'us.anthropic.claude-sonnet-4-5-20250929-v1:0',
839
+ system: systemBlocks,
840
+ inferenceConfig: { maxTokens: 50 },
841
+ };
842
+
843
+ // Call 1: populate the cache (may be a write or read if already warm)
844
+ await client.send(
845
+ new ConverseCommand({
846
+ ...converseArgs,
847
+ messages: [{ role: 'user', content: [{ text: 'Say hello.' }] }],
848
+ })
849
+ );
850
+
851
+ // Call 2: should read from cache — this is the one we assert on
852
+ const response = await client.send(
853
+ new ConverseCommand({
854
+ ...converseArgs,
855
+ messages: [
856
+ { role: 'user', content: [{ text: 'Say hello.' }] },
857
+ { role: 'assistant', content: [{ text: 'Hello!' }] },
858
+ { role: 'user', content: [{ text: 'Say goodbye.' }] },
859
+ ],
860
+ })
861
+ );
862
+
863
+ // Feed raw response through convertConverseMessageToLangChainMessage
864
+ const result = convertConverseMessageToLangChainMessage(
865
+ response.output!.message!,
866
+ response
867
+ );
868
+
869
+ expect(result.usage_metadata).toBeDefined();
870
+ expect(result.usage_metadata!.input_tokens).toBeGreaterThan(0);
871
+ expect(result.usage_metadata!.output_tokens).toBeGreaterThan(0);
872
+
873
+ // Cache should have been populated by call 1, so call 2 should show cache reads
874
+ expect(result.usage_metadata!.input_token_details).toBeDefined();
875
+ expect(
876
+ result.usage_metadata!.input_token_details!.cache_read
877
+ ).toBeGreaterThan(0);
878
+ });
650
879
  });
@@ -2,8 +2,9 @@
2
2
  * Utility functions for converting Bedrock Converse responses to LangChain messages.
3
3
  * Ported from @langchain/aws common.js
4
4
  */
5
- import { AIMessage, AIMessageChunk } from '@langchain/core/messages';
6
5
  import { ChatGenerationChunk } from '@langchain/core/outputs';
6
+ import { AIMessage, AIMessageChunk } from '@langchain/core/messages';
7
+ import type { UsageMetadata } from '@langchain/core/messages';
7
8
  import type {
8
9
  BedrockMessage,
9
10
  ConverseResponse,
@@ -107,17 +108,38 @@ export function convertConverseMessageToLangChainMessage(
107
108
  }
108
109
 
109
110
  let tokenUsage:
110
- | { input_tokens: number; output_tokens: number; total_tokens: number }
111
+ | {
112
+ input_tokens: number;
113
+ output_tokens: number;
114
+ total_tokens: number;
115
+ input_token_details?: {
116
+ cache_read: number;
117
+ cache_creation: number;
118
+ };
119
+ }
111
120
  | undefined;
112
121
  if (responseMetadata.usage != null) {
113
- const input_tokens = responseMetadata.usage.inputTokens ?? 0;
114
- const output_tokens = responseMetadata.usage.outputTokens ?? 0;
122
+ const usage = responseMetadata.usage as NonNullable<
123
+ typeof responseMetadata.usage
124
+ > & {
125
+ cacheReadInputTokens?: number;
126
+ cacheWriteInputTokens?: number;
127
+ };
128
+ const input_tokens = usage.inputTokens ?? 0;
129
+ const output_tokens = usage.outputTokens ?? 0;
130
+ const cacheRead = usage.cacheReadInputTokens;
131
+ const cacheWrite = usage.cacheWriteInputTokens;
115
132
  tokenUsage = {
116
133
  input_tokens,
117
134
  output_tokens,
118
- total_tokens:
119
- responseMetadata.usage.totalTokens ?? input_tokens + output_tokens,
135
+ total_tokens: usage.totalTokens ?? input_tokens + output_tokens,
120
136
  };
137
+ if (cacheRead != null || cacheWrite != null) {
138
+ tokenUsage.input_token_details = {
139
+ cache_read: cacheRead ?? 0,
140
+ cache_creation: cacheWrite ?? 0,
141
+ };
142
+ }
121
143
  }
122
144
 
123
145
  if (
@@ -285,19 +307,37 @@ export function handleConverseStreamMetadata(
285
307
  metadata: ConverseStreamMetadataEvent,
286
308
  extra: { streamUsage: boolean }
287
309
  ): ChatGenerationChunk {
288
- const inputTokens = metadata.usage?.inputTokens ?? 0;
289
- const outputTokens = metadata.usage?.outputTokens ?? 0;
290
- const usage_metadata = {
310
+ const usage = metadata.usage as
311
+ | (NonNullable<ConverseStreamMetadataEvent['usage']> & {
312
+ cacheReadInputTokens?: number;
313
+ cacheWriteInputTokens?: number;
314
+ })
315
+ | undefined;
316
+ const inputTokens = usage?.inputTokens ?? 0;
317
+ const outputTokens = usage?.outputTokens ?? 0;
318
+ const cacheRead = usage?.cacheReadInputTokens;
319
+ const cacheWrite = usage?.cacheWriteInputTokens;
320
+
321
+ const usage_metadata: Record<string, unknown> = {
291
322
  input_tokens: inputTokens,
292
323
  output_tokens: outputTokens,
293
- total_tokens: metadata.usage?.totalTokens ?? inputTokens + outputTokens,
324
+ total_tokens: usage?.totalTokens ?? inputTokens + outputTokens,
294
325
  };
295
326
 
327
+ if (cacheRead != null || cacheWrite != null) {
328
+ usage_metadata.input_token_details = {
329
+ cache_read: cacheRead ?? 0,
330
+ cache_creation: cacheWrite ?? 0,
331
+ };
332
+ }
333
+
296
334
  return new ChatGenerationChunk({
297
335
  text: '',
298
336
  message: new AIMessageChunk({
299
337
  content: '',
300
- usage_metadata: extra.streamUsage ? usage_metadata : undefined,
338
+ usage_metadata: extra.streamUsage
339
+ ? (usage_metadata as UsageMetadata)
340
+ : undefined,
301
341
  response_metadata: {
302
342
  // Use the same key as returned from the Converse API
303
343
  metadata,
@@ -0,0 +1,250 @@
1
+ /**
2
+ * Debug script to investigate cache token omission in Bedrock responses.
3
+ *
4
+ * This script:
5
+ * 1. Makes a streaming call to Bedrock and logs the raw metadata event
6
+ * 2. Shows exactly what fields the AWS SDK returns in usage (including cache tokens)
7
+ * 3. Shows what our handleConverseStreamMetadata produces vs what it should produce
8
+ * 4. Makes a multi-turn call to trigger caching and verify cache tokens appear
9
+ */
10
+ import { config } from 'dotenv';
11
+ config();
12
+ import { HumanMessage } from '@langchain/core/messages';
13
+ import type { AIMessageChunk } from '@langchain/core/messages';
14
+ import { concat } from '@langchain/core/utils/stream';
15
+ import {
16
+ ConverseStreamCommand,
17
+ BedrockRuntimeClient,
18
+ } from '@aws-sdk/client-bedrock-runtime';
19
+ import { CustomChatBedrockConverse } from '@/llm/bedrock';
20
+
21
+ const region = process.env.BEDROCK_AWS_REGION ?? 'us-east-1';
22
+ const credentials = {
23
+ accessKeyId: process.env.BEDROCK_AWS_ACCESS_KEY_ID!,
24
+ secretAccessKey: process.env.BEDROCK_AWS_SECRET_ACCESS_KEY!,
25
+ };
26
+
27
+ const MODEL_ID = 'us.anthropic.claude-sonnet-4-5-20250929-v1:0';
28
+
29
+ // A long system prompt to increase likelihood of cache usage
30
+ // Bedrock requires minimum 1024 tokens for prompt caching to activate
31
+ const SYSTEM_PROMPT = `You are an expert assistant. Here is a large context block to help trigger cache behavior:
32
+
33
+ ${Array(200).fill('This is padding content to make the prompt large enough to trigger Bedrock prompt caching. The minimum requirement for Anthropic models on Bedrock is 1024 tokens in the cached prefix. We need to ensure this prompt is well above that threshold. ').join('')}
34
+
35
+ When answering, be brief and direct.`;
36
+
37
+ async function rawSdkCall(): Promise<void> {
38
+ console.log('='.repeat(60));
39
+ console.log('TEST 1: Raw AWS SDK call - inspect metadata.usage directly');
40
+ console.log('='.repeat(60));
41
+
42
+ const client = new BedrockRuntimeClient({ region, credentials });
43
+
44
+ // First call - should create cache
45
+ // Use cachePoint block to explicitly enable prompt caching
46
+ console.log('\n--- Call 1 (cache write expected) ---');
47
+ const command1 = new ConverseStreamCommand({
48
+ modelId: MODEL_ID,
49
+ system: [{ text: SYSTEM_PROMPT }, { cachePoint: { type: 'default' } }],
50
+ messages: [{ role: 'user', content: [{ text: 'What is 2+2?' }] }],
51
+ inferenceConfig: { maxTokens: 100 },
52
+ });
53
+
54
+ const response1 = await client.send(command1);
55
+ if (response1.stream) {
56
+ for await (const event of response1.stream) {
57
+ if (event.metadata != null) {
58
+ console.log('\nRAW metadata event (Call 1):');
59
+ console.dir(event.metadata, { depth: null });
60
+ console.log('\nRAW metadata.usage:');
61
+ console.dir(event.metadata.usage, { depth: null });
62
+ console.log('\nSpecific cache fields:');
63
+ console.log(
64
+ ' cacheReadInputTokens:',
65
+ (event.metadata.usage as Record<string, unknown>)
66
+ ?.cacheReadInputTokens
67
+ );
68
+ console.log(
69
+ ' cacheWriteInputTokens:',
70
+ (event.metadata.usage as Record<string, unknown>)
71
+ ?.cacheWriteInputTokens
72
+ );
73
+ }
74
+ }
75
+ }
76
+
77
+ // Second call - should read from cache
78
+ console.log('\n--- Call 2 (cache read expected) ---');
79
+ const command2 = new ConverseStreamCommand({
80
+ modelId: MODEL_ID,
81
+ system: [{ text: SYSTEM_PROMPT }, { cachePoint: { type: 'default' } }],
82
+ messages: [
83
+ { role: 'user', content: [{ text: 'What is 2+2?' }] },
84
+ { role: 'assistant', content: [{ text: '4' }] },
85
+ { role: 'user', content: [{ text: 'And what is 3+3?' }] },
86
+ ],
87
+ inferenceConfig: { maxTokens: 100 },
88
+ });
89
+
90
+ const response2 = await client.send(command2);
91
+ if (response2.stream) {
92
+ for await (const event of response2.stream) {
93
+ if (event.metadata != null) {
94
+ console.log('\nRAW metadata event (Call 2):');
95
+ console.dir(event.metadata, { depth: null });
96
+ console.log('\nRAW metadata.usage:');
97
+ console.dir(event.metadata.usage, { depth: null });
98
+ console.log('\nSpecific cache fields:');
99
+ console.log(
100
+ ' cacheReadInputTokens:',
101
+ (event.metadata.usage as Record<string, unknown>)
102
+ ?.cacheReadInputTokens
103
+ );
104
+ console.log(
105
+ ' cacheWriteInputTokens:',
106
+ (event.metadata.usage as Record<string, unknown>)
107
+ ?.cacheWriteInputTokens
108
+ );
109
+ }
110
+ }
111
+ }
112
+ }
113
+
114
+ async function wrapperStreamCallNoCachePoint(): Promise<void> {
115
+ console.log('\n' + '='.repeat(60));
116
+ console.log(
117
+ 'TEST 2: CustomChatBedrockConverse stream (NO cachePoint) - check usage_metadata'
118
+ );
119
+ console.log('='.repeat(60));
120
+ console.log('(Without cachePoint, Bedrock does NOT return cache tokens)');
121
+
122
+ const model = new CustomChatBedrockConverse({
123
+ model: MODEL_ID,
124
+ region,
125
+ credentials,
126
+ maxTokens: 100,
127
+ streaming: true,
128
+ streamUsage: true,
129
+ });
130
+
131
+ console.log('\n--- Wrapper Call (no cachePoint) ---');
132
+ const messages1 = [new HumanMessage(SYSTEM_PROMPT + '\n\nWhat is 2+2?')];
133
+ let finalChunk1: AIMessageChunk | undefined;
134
+
135
+ for await (const chunk of await model.stream(messages1)) {
136
+ finalChunk1 = finalChunk1 ? concat(finalChunk1, chunk) : chunk;
137
+ }
138
+
139
+ console.log(
140
+ '\nFinal usage_metadata:',
141
+ JSON.stringify(finalChunk1!.usage_metadata)
142
+ );
143
+ console.log('(No cache tokens expected since no cachePoint block was sent)');
144
+ }
145
+
146
+ async function wrapperStreamCallWithCachePoint(): Promise<void> {
147
+ console.log('\n' + '='.repeat(60));
148
+ console.log(
149
+ 'TEST 3: Raw SDK with cachePoint -> verify handleConverseStreamMetadata extracts cache tokens'
150
+ );
151
+ console.log('='.repeat(60));
152
+
153
+ // We use the raw SDK with cachePoint to trigger caching, then verify
154
+ // that our handleConverseStreamMetadata function properly extracts cache fields
155
+ const { handleConverseStreamMetadata } = await import(
156
+ '@/llm/bedrock/utils/message_outputs'
157
+ );
158
+
159
+ const client = new BedrockRuntimeClient({ region, credentials });
160
+
161
+ // Call 1 - establish cache
162
+ console.log('\n--- Call 1 (cache write) ---');
163
+ const command1 = new ConverseStreamCommand({
164
+ modelId: MODEL_ID,
165
+ system: [{ text: SYSTEM_PROMPT }, { cachePoint: { type: 'default' } }],
166
+ messages: [{ role: 'user', content: [{ text: 'What is 2+2?' }] }],
167
+ inferenceConfig: { maxTokens: 100 },
168
+ });
169
+
170
+ const response1 = await client.send(command1);
171
+ if (response1.stream) {
172
+ for await (const event of response1.stream) {
173
+ if (event.metadata != null) {
174
+ console.log('Raw usage:', JSON.stringify(event.metadata.usage));
175
+
176
+ // Test our handler
177
+ const chunk = handleConverseStreamMetadata(event.metadata, {
178
+ streamUsage: true,
179
+ });
180
+ console.log(
181
+ 'handleConverseStreamMetadata output usage_metadata:',
182
+ JSON.stringify(chunk.message.usage_metadata)
183
+ );
184
+
185
+ const hasDetails =
186
+ chunk.message.usage_metadata?.input_token_details != null;
187
+ console.log(
188
+ `Has input_token_details: ${hasDetails}`,
189
+ hasDetails
190
+ ? JSON.stringify(chunk.message.usage_metadata!.input_token_details)
191
+ : '(MISSING - BUG!)'
192
+ );
193
+ }
194
+ }
195
+ }
196
+
197
+ // Call 2 - read from cache
198
+ console.log('\n--- Call 2 (cache read) ---');
199
+ const command2 = new ConverseStreamCommand({
200
+ modelId: MODEL_ID,
201
+ system: [{ text: SYSTEM_PROMPT }, { cachePoint: { type: 'default' } }],
202
+ messages: [
203
+ { role: 'user', content: [{ text: 'What is 2+2?' }] },
204
+ { role: 'assistant', content: [{ text: '4' }] },
205
+ { role: 'user', content: [{ text: 'What is 3+3?' }] },
206
+ ],
207
+ inferenceConfig: { maxTokens: 100 },
208
+ });
209
+
210
+ const response2 = await client.send(command2);
211
+ if (response2.stream) {
212
+ for await (const event of response2.stream) {
213
+ if (event.metadata != null) {
214
+ console.log('Raw usage:', JSON.stringify(event.metadata.usage));
215
+
216
+ const chunk = handleConverseStreamMetadata(event.metadata, {
217
+ streamUsage: true,
218
+ });
219
+ console.log(
220
+ 'handleConverseStreamMetadata output usage_metadata:',
221
+ JSON.stringify(chunk.message.usage_metadata)
222
+ );
223
+
224
+ const hasDetails =
225
+ chunk.message.usage_metadata?.input_token_details != null;
226
+ console.log(
227
+ `Has input_token_details: ${hasDetails}`,
228
+ hasDetails
229
+ ? JSON.stringify(chunk.message.usage_metadata!.input_token_details)
230
+ : '(MISSING - BUG!)'
231
+ );
232
+ }
233
+ }
234
+ }
235
+ }
236
+
237
+ async function main(): Promise<void> {
238
+ console.log('Bedrock Cache Token Debug Script');
239
+ console.log(`Model: ${MODEL_ID}`);
240
+ console.log(`Region: ${region}\n`);
241
+
242
+ await rawSdkCall();
243
+ await wrapperStreamCallNoCachePoint();
244
+ await wrapperStreamCallWithCachePoint();
245
+ }
246
+
247
+ main().catch((err) => {
248
+ console.error('Fatal error:', err);
249
+ process.exit(1);
250
+ });