@librechat/agents 3.3.11 → 3.3.12
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.
- package/dist/cjs/graphs/Graph.cjs +246 -25
- package/dist/cjs/graphs/Graph.cjs.map +1 -1
- package/dist/cjs/llm/bedrock/index.cjs +13 -2
- package/dist/cjs/llm/bedrock/index.cjs.map +1 -1
- package/dist/cjs/llm/invoke.cjs +146 -36
- package/dist/cjs/llm/invoke.cjs.map +1 -1
- package/dist/cjs/llm/openai/index.cjs +2 -2
- package/dist/cjs/llm/openai/index.cjs.map +1 -1
- package/dist/cjs/llm/streamLimits.cjs +723 -0
- package/dist/cjs/llm/streamLimits.cjs.map +1 -0
- package/dist/cjs/main.cjs +8 -0
- package/dist/cjs/run.cjs +6 -2
- package/dist/cjs/run.cjs.map +1 -1
- package/dist/cjs/session/AgentSession.cjs +4 -1
- package/dist/cjs/session/AgentSession.cjs.map +1 -1
- package/dist/cjs/stream.cjs +103 -12
- package/dist/cjs/stream.cjs.map +1 -1
- package/dist/cjs/summarization/node.cjs +157 -37
- package/dist/cjs/summarization/node.cjs.map +1 -1
- package/dist/cjs/tools/BashExecutor.cjs +3 -2
- package/dist/cjs/tools/BashExecutor.cjs.map +1 -1
- package/dist/cjs/tools/CodeExecutor.cjs +4 -3
- package/dist/cjs/tools/CodeExecutor.cjs.map +1 -1
- package/dist/cjs/tools/ProgrammaticToolCalling.cjs +5 -3
- package/dist/cjs/tools/ProgrammaticToolCalling.cjs.map +1 -1
- package/dist/cjs/tools/ToolNode.cjs +76 -3
- package/dist/cjs/tools/ToolNode.cjs.map +1 -1
- package/dist/cjs/tools/ToolSearch.cjs +3 -2
- package/dist/cjs/tools/ToolSearch.cjs.map +1 -1
- package/dist/cjs/tools/search/crw-scraper.cjs +7 -1
- package/dist/cjs/tools/search/crw-scraper.cjs.map +1 -1
- package/dist/cjs/tools/search/crw-search.cjs +3 -1
- package/dist/cjs/tools/search/crw-search.cjs.map +1 -1
- package/dist/cjs/tools/search/firecrawl.cjs +7 -1
- package/dist/cjs/tools/search/firecrawl.cjs.map +1 -1
- package/dist/cjs/tools/search/keenable-scraper.cjs +7 -1
- package/dist/cjs/tools/search/keenable-scraper.cjs.map +1 -1
- package/dist/cjs/tools/search/keenable-search.cjs +3 -1
- package/dist/cjs/tools/search/keenable-search.cjs.map +1 -1
- package/dist/cjs/tools/search/rerankers.cjs +26 -8
- package/dist/cjs/tools/search/rerankers.cjs.map +1 -1
- package/dist/cjs/tools/search/search.cjs +30 -10
- package/dist/cjs/tools/search/search.cjs.map +1 -1
- package/dist/cjs/tools/search/serper-scraper.cjs +7 -1
- package/dist/cjs/tools/search/serper-scraper.cjs.map +1 -1
- package/dist/cjs/tools/search/tavily-scraper.cjs +7 -1
- package/dist/cjs/tools/search/tavily-scraper.cjs.map +1 -1
- package/dist/cjs/tools/search/tavily-search.cjs +3 -1
- package/dist/cjs/tools/search/tavily-search.cjs.map +1 -1
- package/dist/cjs/tools/search/tool.cjs +16 -2
- package/dist/cjs/tools/search/tool.cjs.map +1 -1
- package/dist/cjs/tools/subagent/SubagentExecutor.cjs +54 -3
- package/dist/cjs/tools/subagent/SubagentExecutor.cjs.map +1 -1
- package/dist/cjs/utils/index.cjs +1 -0
- package/dist/cjs/utils/misc.cjs +12 -0
- package/dist/cjs/utils/misc.cjs.map +1 -1
- package/dist/cjs/utils/proxy.cjs +63 -0
- package/dist/cjs/utils/proxy.cjs.map +1 -0
- package/dist/esm/graphs/Graph.mjs +245 -24
- package/dist/esm/graphs/Graph.mjs.map +1 -1
- package/dist/esm/llm/bedrock/index.mjs +13 -2
- package/dist/esm/llm/bedrock/index.mjs.map +1 -1
- package/dist/esm/llm/invoke.mjs +146 -36
- package/dist/esm/llm/invoke.mjs.map +1 -1
- package/dist/esm/llm/openai/index.mjs +2 -2
- package/dist/esm/llm/openai/index.mjs.map +1 -1
- package/dist/esm/llm/streamLimits.mjs +704 -0
- package/dist/esm/llm/streamLimits.mjs.map +1 -0
- package/dist/esm/main.mjs +4 -2
- package/dist/esm/run.mjs +6 -2
- package/dist/esm/run.mjs.map +1 -1
- package/dist/esm/session/AgentSession.mjs +4 -1
- package/dist/esm/session/AgentSession.mjs.map +1 -1
- package/dist/esm/stream.mjs +103 -12
- package/dist/esm/stream.mjs.map +1 -1
- package/dist/esm/summarization/node.mjs +157 -37
- package/dist/esm/summarization/node.mjs.map +1 -1
- package/dist/esm/tools/BashExecutor.mjs +3 -2
- package/dist/esm/tools/BashExecutor.mjs.map +1 -1
- package/dist/esm/tools/CodeExecutor.mjs +4 -3
- package/dist/esm/tools/CodeExecutor.mjs.map +1 -1
- package/dist/esm/tools/ProgrammaticToolCalling.mjs +5 -3
- package/dist/esm/tools/ProgrammaticToolCalling.mjs.map +1 -1
- package/dist/esm/tools/ToolNode.mjs +76 -3
- package/dist/esm/tools/ToolNode.mjs.map +1 -1
- package/dist/esm/tools/ToolSearch.mjs +3 -2
- package/dist/esm/tools/ToolSearch.mjs.map +1 -1
- package/dist/esm/tools/search/crw-scraper.mjs +7 -1
- package/dist/esm/tools/search/crw-scraper.mjs.map +1 -1
- package/dist/esm/tools/search/crw-search.mjs +3 -1
- package/dist/esm/tools/search/crw-search.mjs.map +1 -1
- package/dist/esm/tools/search/firecrawl.mjs +7 -1
- package/dist/esm/tools/search/firecrawl.mjs.map +1 -1
- package/dist/esm/tools/search/keenable-scraper.mjs +7 -1
- package/dist/esm/tools/search/keenable-scraper.mjs.map +1 -1
- package/dist/esm/tools/search/keenable-search.mjs +3 -1
- package/dist/esm/tools/search/keenable-search.mjs.map +1 -1
- package/dist/esm/tools/search/rerankers.mjs +26 -8
- package/dist/esm/tools/search/rerankers.mjs.map +1 -1
- package/dist/esm/tools/search/search.mjs +30 -10
- package/dist/esm/tools/search/search.mjs.map +1 -1
- package/dist/esm/tools/search/serper-scraper.mjs +7 -1
- package/dist/esm/tools/search/serper-scraper.mjs.map +1 -1
- package/dist/esm/tools/search/tavily-scraper.mjs +7 -1
- package/dist/esm/tools/search/tavily-scraper.mjs.map +1 -1
- package/dist/esm/tools/search/tavily-search.mjs +3 -1
- package/dist/esm/tools/search/tavily-search.mjs.map +1 -1
- package/dist/esm/tools/search/tool.mjs +16 -2
- package/dist/esm/tools/search/tool.mjs.map +1 -1
- package/dist/esm/tools/subagent/SubagentExecutor.mjs +54 -3
- package/dist/esm/tools/subagent/SubagentExecutor.mjs.map +1 -1
- package/dist/esm/utils/index.mjs +1 -0
- package/dist/esm/utils/misc.mjs +12 -1
- package/dist/esm/utils/misc.mjs.map +1 -1
- package/dist/esm/utils/proxy.mjs +62 -0
- package/dist/esm/utils/proxy.mjs.map +1 -0
- package/dist/types/graphs/Graph.d.ts +53 -1
- package/dist/types/index.d.ts +2 -0
- package/dist/types/llm/invoke.d.ts +25 -4
- package/dist/types/llm/openai/index.d.ts +3 -0
- package/dist/types/llm/streamLimits.d.ts +314 -0
- package/dist/types/run.d.ts +1 -0
- package/dist/types/summarization/node.d.ts +27 -2
- package/dist/types/tools/BashExecutor.d.ts +2 -2
- package/dist/types/tools/CodeExecutor.d.ts +3 -3
- package/dist/types/tools/ToolNode.d.ts +11 -1
- package/dist/types/tools/search/crw-scraper.d.ts +2 -0
- package/dist/types/tools/search/firecrawl.d.ts +2 -0
- package/dist/types/tools/search/keenable-scraper.d.ts +2 -0
- package/dist/types/tools/search/rerankers.d.ts +9 -5
- package/dist/types/tools/search/serper-scraper.d.ts +2 -0
- package/dist/types/tools/search/tavily-scraper.d.ts +2 -0
- package/dist/types/tools/search/types.d.ts +28 -33
- package/dist/types/tools/subagent/SubagentExecutor.d.ts +44 -0
- package/dist/types/types/graph.d.ts +7 -1
- package/dist/types/types/run.d.ts +46 -1
- package/dist/types/types/tools.d.ts +21 -0
- package/dist/types/utils/index.d.ts +1 -0
- package/dist/types/utils/misc.d.ts +7 -0
- package/dist/types/utils/proxy.d.ts +31 -0
- package/package.json +2 -1
- package/src/__tests__/stream.eagerArgsDivergence.test.ts +158 -0
- package/src/__tests__/stream.eagerEventExecution.test.ts +1 -0
- package/src/__tests__/stream.streamLimits.test.ts +1982 -0
- package/src/graphs/Graph.ts +302 -27
- package/src/graphs/__tests__/Graph.breakerLifecycle.test.ts +234 -0
- package/src/index.ts +11 -0
- package/src/llm/bedrock/index.ts +22 -2
- package/src/llm/invoke.streamLimits.test.ts +142 -0
- package/src/llm/invoke.test.ts +89 -1
- package/src/llm/invoke.ts +197 -20
- package/src/llm/openai/cacheWriteTokens.test.ts +112 -0
- package/src/llm/openai/index.ts +14 -6
- package/src/llm/streamLimits.test.ts +450 -0
- package/src/llm/streamLimits.ts +1158 -0
- package/src/run.ts +4 -0
- package/src/session/AgentSession.ts +5 -0
- package/src/stream.ts +141 -6
- package/src/summarization/__tests__/node.test.ts +269 -0
- package/src/summarization/chunkHandler.test.ts +196 -0
- package/src/summarization/node.ts +203 -6
- package/src/tools/BashExecutor.ts +4 -3
- package/src/tools/CodeExecutor.ts +5 -4
- package/src/tools/ProgrammaticToolCalling.ts +7 -5
- package/src/tools/ToolNode.ts +109 -6
- package/src/tools/ToolSearch.ts +4 -3
- package/src/tools/__tests__/BashExecutor.test.ts +2 -2
- package/src/tools/__tests__/ProgrammaticToolCalling.test.ts +2 -4
- package/src/tools/__tests__/SubagentExecutor.test.ts +126 -0
- package/src/tools/__tests__/ToolNode.breakerSignal.test.ts +389 -0
- package/src/tools/__tests__/ToolNode.streamLimits.test.ts +69 -0
- package/src/tools/search/crw-scraper.ts +6 -0
- package/src/tools/search/crw-search.ts +6 -1
- package/src/tools/search/firecrawl.ts +6 -0
- package/src/tools/search/http-agent.test.ts +133 -0
- package/src/tools/search/keenable-scraper.ts +6 -0
- package/src/tools/search/keenable-search.ts +6 -1
- package/src/tools/search/rerankers.ts +36 -10
- package/src/tools/search/search.ts +29 -11
- package/src/tools/search/serper-scraper.ts +6 -0
- package/src/tools/search/tavily-scraper.ts +6 -0
- package/src/tools/search/tavily-search.ts +2 -0
- package/src/tools/search/tool.ts +16 -0
- package/src/tools/search/types.ts +31 -33
- package/src/tools/subagent/SubagentExecutor.ts +96 -3
- package/src/types/graph.ts +7 -0
- package/src/types/run.ts +49 -1
- package/src/types/tools.ts +21 -0
- package/src/utils/index.ts +1 -0
- package/src/utils/misc.ts +19 -0
- package/src/utils/proxy.test.ts +176 -0
- package/src/utils/proxy.ts +93 -0
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { resolveBedrockPromptCacheTtl, supportsBedrockToolCache } from "../../messages/cache.mjs";
|
|
2
|
+
import { linkStreamLimitCanonical } from "../streamLimits.mjs";
|
|
2
3
|
import { convertToConverseMessages } from "./utils/message_inputs.mjs";
|
|
3
4
|
import { createConverseToolUseStopChunk, handleConverseStreamContentBlockDelta, handleConverseStreamContentBlockStart, handleConverseStreamMetadata } from "./utils/message_outputs.mjs";
|
|
4
5
|
import "./utils/index.mjs";
|
|
@@ -300,8 +301,16 @@ var CustomChatBedrockConverse = class extends ChatBedrockConverse {
|
|
|
300
301
|
} }
|
|
301
302
|
};
|
|
302
303
|
const deltaChunk = handleConverseStreamContentBlockDelta(splitDelta);
|
|
304
|
+
const enrichedChunk = this.enrichChunk(deltaChunk, seenBlockIndices);
|
|
305
|
+
if (enrichedChunk !== deltaChunk)
|
|
306
|
+
/** The callback copy is the same emission as the enriched yield;
|
|
307
|
+
* without the link, stream-limit accounting charges both message
|
|
308
|
+
* objects and Bedrock tool arguments falsely trip near half the
|
|
309
|
+
* cap. Linked on the messages, which are what the accounting
|
|
310
|
+
* observes. */
|
|
311
|
+
linkStreamLimitCanonical(deltaChunk.message, enrichedChunk.message);
|
|
303
312
|
await enqueueChunk({
|
|
304
|
-
chunk:
|
|
313
|
+
chunk: enrichedChunk,
|
|
305
314
|
callbackChunk: deltaChunk,
|
|
306
315
|
callbackToken: deltaChunk.text,
|
|
307
316
|
smooth,
|
|
@@ -324,8 +333,10 @@ var CustomChatBedrockConverse = class extends ChatBedrockConverse {
|
|
|
324
333
|
seenBlockIndices.add(idx);
|
|
325
334
|
if (event.contentBlockStart.start?.toolUse != null) toolUseBlockIndices.add(idx);
|
|
326
335
|
}
|
|
336
|
+
const enrichedStart = this.enrichChunk(startChunk, seenBlockIndices);
|
|
337
|
+
if (enrichedStart !== startChunk) linkStreamLimitCanonical(startChunk.message, enrichedStart.message);
|
|
327
338
|
await enqueueChunk({
|
|
328
|
-
chunk:
|
|
339
|
+
chunk: enrichedStart,
|
|
329
340
|
callbackChunk: startChunk,
|
|
330
341
|
callbackToken: startChunk.text
|
|
331
342
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../../../../src/llm/bedrock/index.ts"],"sourcesContent":["/**\n * Optimized ChatBedrockConverse wrapper that fixes content block merging for\n * streaming responses and adds support for latest @langchain/aws features:\n *\n * - Application Inference Profiles (PR #9129)\n * - Service Tiers (Priority/Standard/Flex) (PR #9785) - requires AWS SDK 3.966.0+\n *\n * Bedrock's `@langchain/aws` library does not include an `index` property on content\n * blocks (unlike Anthropic/OpenAI), which causes LangChain's `_mergeLists` to append\n * each streaming chunk as a separate array entry instead of merging by index.\n *\n * This wrapper takes full ownership of the stream by directly interfacing with the\n * AWS SDK client (`this.client`) and using custom handlers from `./utils/` that\n * include `contentBlockIndex` in response_metadata for every delta type. It then\n * promotes `contentBlockIndex` to an `index` property on each content block\n * (mirroring Anthropic's pattern) and strips it from metadata to avoid\n * `_mergeDicts` conflicts.\n *\n * When multiple content block types are present (e.g. reasoning + text), text deltas\n * are promoted from strings to array form with `index` so they merge correctly once\n * the accumulated content is already an array.\n */\n\nimport { ChatBedrockConverse } from '@langchain/aws';\nimport { AIMessageChunk } from '@langchain/core/messages';\nimport { ChatGenerationChunk, ChatResult } from '@langchain/core/outputs';\nimport {\n ConverseStreamCommand,\n type ConverseStreamOutput,\n type GuardrailConfiguration,\n type GuardrailStreamConfiguration,\n} from '@aws-sdk/client-bedrock-runtime';\nimport type { CallbackManagerForLLMRun } from '@langchain/core/callbacks/manager';\nimport type { BaseMessage, ResponseMetadata } from '@langchain/core/messages';\nimport type { ChatBedrockConverseInput } from '@langchain/aws';\nimport type { ContentBlockDeltaEvent } from './types';\nimport {\n convertToConverseMessages,\n createConverseToolUseStopChunk,\n handleConverseStreamContentBlockStart,\n handleConverseStreamContentBlockDelta,\n handleConverseStreamMetadata,\n} from './utils';\nimport {\n resolveBedrockPromptCacheTtl,\n supportsBedrockToolCache,\n type PromptCacheTtl,\n} from '@/messages/cache';\nimport { applyCachePointsToConversePayload } from './cachePoints';\nimport { insertBedrockToolCachePoint } from './toolCache';\n\n/**\n * Service tier type for Bedrock invocations.\n * Requires AWS SDK >= 3.966.0 to actually work.\n * @see https://docs.aws.amazon.com/bedrock/latest/userguide/service-tiers-inference.html\n */\nexport type ServiceTierType = 'priority' | 'default' | 'flex' | 'reserved';\n\nexport type CustomGuardrailConfiguration = GuardrailConfiguration &\n Pick<GuardrailStreamConfiguration, 'streamProcessingMode'>;\n\nconst MAX_STREAM_QUEUE_CHUNKS = 256;\nconst MAX_STREAM_QUEUE_TEXT_CHARS = 8192;\nconst STREAM_CHUNK_MIN_SIZE = 4;\nconst STREAM_BOUNDARIES = new Set([' ', '.', ',', '!', '?', ';', ':']);\n\ntype QueuedGenerationChunk = {\n chunk: ChatGenerationChunk;\n callbackChunk?: ChatGenerationChunk;\n callbackToken: string;\n smooth: boolean;\n textLength: number;\n};\n\nfunction findStreamChunkBoundary(text: string, minSize: number): number {\n if (minSize >= text.length) {\n return text.length;\n }\n\n for (let position = minSize; position < text.length; position++) {\n if (STREAM_BOUNDARIES.has(text[position])) {\n return position + 1;\n }\n }\n\n return text.length;\n}\n\nfunction splitStreamToken(text: string): string[] {\n const chunks: string[] = [];\n let currentIndex = 0;\n\n while (currentIndex < text.length) {\n const remainingText = text.slice(currentIndex);\n const chunkSize = findStreamChunkBoundary(\n remainingText,\n STREAM_CHUNK_MIN_SIZE\n );\n chunks.push(text.slice(currentIndex, currentIndex + chunkSize));\n currentIndex += chunkSize;\n }\n\n return chunks;\n}\n\n/**\n * Resolves the text a delta contributes to the smoothing cadence, preferring a\n * text delta over a reasoning delta and ignoring non-string payloads.\n */\nfunction resolveVisibleText(text?: string, reasoningText?: string): string {\n if (typeof text === 'string') {\n return text;\n }\n\n if (typeof reasoningText === 'string') {\n return reasoningText;\n }\n\n return '';\n}\n\nfunction getCadencedStreamDelay({\n targetDelay,\n lastVisibleContentAt,\n now,\n}: {\n targetDelay: number;\n lastVisibleContentAt?: number;\n now: number;\n}): number {\n if (targetDelay <= 0 || lastVisibleContentAt == null) {\n return 0;\n }\n return Math.max(0, targetDelay - (now - lastVisibleContentAt));\n}\n\nasync function waitForStreamDelay(\n delay: number,\n signal?: AbortSignal\n): Promise<void> {\n if (delay <= 0 || isSignalAborted(signal)) {\n return;\n }\n await new Promise<void>((resolve) => {\n const timeoutRef: { current?: ReturnType<typeof setTimeout> } = {};\n const onAbort = (): void => {\n if (timeoutRef.current) {\n clearTimeout(timeoutRef.current);\n }\n signal?.removeEventListener('abort', onAbort);\n resolve();\n };\n timeoutRef.current = setTimeout(() => {\n signal?.removeEventListener('abort', onAbort);\n resolve();\n }, delay);\n signal?.addEventListener('abort', onAbort, { once: true });\n if (isSignalAborted(signal)) {\n onAbort();\n }\n });\n}\n\nfunction isSignalAborted(signal?: AbortSignal): boolean {\n return signal?.aborted === true;\n}\n\n/**\n * Extended input interface with additional features:\n * - applicationInferenceProfile: Use an inference profile ARN instead of model ID\n * - serviceTier: Specify service tier (Priority, Standard, Flex, Reserved)\n */\nexport interface CustomChatBedrockConverseInput\n extends ChatBedrockConverseInput {\n /**\n * Enables Bedrock prompt cache checkpoints for message and tool prefixes.\n */\n promptCache?: boolean;\n\n /**\n * Prompt-cache checkpoint TTL. Defaults to `'1h'` (extended cache) when\n * `promptCache` is enabled; set `'5m'` for the legacy 5-minute behavior.\n * Bedrock models that don't support the 1-hour TTL downgrade to 5m\n * server-side (verified on Sonnet/Opus 4.6), so the default is safe to leave\n * on; use `'5m'` for any model that rejects it.\n */\n promptCacheTtl?: PromptCacheTtl;\n\n /**\n * Minimum delay in milliseconds between visible streamed content deltas.\n */\n _lc_stream_delay?: number;\n\n /**\n * Guardrail configuration for Converse and ConverseStream invocations.\n * `streamProcessingMode` is only used by ConverseStream.\n */\n guardrailConfig?: CustomGuardrailConfiguration;\n\n /**\n * Application Inference Profile ARN to use for the model.\n * For example, \"arn:aws:bedrock:eu-west-1:123456789102:application-inference-profile/fm16bt65tzgx\"\n * When provided, this ARN will be used for the actual inference calls instead of the model ID.\n * Must still provide `model` as normal modelId to benefit from all the metadata.\n * @see https://docs.aws.amazon.com/bedrock/latest/userguide/inference-profiles-create.html\n */\n applicationInferenceProfile?: string;\n\n /**\n * Service tier for model invocation.\n * Specifies the processing tier type used for serving the request.\n * Supported values are 'priority', 'default', 'flex', and 'reserved'.\n *\n * - 'priority': Prioritized processing for lower latency\n * - 'default': Standard processing tier\n * - 'flex': Flexible processing tier with lower cost\n * - 'reserved': Reserved capacity for consistent performance\n *\n * If not provided, AWS uses the default tier.\n * Note: Requires AWS SDK >= 3.966.0 to work.\n * @see https://docs.aws.amazon.com/bedrock/latest/userguide/service-tiers-inference.html\n */\n serviceTier?: ServiceTierType;\n}\n\n/**\n * Extended call options with serviceTier override support.\n */\nexport interface CustomChatBedrockConverseCallOptions {\n serviceTier?: ServiceTierType;\n guardrailConfig?: CustomGuardrailConfiguration;\n}\n\nexport class CustomChatBedrockConverse extends ChatBedrockConverse {\n _lc_stream_delay: number;\n\n /**\n * Whether to insert Bedrock prompt cache checkpoints when available.\n */\n promptCache?: boolean;\n\n /**\n * Prompt-cache checkpoint TTL (`'5m'` legacy or `'1h'` extended cache).\n */\n promptCacheTtl?: PromptCacheTtl;\n\n /**\n * Application Inference Profile ARN to use instead of model ID.\n */\n applicationInferenceProfile?: string;\n\n /**\n * Service tier for model invocation.\n */\n serviceTier?: ServiceTierType;\n\n /**\n * The configured model id, captured at construction so it survives the\n * temporary `this.model` swap to an application-inference-profile ARN during\n * generation. Used to gate the Bedrock tool cache point to Claude models\n * (see {@link supportsBedrockToolCache}).\n */\n private readonly cacheModelId: string;\n\n constructor(fields?: CustomChatBedrockConverseInput) {\n super(fields);\n this.promptCache = fields?.promptCache;\n this.promptCacheTtl = fields?.promptCacheTtl;\n this._lc_stream_delay = Math.max(0, fields?._lc_stream_delay ?? 0);\n this.applicationInferenceProfile = fields?.applicationInferenceProfile;\n this.serviceTier = fields?.serviceTier;\n // `super(fields)` initializes `this.model` to LangChain's default Claude\n // model when `fields.model` is omitted, so fall back to it rather than ''\n // (which would treat the default Claude model as tool-cache-unsupported).\n this.cacheModelId = fields?.model ?? this.model;\n }\n\n static lc_name(): string {\n return 'LibreChatBedrockConverse';\n }\n\n /**\n * Get the model ID to use for API calls.\n * Returns applicationInferenceProfile if set, otherwise returns this.model.\n */\n protected getModelId(): string {\n return this.applicationInferenceProfile ?? this.model;\n }\n\n /**\n * Override invocationParams to add serviceTier support.\n */\n override invocationParams(\n options?: this['ParsedCallOptions'] & CustomChatBedrockConverseCallOptions\n ): ReturnType<ChatBedrockConverse['invocationParams']> & {\n serviceTier?: { type: ServiceTierType };\n } {\n const baseParams = super.invocationParams(options);\n const toolConfig =\n this.promptCache === true && supportsBedrockToolCache(this.cacheModelId)\n ? insertBedrockToolCachePoint(\n baseParams.toolConfig,\n true,\n resolveBedrockPromptCacheTtl(this.promptCacheTtl, this.cacheModelId)\n )\n : baseParams.toolConfig;\n\n /** Service tier from options or fall back to class-level setting */\n const serviceTierType = options?.serviceTier ?? this.serviceTier;\n\n return {\n ...baseParams,\n toolConfig,\n serviceTier: serviceTierType ? { type: serviceTierType } : undefined,\n };\n }\n\n /**\n * Override _generateNonStreaming to use applicationInferenceProfile as modelId.\n * Uses the same model-swapping pattern as streaming for consistency.\n */\n override async _generateNonStreaming(\n messages: BaseMessage[],\n options: this['ParsedCallOptions'] & CustomChatBedrockConverseCallOptions,\n runManager?: CallbackManagerForLLMRun\n ): Promise<ChatResult> {\n const originalModel = this.model;\n if (\n this.applicationInferenceProfile != null &&\n this.applicationInferenceProfile !== ''\n ) {\n this.model = this.applicationInferenceProfile;\n }\n\n try {\n return await super._generateNonStreaming(messages, options, runManager);\n } finally {\n this.model = originalModel;\n }\n }\n\n /**\n * Own the stream end-to-end so we have direct access to every\n * `contentBlockDelta.contentBlockIndex` from the AWS SDK.\n *\n * This replaces the parent's implementation which strips contentBlockIndex\n * from text and reasoning deltas, making it impossible to merge correctly.\n */\n override async *_streamResponseChunks(\n messages: BaseMessage[],\n options: this['ParsedCallOptions'] & CustomChatBedrockConverseCallOptions,\n runManager?: CallbackManagerForLLMRun\n ): AsyncGenerator<ChatGenerationChunk> {\n const { converseMessages, converseSystem } =\n convertToConverseMessages(messages);\n const params = this.invocationParams(options);\n\n let { streamUsage } = this;\n if ((options as Record<string, unknown>).streamUsage !== undefined) {\n streamUsage = (options as Record<string, unknown>).streamUsage as boolean;\n }\n\n const modelId = this.getModelId();\n\n applyCachePointsToConversePayload({\n cacheControl: options.cache_control,\n system: converseSystem,\n messages: converseMessages,\n params,\n modelId,\n });\n\n const command = new ConverseStreamCommand({\n modelId,\n messages: converseMessages,\n system: converseSystem,\n ...(params as Record<string, unknown>),\n });\n\n const streamAbortController = new AbortController();\n const abortStream = (): void => streamAbortController.abort();\n options.signal?.addEventListener('abort', abortStream, { once: true });\n if (isSignalAborted(options.signal)) {\n abortStream();\n }\n\n try {\n const response = await this.client.send(command, {\n abortSignal: streamAbortController.signal,\n });\n\n const stream: AsyncIterable<ConverseStreamOutput> | undefined =\n response.stream;\n if (!stream) {\n return;\n }\n\n const seenBlockIndices = new Set<number>();\n const toolUseBlockIndices = new Set<number>();\n const queuedChunks: QueuedGenerationChunk[] = [];\n const producerState: { done: boolean; error?: unknown } = { done: false };\n let queuedChunkIndex = 0;\n let bufferedTextLength = 0;\n let consumerClosed = false;\n let notifyConsumer: (() => void) | undefined;\n let notifyProducer: (() => void) | undefined;\n let hasEmittedVisibleContent = false;\n let lastVisibleContentAt: number | undefined;\n\n /**\n * Guardrails can reject an already-streamed toolUse block at\n * `messageStop` (`guardrail_intervened`), after `contentBlockStop` has\n * passed. Only emit eager-execution seals when no guardrails are\n * configured, so a later intervention can't race an eagerly started tool.\n */\n const sealToolUseOnStop =\n options.guardrailConfig == null && this.guardrailConfig == null;\n\n const notifyConsumerForChunk = (): void => {\n notifyConsumer?.();\n notifyConsumer = undefined;\n };\n\n const notifyProducerForSpace = (): void => {\n notifyProducer?.();\n notifyProducer = undefined;\n };\n\n const hasQueuedChunks = (): boolean =>\n queuedChunkIndex < queuedChunks.length;\n\n const getQueuedChunkCount = (): number =>\n queuedChunks.length - queuedChunkIndex;\n\n const isQueueAtCapacity = (): boolean =>\n getQueuedChunkCount() >= MAX_STREAM_QUEUE_CHUNKS ||\n bufferedTextLength >= MAX_STREAM_QUEUE_TEXT_CHARS;\n\n const waitForNextChunk = async (): Promise<void> => {\n if (\n hasQueuedChunks() ||\n producerState.done ||\n producerState.error != null\n ) {\n return;\n }\n await new Promise<void>((resolve) => {\n notifyConsumer = resolve;\n });\n };\n\n const waitForQueueSpace = async (): Promise<void> => {\n while (\n isQueueAtCapacity() &&\n !consumerClosed &&\n !isSignalAborted(options.signal)\n ) {\n await new Promise<void>((resolve) => {\n const signal = options.signal;\n const onAbort = (): void => {\n signal?.removeEventListener('abort', onAbort);\n resolve();\n };\n const onSpace = (): void => {\n signal?.removeEventListener('abort', onAbort);\n resolve();\n };\n notifyProducer = onSpace;\n signal?.addEventListener('abort', onAbort, { once: true });\n if (isSignalAborted(signal)) {\n onAbort();\n }\n });\n }\n };\n\n const dequeue = (): QueuedGenerationChunk | undefined => {\n if (!hasQueuedChunks()) {\n return undefined;\n }\n const queuedChunk = queuedChunks[queuedChunkIndex];\n queuedChunkIndex++;\n if (\n queuedChunkIndex > 128 &&\n queuedChunkIndex * 2 >= queuedChunks.length\n ) {\n queuedChunks.splice(0, queuedChunkIndex);\n queuedChunkIndex = 0;\n }\n return queuedChunk;\n };\n\n const enqueue = async (\n queuedChunk: QueuedGenerationChunk\n ): Promise<void> => {\n await waitForQueueSpace();\n if (consumerClosed || isSignalAborted(options.signal)) {\n abortStream();\n throw new Error('AbortError: User aborted the request.');\n }\n queuedChunks.push(queuedChunk);\n if (queuedChunk.smooth) {\n bufferedTextLength += queuedChunk.textLength;\n }\n notifyConsumerForChunk();\n };\n\n const enqueueChunk = async ({\n chunk,\n callbackChunk,\n callbackToken = '',\n smooth = false,\n textLength = 0,\n }: {\n chunk: ChatGenerationChunk;\n callbackChunk?: ChatGenerationChunk;\n callbackToken?: string;\n smooth?: boolean;\n textLength?: number;\n }): Promise<void> => {\n await enqueue({\n chunk,\n callbackChunk,\n callbackToken,\n smooth,\n textLength: smooth ? textLength : 0,\n });\n };\n\n const enqueueDelta = async (\n contentBlockDelta: ContentBlockDeltaEvent\n ): Promise<void> => {\n const delta = contentBlockDelta.delta;\n if (delta == null) {\n throw new Error('No delta found in content block.');\n }\n\n const idx = contentBlockDelta.contentBlockIndex;\n if (idx != null) {\n seenBlockIndices.add(idx);\n }\n\n const text = delta.text;\n const reasoningContent = delta.reasoningContent;\n const reasoningText = reasoningContent?.text;\n const visibleText = resolveVisibleText(text, reasoningText);\n const smooth = this._lc_stream_delay > 0 && visibleText !== '';\n const tokenChunks = smooth\n ? splitStreamToken(visibleText)\n : [visibleText];\n\n for (const token of tokenChunks) {\n let splitDelta = contentBlockDelta;\n if (typeof text === 'string') {\n splitDelta = {\n ...contentBlockDelta,\n delta: { text: token },\n };\n } else if (\n typeof reasoningText === 'string' &&\n reasoningContent != null\n ) {\n splitDelta = {\n ...contentBlockDelta,\n delta: {\n reasoningContent: {\n ...reasoningContent,\n text: token,\n },\n },\n };\n }\n\n const deltaChunk = handleConverseStreamContentBlockDelta(splitDelta);\n await enqueueChunk({\n chunk: this.enrichChunk(deltaChunk, seenBlockIndices),\n callbackChunk: deltaChunk,\n callbackToken: deltaChunk.text,\n smooth,\n textLength: token.length,\n });\n }\n };\n\n const producer = (async (): Promise<void> => {\n try {\n for await (const event of stream) {\n if (isSignalAborted(options.signal)) {\n abortStream();\n throw new Error('AbortError: User aborted the request.');\n }\n\n if (event.contentBlockStart != null) {\n const startChunk = handleConverseStreamContentBlockStart(\n event.contentBlockStart\n );\n if (startChunk != null) {\n const idx = event.contentBlockStart.contentBlockIndex;\n if (idx != null) {\n seenBlockIndices.add(idx);\n if (event.contentBlockStart.start?.toolUse != null) {\n toolUseBlockIndices.add(idx);\n }\n }\n await enqueueChunk({\n chunk: this.enrichChunk(startChunk, seenBlockIndices),\n callbackChunk: startChunk,\n callbackToken: startChunk.text,\n });\n }\n } else if (event.contentBlockDelta != null) {\n await enqueueDelta(event.contentBlockDelta);\n } else if (event.metadata != null) {\n await enqueueChunk({\n chunk: handleConverseStreamMetadata(event.metadata, {\n streamUsage,\n }),\n });\n } else if (event.contentBlockStop != null) {\n const stopIdx = event.contentBlockStop.contentBlockIndex;\n if (stopIdx != null) {\n seenBlockIndices.add(stopIdx);\n if (sealToolUseOnStop && toolUseBlockIndices.has(stopIdx)) {\n const sealChunk = createConverseToolUseStopChunk(stopIdx);\n await enqueueChunk({\n chunk: sealChunk,\n callbackChunk: sealChunk,\n callbackToken: sealChunk.text,\n });\n }\n }\n } else {\n await enqueueChunk({\n chunk: new ChatGenerationChunk({\n text: '',\n message: new AIMessageChunk({\n content: '',\n response_metadata: { ...event } as ResponseMetadata,\n }),\n }),\n });\n }\n }\n } catch (error) {\n producerState.error = error;\n } finally {\n producerState.done = true;\n notifyConsumerForChunk();\n }\n })();\n\n try {\n let keepStreaming = true;\n while (keepStreaming) {\n if (isSignalAborted(options.signal)) {\n abortStream();\n throw new Error('AbortError: User aborted the request.');\n }\n\n await waitForNextChunk();\n const queuedChunk = dequeue();\n\n if (!queuedChunk) {\n if (producerState.error != null) {\n throw producerState.error;\n }\n if (producerState.done) {\n keepStreaming = false;\n }\n continue;\n }\n\n if (queuedChunk.smooth) {\n bufferedTextLength = Math.max(\n 0,\n bufferedTextLength - queuedChunk.textLength\n );\n notifyProducerForSpace();\n await waitForStreamDelay(\n getCadencedStreamDelay({\n targetDelay: hasEmittedVisibleContent\n ? this._lc_stream_delay\n : 0,\n lastVisibleContentAt,\n now: Date.now(),\n }),\n options.signal\n );\n if (isSignalAborted(options.signal)) {\n abortStream();\n throw new Error('AbortError: User aborted the request.');\n }\n hasEmittedVisibleContent = true;\n lastVisibleContentAt = Date.now();\n } else {\n notifyProducerForSpace();\n }\n\n yield queuedChunk.chunk;\n\n if (queuedChunk.callbackChunk != null) {\n await runManager?.handleLLMNewToken(\n queuedChunk.callbackToken,\n undefined,\n undefined,\n undefined,\n undefined,\n { chunk: queuedChunk.callbackChunk }\n );\n }\n }\n } finally {\n consumerClosed = true;\n if (!producerState.done) {\n abortStream();\n notifyProducerForSpace();\n }\n await producer;\n }\n } finally {\n options.signal?.removeEventListener('abort', abortStream);\n if (!streamAbortController.signal.aborted) {\n streamAbortController.abort();\n }\n }\n }\n\n /**\n * Inject `index` on content blocks for proper merge behaviour, then strip\n * `contentBlockIndex` from response_metadata to prevent `_mergeDicts` conflicts.\n *\n * Text string content is promoted to array form only when the stream contains\n * multiple content block indices (e.g. reasoning at index 0, text at index 1),\n * ensuring text merges correctly with the already-array accumulated content.\n */\n private enrichChunk(\n chunk: ChatGenerationChunk,\n seenBlockIndices: Set<number>\n ): ChatGenerationChunk {\n const message = chunk.message;\n if (!(message instanceof AIMessageChunk)) {\n return chunk;\n }\n\n const metadata = message.response_metadata as Record<string, unknown>;\n const blockIndex = this.extractContentBlockIndex(metadata);\n const hasMetadataIndex = blockIndex != null;\n\n let content: AIMessageChunk['content'] = message.content;\n let contentModified = false;\n\n if (Array.isArray(content) && blockIndex != null) {\n content = content.map((block) =>\n typeof block === 'object' && !('index' in block)\n ? { ...block, index: blockIndex }\n : block\n );\n contentModified = true;\n } else if (\n typeof content === 'string' &&\n content !== '' &&\n blockIndex != null &&\n seenBlockIndices.size > 1\n ) {\n content = [{ type: 'text', text: content, index: blockIndex }];\n contentModified = true;\n }\n\n if (!contentModified && !hasMetadataIndex) {\n return chunk;\n }\n\n const cleanedMetadata = hasMetadataIndex\n ? (this.removeContentBlockIndex(metadata) as Record<string, unknown>)\n : metadata;\n\n return new ChatGenerationChunk({\n text: chunk.text,\n message: new AIMessageChunk({\n ...message,\n content,\n response_metadata: cleanedMetadata,\n }),\n generationInfo: chunk.generationInfo,\n });\n }\n\n /**\n * Extract `contentBlockIndex` from the top level of response_metadata.\n * Our custom handlers always place it at the top level.\n */\n private extractContentBlockIndex(\n metadata: Record<string, unknown>\n ): number | undefined {\n if (\n 'contentBlockIndex' in metadata &&\n typeof metadata.contentBlockIndex === 'number'\n ) {\n return metadata.contentBlockIndex;\n }\n return undefined;\n }\n\n private removeContentBlockIndex(obj: unknown): unknown {\n if (obj === null || obj === undefined) {\n return obj;\n }\n\n if (Array.isArray(obj)) {\n return obj.map((item) => this.removeContentBlockIndex(item));\n }\n\n if (typeof obj === 'object') {\n const cleaned: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(obj)) {\n if (key !== 'contentBlockIndex') {\n cleaned[key] = this.removeContentBlockIndex(value);\n }\n }\n return cleaned;\n }\n\n return obj;\n }\n}\n\nexport type { ChatBedrockConverseInput };\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../../../../src/llm/bedrock/index.ts"],"sourcesContent":["/**\n * Optimized ChatBedrockConverse wrapper that fixes content block merging for\n * streaming responses and adds support for latest @langchain/aws features:\n *\n * - Application Inference Profiles (PR #9129)\n * - Service Tiers (Priority/Standard/Flex) (PR #9785) - requires AWS SDK 3.966.0+\n *\n * Bedrock's `@langchain/aws` library does not include an `index` property on content\n * blocks (unlike Anthropic/OpenAI), which causes LangChain's `_mergeLists` to append\n * each streaming chunk as a separate array entry instead of merging by index.\n *\n * This wrapper takes full ownership of the stream by directly interfacing with the\n * AWS SDK client (`this.client`) and using custom handlers from `./utils/` that\n * include `contentBlockIndex` in response_metadata for every delta type. It then\n * promotes `contentBlockIndex` to an `index` property on each content block\n * (mirroring Anthropic's pattern) and strips it from metadata to avoid\n * `_mergeDicts` conflicts.\n *\n * When multiple content block types are present (e.g. reasoning + text), text deltas\n * are promoted from strings to array form with `index` so they merge correctly once\n * the accumulated content is already an array.\n */\n\nimport { ChatBedrockConverse } from '@langchain/aws';\nimport { AIMessageChunk } from '@langchain/core/messages';\nimport { ChatGenerationChunk, ChatResult } from '@langchain/core/outputs';\nimport {\n ConverseStreamCommand,\n type ConverseStreamOutput,\n type GuardrailConfiguration,\n type GuardrailStreamConfiguration,\n} from '@aws-sdk/client-bedrock-runtime';\nimport type { CallbackManagerForLLMRun } from '@langchain/core/callbacks/manager';\nimport type { BaseMessage, ResponseMetadata } from '@langchain/core/messages';\nimport type { ChatBedrockConverseInput } from '@langchain/aws';\nimport type { ContentBlockDeltaEvent } from './types';\nimport {\n convertToConverseMessages,\n createConverseToolUseStopChunk,\n handleConverseStreamContentBlockStart,\n handleConverseStreamContentBlockDelta,\n handleConverseStreamMetadata,\n} from './utils';\nimport {\n resolveBedrockPromptCacheTtl,\n supportsBedrockToolCache,\n type PromptCacheTtl,\n} from '@/messages/cache';\nimport { linkStreamLimitCanonical } from '@/llm/streamLimits';\nimport { applyCachePointsToConversePayload } from './cachePoints';\nimport { insertBedrockToolCachePoint } from './toolCache';\n\n/**\n * Service tier type for Bedrock invocations.\n * Requires AWS SDK >= 3.966.0 to actually work.\n * @see https://docs.aws.amazon.com/bedrock/latest/userguide/service-tiers-inference.html\n */\nexport type ServiceTierType = 'priority' | 'default' | 'flex' | 'reserved';\n\nexport type CustomGuardrailConfiguration = GuardrailConfiguration &\n Pick<GuardrailStreamConfiguration, 'streamProcessingMode'>;\n\nconst MAX_STREAM_QUEUE_CHUNKS = 256;\nconst MAX_STREAM_QUEUE_TEXT_CHARS = 8192;\nconst STREAM_CHUNK_MIN_SIZE = 4;\nconst STREAM_BOUNDARIES = new Set([' ', '.', ',', '!', '?', ';', ':']);\n\ntype QueuedGenerationChunk = {\n chunk: ChatGenerationChunk;\n callbackChunk?: ChatGenerationChunk;\n callbackToken: string;\n smooth: boolean;\n textLength: number;\n};\n\nfunction findStreamChunkBoundary(text: string, minSize: number): number {\n if (minSize >= text.length) {\n return text.length;\n }\n\n for (let position = minSize; position < text.length; position++) {\n if (STREAM_BOUNDARIES.has(text[position])) {\n return position + 1;\n }\n }\n\n return text.length;\n}\n\nfunction splitStreamToken(text: string): string[] {\n const chunks: string[] = [];\n let currentIndex = 0;\n\n while (currentIndex < text.length) {\n const remainingText = text.slice(currentIndex);\n const chunkSize = findStreamChunkBoundary(\n remainingText,\n STREAM_CHUNK_MIN_SIZE\n );\n chunks.push(text.slice(currentIndex, currentIndex + chunkSize));\n currentIndex += chunkSize;\n }\n\n return chunks;\n}\n\n/**\n * Resolves the text a delta contributes to the smoothing cadence, preferring a\n * text delta over a reasoning delta and ignoring non-string payloads.\n */\nfunction resolveVisibleText(text?: string, reasoningText?: string): string {\n if (typeof text === 'string') {\n return text;\n }\n\n if (typeof reasoningText === 'string') {\n return reasoningText;\n }\n\n return '';\n}\n\nfunction getCadencedStreamDelay({\n targetDelay,\n lastVisibleContentAt,\n now,\n}: {\n targetDelay: number;\n lastVisibleContentAt?: number;\n now: number;\n}): number {\n if (targetDelay <= 0 || lastVisibleContentAt == null) {\n return 0;\n }\n return Math.max(0, targetDelay - (now - lastVisibleContentAt));\n}\n\nasync function waitForStreamDelay(\n delay: number,\n signal?: AbortSignal\n): Promise<void> {\n if (delay <= 0 || isSignalAborted(signal)) {\n return;\n }\n await new Promise<void>((resolve) => {\n const timeoutRef: { current?: ReturnType<typeof setTimeout> } = {};\n const onAbort = (): void => {\n if (timeoutRef.current) {\n clearTimeout(timeoutRef.current);\n }\n signal?.removeEventListener('abort', onAbort);\n resolve();\n };\n timeoutRef.current = setTimeout(() => {\n signal?.removeEventListener('abort', onAbort);\n resolve();\n }, delay);\n signal?.addEventListener('abort', onAbort, { once: true });\n if (isSignalAborted(signal)) {\n onAbort();\n }\n });\n}\n\nfunction isSignalAborted(signal?: AbortSignal): boolean {\n return signal?.aborted === true;\n}\n\n/**\n * Extended input interface with additional features:\n * - applicationInferenceProfile: Use an inference profile ARN instead of model ID\n * - serviceTier: Specify service tier (Priority, Standard, Flex, Reserved)\n */\nexport interface CustomChatBedrockConverseInput\n extends ChatBedrockConverseInput {\n /**\n * Enables Bedrock prompt cache checkpoints for message and tool prefixes.\n */\n promptCache?: boolean;\n\n /**\n * Prompt-cache checkpoint TTL. Defaults to `'1h'` (extended cache) when\n * `promptCache` is enabled; set `'5m'` for the legacy 5-minute behavior.\n * Bedrock models that don't support the 1-hour TTL downgrade to 5m\n * server-side (verified on Sonnet/Opus 4.6), so the default is safe to leave\n * on; use `'5m'` for any model that rejects it.\n */\n promptCacheTtl?: PromptCacheTtl;\n\n /**\n * Minimum delay in milliseconds between visible streamed content deltas.\n */\n _lc_stream_delay?: number;\n\n /**\n * Guardrail configuration for Converse and ConverseStream invocations.\n * `streamProcessingMode` is only used by ConverseStream.\n */\n guardrailConfig?: CustomGuardrailConfiguration;\n\n /**\n * Application Inference Profile ARN to use for the model.\n * For example, \"arn:aws:bedrock:eu-west-1:123456789102:application-inference-profile/fm16bt65tzgx\"\n * When provided, this ARN will be used for the actual inference calls instead of the model ID.\n * Must still provide `model` as normal modelId to benefit from all the metadata.\n * @see https://docs.aws.amazon.com/bedrock/latest/userguide/inference-profiles-create.html\n */\n applicationInferenceProfile?: string;\n\n /**\n * Service tier for model invocation.\n * Specifies the processing tier type used for serving the request.\n * Supported values are 'priority', 'default', 'flex', and 'reserved'.\n *\n * - 'priority': Prioritized processing for lower latency\n * - 'default': Standard processing tier\n * - 'flex': Flexible processing tier with lower cost\n * - 'reserved': Reserved capacity for consistent performance\n *\n * If not provided, AWS uses the default tier.\n * Note: Requires AWS SDK >= 3.966.0 to work.\n * @see https://docs.aws.amazon.com/bedrock/latest/userguide/service-tiers-inference.html\n */\n serviceTier?: ServiceTierType;\n}\n\n/**\n * Extended call options with serviceTier override support.\n */\nexport interface CustomChatBedrockConverseCallOptions {\n serviceTier?: ServiceTierType;\n guardrailConfig?: CustomGuardrailConfiguration;\n}\n\nexport class CustomChatBedrockConverse extends ChatBedrockConverse {\n _lc_stream_delay: number;\n\n /**\n * Whether to insert Bedrock prompt cache checkpoints when available.\n */\n promptCache?: boolean;\n\n /**\n * Prompt-cache checkpoint TTL (`'5m'` legacy or `'1h'` extended cache).\n */\n promptCacheTtl?: PromptCacheTtl;\n\n /**\n * Application Inference Profile ARN to use instead of model ID.\n */\n applicationInferenceProfile?: string;\n\n /**\n * Service tier for model invocation.\n */\n serviceTier?: ServiceTierType;\n\n /**\n * The configured model id, captured at construction so it survives the\n * temporary `this.model` swap to an application-inference-profile ARN during\n * generation. Used to gate the Bedrock tool cache point to Claude models\n * (see {@link supportsBedrockToolCache}).\n */\n private readonly cacheModelId: string;\n\n constructor(fields?: CustomChatBedrockConverseInput) {\n super(fields);\n this.promptCache = fields?.promptCache;\n this.promptCacheTtl = fields?.promptCacheTtl;\n this._lc_stream_delay = Math.max(0, fields?._lc_stream_delay ?? 0);\n this.applicationInferenceProfile = fields?.applicationInferenceProfile;\n this.serviceTier = fields?.serviceTier;\n // `super(fields)` initializes `this.model` to LangChain's default Claude\n // model when `fields.model` is omitted, so fall back to it rather than ''\n // (which would treat the default Claude model as tool-cache-unsupported).\n this.cacheModelId = fields?.model ?? this.model;\n }\n\n static lc_name(): string {\n return 'LibreChatBedrockConverse';\n }\n\n /**\n * Get the model ID to use for API calls.\n * Returns applicationInferenceProfile if set, otherwise returns this.model.\n */\n protected getModelId(): string {\n return this.applicationInferenceProfile ?? this.model;\n }\n\n /**\n * Override invocationParams to add serviceTier support.\n */\n override invocationParams(\n options?: this['ParsedCallOptions'] & CustomChatBedrockConverseCallOptions\n ): ReturnType<ChatBedrockConverse['invocationParams']> & {\n serviceTier?: { type: ServiceTierType };\n } {\n const baseParams = super.invocationParams(options);\n const toolConfig =\n this.promptCache === true && supportsBedrockToolCache(this.cacheModelId)\n ? insertBedrockToolCachePoint(\n baseParams.toolConfig,\n true,\n resolveBedrockPromptCacheTtl(this.promptCacheTtl, this.cacheModelId)\n )\n : baseParams.toolConfig;\n\n /** Service tier from options or fall back to class-level setting */\n const serviceTierType = options?.serviceTier ?? this.serviceTier;\n\n return {\n ...baseParams,\n toolConfig,\n serviceTier: serviceTierType ? { type: serviceTierType } : undefined,\n };\n }\n\n /**\n * Override _generateNonStreaming to use applicationInferenceProfile as modelId.\n * Uses the same model-swapping pattern as streaming for consistency.\n */\n override async _generateNonStreaming(\n messages: BaseMessage[],\n options: this['ParsedCallOptions'] & CustomChatBedrockConverseCallOptions,\n runManager?: CallbackManagerForLLMRun\n ): Promise<ChatResult> {\n const originalModel = this.model;\n if (\n this.applicationInferenceProfile != null &&\n this.applicationInferenceProfile !== ''\n ) {\n this.model = this.applicationInferenceProfile;\n }\n\n try {\n return await super._generateNonStreaming(messages, options, runManager);\n } finally {\n this.model = originalModel;\n }\n }\n\n /**\n * Own the stream end-to-end so we have direct access to every\n * `contentBlockDelta.contentBlockIndex` from the AWS SDK.\n *\n * This replaces the parent's implementation which strips contentBlockIndex\n * from text and reasoning deltas, making it impossible to merge correctly.\n */\n override async *_streamResponseChunks(\n messages: BaseMessage[],\n options: this['ParsedCallOptions'] & CustomChatBedrockConverseCallOptions,\n runManager?: CallbackManagerForLLMRun\n ): AsyncGenerator<ChatGenerationChunk> {\n const { converseMessages, converseSystem } =\n convertToConverseMessages(messages);\n const params = this.invocationParams(options);\n\n let { streamUsage } = this;\n if ((options as Record<string, unknown>).streamUsage !== undefined) {\n streamUsage = (options as Record<string, unknown>).streamUsage as boolean;\n }\n\n const modelId = this.getModelId();\n\n applyCachePointsToConversePayload({\n cacheControl: options.cache_control,\n system: converseSystem,\n messages: converseMessages,\n params,\n modelId,\n });\n\n const command = new ConverseStreamCommand({\n modelId,\n messages: converseMessages,\n system: converseSystem,\n ...(params as Record<string, unknown>),\n });\n\n const streamAbortController = new AbortController();\n const abortStream = (): void => streamAbortController.abort();\n options.signal?.addEventListener('abort', abortStream, { once: true });\n if (isSignalAborted(options.signal)) {\n abortStream();\n }\n\n try {\n const response = await this.client.send(command, {\n abortSignal: streamAbortController.signal,\n });\n\n const stream: AsyncIterable<ConverseStreamOutput> | undefined =\n response.stream;\n if (!stream) {\n return;\n }\n\n const seenBlockIndices = new Set<number>();\n const toolUseBlockIndices = new Set<number>();\n const queuedChunks: QueuedGenerationChunk[] = [];\n const producerState: { done: boolean; error?: unknown } = { done: false };\n let queuedChunkIndex = 0;\n let bufferedTextLength = 0;\n let consumerClosed = false;\n let notifyConsumer: (() => void) | undefined;\n let notifyProducer: (() => void) | undefined;\n let hasEmittedVisibleContent = false;\n let lastVisibleContentAt: number | undefined;\n\n /**\n * Guardrails can reject an already-streamed toolUse block at\n * `messageStop` (`guardrail_intervened`), after `contentBlockStop` has\n * passed. Only emit eager-execution seals when no guardrails are\n * configured, so a later intervention can't race an eagerly started tool.\n */\n const sealToolUseOnStop =\n options.guardrailConfig == null && this.guardrailConfig == null;\n\n const notifyConsumerForChunk = (): void => {\n notifyConsumer?.();\n notifyConsumer = undefined;\n };\n\n const notifyProducerForSpace = (): void => {\n notifyProducer?.();\n notifyProducer = undefined;\n };\n\n const hasQueuedChunks = (): boolean =>\n queuedChunkIndex < queuedChunks.length;\n\n const getQueuedChunkCount = (): number =>\n queuedChunks.length - queuedChunkIndex;\n\n const isQueueAtCapacity = (): boolean =>\n getQueuedChunkCount() >= MAX_STREAM_QUEUE_CHUNKS ||\n bufferedTextLength >= MAX_STREAM_QUEUE_TEXT_CHARS;\n\n const waitForNextChunk = async (): Promise<void> => {\n if (\n hasQueuedChunks() ||\n producerState.done ||\n producerState.error != null\n ) {\n return;\n }\n await new Promise<void>((resolve) => {\n notifyConsumer = resolve;\n });\n };\n\n const waitForQueueSpace = async (): Promise<void> => {\n while (\n isQueueAtCapacity() &&\n !consumerClosed &&\n !isSignalAborted(options.signal)\n ) {\n await new Promise<void>((resolve) => {\n const signal = options.signal;\n const onAbort = (): void => {\n signal?.removeEventListener('abort', onAbort);\n resolve();\n };\n const onSpace = (): void => {\n signal?.removeEventListener('abort', onAbort);\n resolve();\n };\n notifyProducer = onSpace;\n signal?.addEventListener('abort', onAbort, { once: true });\n if (isSignalAborted(signal)) {\n onAbort();\n }\n });\n }\n };\n\n const dequeue = (): QueuedGenerationChunk | undefined => {\n if (!hasQueuedChunks()) {\n return undefined;\n }\n const queuedChunk = queuedChunks[queuedChunkIndex];\n queuedChunkIndex++;\n if (\n queuedChunkIndex > 128 &&\n queuedChunkIndex * 2 >= queuedChunks.length\n ) {\n queuedChunks.splice(0, queuedChunkIndex);\n queuedChunkIndex = 0;\n }\n return queuedChunk;\n };\n\n const enqueue = async (\n queuedChunk: QueuedGenerationChunk\n ): Promise<void> => {\n await waitForQueueSpace();\n if (consumerClosed || isSignalAborted(options.signal)) {\n abortStream();\n throw new Error('AbortError: User aborted the request.');\n }\n queuedChunks.push(queuedChunk);\n if (queuedChunk.smooth) {\n bufferedTextLength += queuedChunk.textLength;\n }\n notifyConsumerForChunk();\n };\n\n const enqueueChunk = async ({\n chunk,\n callbackChunk,\n callbackToken = '',\n smooth = false,\n textLength = 0,\n }: {\n chunk: ChatGenerationChunk;\n callbackChunk?: ChatGenerationChunk;\n callbackToken?: string;\n smooth?: boolean;\n textLength?: number;\n }): Promise<void> => {\n await enqueue({\n chunk,\n callbackChunk,\n callbackToken,\n smooth,\n textLength: smooth ? textLength : 0,\n });\n };\n\n const enqueueDelta = async (\n contentBlockDelta: ContentBlockDeltaEvent\n ): Promise<void> => {\n const delta = contentBlockDelta.delta;\n if (delta == null) {\n throw new Error('No delta found in content block.');\n }\n\n const idx = contentBlockDelta.contentBlockIndex;\n if (idx != null) {\n seenBlockIndices.add(idx);\n }\n\n const text = delta.text;\n const reasoningContent = delta.reasoningContent;\n const reasoningText = reasoningContent?.text;\n const visibleText = resolveVisibleText(text, reasoningText);\n const smooth = this._lc_stream_delay > 0 && visibleText !== '';\n const tokenChunks = smooth\n ? splitStreamToken(visibleText)\n : [visibleText];\n\n for (const token of tokenChunks) {\n let splitDelta = contentBlockDelta;\n if (typeof text === 'string') {\n splitDelta = {\n ...contentBlockDelta,\n delta: { text: token },\n };\n } else if (\n typeof reasoningText === 'string' &&\n reasoningContent != null\n ) {\n splitDelta = {\n ...contentBlockDelta,\n delta: {\n reasoningContent: {\n ...reasoningContent,\n text: token,\n },\n },\n };\n }\n\n const deltaChunk = handleConverseStreamContentBlockDelta(splitDelta);\n const enrichedChunk = this.enrichChunk(deltaChunk, seenBlockIndices);\n if (enrichedChunk !== deltaChunk) {\n /** The callback copy is the same emission as the enriched yield;\n * without the link, stream-limit accounting charges both message\n * objects and Bedrock tool arguments falsely trip near half the\n * cap. Linked on the messages, which are what the accounting\n * observes. */\n linkStreamLimitCanonical(deltaChunk.message, enrichedChunk.message);\n }\n await enqueueChunk({\n chunk: enrichedChunk,\n callbackChunk: deltaChunk,\n callbackToken: deltaChunk.text,\n smooth,\n textLength: token.length,\n });\n }\n };\n\n const producer = (async (): Promise<void> => {\n try {\n for await (const event of stream) {\n if (isSignalAborted(options.signal)) {\n abortStream();\n throw new Error('AbortError: User aborted the request.');\n }\n\n if (event.contentBlockStart != null) {\n const startChunk = handleConverseStreamContentBlockStart(\n event.contentBlockStart\n );\n if (startChunk != null) {\n const idx = event.contentBlockStart.contentBlockIndex;\n if (idx != null) {\n seenBlockIndices.add(idx);\n if (event.contentBlockStart.start?.toolUse != null) {\n toolUseBlockIndices.add(idx);\n }\n }\n const enrichedStart = this.enrichChunk(\n startChunk,\n seenBlockIndices\n );\n if (enrichedStart !== startChunk) {\n linkStreamLimitCanonical(\n startChunk.message,\n enrichedStart.message\n );\n }\n await enqueueChunk({\n chunk: enrichedStart,\n callbackChunk: startChunk,\n callbackToken: startChunk.text,\n });\n }\n } else if (event.contentBlockDelta != null) {\n await enqueueDelta(event.contentBlockDelta);\n } else if (event.metadata != null) {\n await enqueueChunk({\n chunk: handleConverseStreamMetadata(event.metadata, {\n streamUsage,\n }),\n });\n } else if (event.contentBlockStop != null) {\n const stopIdx = event.contentBlockStop.contentBlockIndex;\n if (stopIdx != null) {\n seenBlockIndices.add(stopIdx);\n if (sealToolUseOnStop && toolUseBlockIndices.has(stopIdx)) {\n const sealChunk = createConverseToolUseStopChunk(stopIdx);\n await enqueueChunk({\n chunk: sealChunk,\n callbackChunk: sealChunk,\n callbackToken: sealChunk.text,\n });\n }\n }\n } else {\n await enqueueChunk({\n chunk: new ChatGenerationChunk({\n text: '',\n message: new AIMessageChunk({\n content: '',\n response_metadata: { ...event } as ResponseMetadata,\n }),\n }),\n });\n }\n }\n } catch (error) {\n producerState.error = error;\n } finally {\n producerState.done = true;\n notifyConsumerForChunk();\n }\n })();\n\n try {\n let keepStreaming = true;\n while (keepStreaming) {\n if (isSignalAborted(options.signal)) {\n abortStream();\n throw new Error('AbortError: User aborted the request.');\n }\n\n await waitForNextChunk();\n const queuedChunk = dequeue();\n\n if (!queuedChunk) {\n if (producerState.error != null) {\n throw producerState.error;\n }\n if (producerState.done) {\n keepStreaming = false;\n }\n continue;\n }\n\n if (queuedChunk.smooth) {\n bufferedTextLength = Math.max(\n 0,\n bufferedTextLength - queuedChunk.textLength\n );\n notifyProducerForSpace();\n await waitForStreamDelay(\n getCadencedStreamDelay({\n targetDelay: hasEmittedVisibleContent\n ? this._lc_stream_delay\n : 0,\n lastVisibleContentAt,\n now: Date.now(),\n }),\n options.signal\n );\n if (isSignalAborted(options.signal)) {\n abortStream();\n throw new Error('AbortError: User aborted the request.');\n }\n hasEmittedVisibleContent = true;\n lastVisibleContentAt = Date.now();\n } else {\n notifyProducerForSpace();\n }\n\n yield queuedChunk.chunk;\n\n if (queuedChunk.callbackChunk != null) {\n await runManager?.handleLLMNewToken(\n queuedChunk.callbackToken,\n undefined,\n undefined,\n undefined,\n undefined,\n { chunk: queuedChunk.callbackChunk }\n );\n }\n }\n } finally {\n consumerClosed = true;\n if (!producerState.done) {\n abortStream();\n notifyProducerForSpace();\n }\n await producer;\n }\n } finally {\n options.signal?.removeEventListener('abort', abortStream);\n if (!streamAbortController.signal.aborted) {\n streamAbortController.abort();\n }\n }\n }\n\n /**\n * Inject `index` on content blocks for proper merge behaviour, then strip\n * `contentBlockIndex` from response_metadata to prevent `_mergeDicts` conflicts.\n *\n * Text string content is promoted to array form only when the stream contains\n * multiple content block indices (e.g. reasoning at index 0, text at index 1),\n * ensuring text merges correctly with the already-array accumulated content.\n */\n private enrichChunk(\n chunk: ChatGenerationChunk,\n seenBlockIndices: Set<number>\n ): ChatGenerationChunk {\n const message = chunk.message;\n if (!(message instanceof AIMessageChunk)) {\n return chunk;\n }\n\n const metadata = message.response_metadata as Record<string, unknown>;\n const blockIndex = this.extractContentBlockIndex(metadata);\n const hasMetadataIndex = blockIndex != null;\n\n let content: AIMessageChunk['content'] = message.content;\n let contentModified = false;\n\n if (Array.isArray(content) && blockIndex != null) {\n content = content.map((block) =>\n typeof block === 'object' && !('index' in block)\n ? { ...block, index: blockIndex }\n : block\n );\n contentModified = true;\n } else if (\n typeof content === 'string' &&\n content !== '' &&\n blockIndex != null &&\n seenBlockIndices.size > 1\n ) {\n content = [{ type: 'text', text: content, index: blockIndex }];\n contentModified = true;\n }\n\n if (!contentModified && !hasMetadataIndex) {\n return chunk;\n }\n\n const cleanedMetadata = hasMetadataIndex\n ? (this.removeContentBlockIndex(metadata) as Record<string, unknown>)\n : metadata;\n\n return new ChatGenerationChunk({\n text: chunk.text,\n message: new AIMessageChunk({\n ...message,\n content,\n response_metadata: cleanedMetadata,\n }),\n generationInfo: chunk.generationInfo,\n });\n }\n\n /**\n * Extract `contentBlockIndex` from the top level of response_metadata.\n * Our custom handlers always place it at the top level.\n */\n private extractContentBlockIndex(\n metadata: Record<string, unknown>\n ): number | undefined {\n if (\n 'contentBlockIndex' in metadata &&\n typeof metadata.contentBlockIndex === 'number'\n ) {\n return metadata.contentBlockIndex;\n }\n return undefined;\n }\n\n private removeContentBlockIndex(obj: unknown): unknown {\n if (obj === null || obj === undefined) {\n return obj;\n }\n\n if (Array.isArray(obj)) {\n return obj.map((item) => this.removeContentBlockIndex(item));\n }\n\n if (typeof obj === 'object') {\n const cleaned: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(obj)) {\n if (key !== 'contentBlockIndex') {\n cleaned[key] = this.removeContentBlockIndex(value);\n }\n }\n return cleaned;\n }\n\n return obj;\n }\n}\n\nexport type { ChatBedrockConverseInput };\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8DA,MAAM,0BAA0B;AAChC,MAAM,8BAA8B;AACpC,MAAM,wBAAwB;AAC9B,MAAM,oBAAoB,IAAI,IAAI;CAAC;CAAK;CAAK;CAAK;CAAK;CAAK;CAAK;AAAG,CAAC;AAUrE,SAAS,wBAAwB,MAAc,SAAyB;CACtE,IAAI,WAAW,KAAK,QAClB,OAAO,KAAK;CAGd,KAAK,IAAI,WAAW,SAAS,WAAW,KAAK,QAAQ,YACnD,IAAI,kBAAkB,IAAI,KAAK,SAAS,GACtC,OAAO,WAAW;CAItB,OAAO,KAAK;AACd;AAEA,SAAS,iBAAiB,MAAwB;CAChD,MAAM,SAAmB,CAAC;CAC1B,IAAI,eAAe;CAEnB,OAAO,eAAe,KAAK,QAAQ;EAEjC,MAAM,YAAY,wBADI,KAAK,MAAM,YAEnB,GACZ,qBACF;EACA,OAAO,KAAK,KAAK,MAAM,cAAc,eAAe,SAAS,CAAC;EAC9D,gBAAgB;CAClB;CAEA,OAAO;AACT;;;;;AAMA,SAAS,mBAAmB,MAAe,eAAgC;CACzE,IAAI,OAAO,SAAS,UAClB,OAAO;CAGT,IAAI,OAAO,kBAAkB,UAC3B,OAAO;CAGT,OAAO;AACT;AAEA,SAAS,uBAAuB,EAC9B,aACA,sBACA,OAKS;CACT,IAAI,eAAe,KAAK,wBAAwB,MAC9C,OAAO;CAET,OAAO,KAAK,IAAI,GAAG,eAAe,MAAM,qBAAqB;AAC/D;AAEA,eAAe,mBACb,OACA,QACe;CACf,IAAI,SAAS,KAAK,gBAAgB,MAAM,GACtC;CAEF,MAAM,IAAI,SAAe,YAAY;EACnC,MAAM,aAA0D,CAAC;EACjE,MAAM,gBAAsB;GAC1B,IAAI,WAAW,SACb,aAAa,WAAW,OAAO;GAEjC,QAAQ,oBAAoB,SAAS,OAAO;GAC5C,QAAQ;EACV;EACA,WAAW,UAAU,iBAAiB;GACpC,QAAQ,oBAAoB,SAAS,OAAO;GAC5C,QAAQ;EACV,GAAG,KAAK;EACR,QAAQ,iBAAiB,SAAS,SAAS,EAAE,MAAM,KAAK,CAAC;EACzD,IAAI,gBAAgB,MAAM,GACxB,QAAQ;CAEZ,CAAC;AACH;AAEA,SAAS,gBAAgB,QAA+B;CACtD,OAAO,QAAQ,YAAY;AAC7B;AAoEA,IAAa,4BAAb,cAA+C,oBAAoB;CACjE;;;;CAKA;;;;CAKA;;;;CAKA;;;;CAKA;;;;;;;CAQA;CAEA,YAAY,QAAyC;EACnD,MAAM,MAAM;EACZ,KAAK,cAAc,QAAQ;EAC3B,KAAK,iBAAiB,QAAQ;EAC9B,KAAK,mBAAmB,KAAK,IAAI,GAAG,QAAQ,oBAAoB,CAAC;EACjE,KAAK,8BAA8B,QAAQ;EAC3C,KAAK,cAAc,QAAQ;EAI3B,KAAK,eAAe,QAAQ,SAAS,KAAK;CAC5C;CAEA,OAAO,UAAkB;EACvB,OAAO;CACT;;;;;CAMA,aAA+B;EAC7B,OAAO,KAAK,+BAA+B,KAAK;CAClD;;;;CAKA,iBACE,SAGA;EACA,MAAM,aAAa,MAAM,iBAAiB,OAAO;EACjD,MAAM,aACJ,KAAK,gBAAgB,QAAQ,yBAAyB,KAAK,YAAY,IACnE,4BACA,WAAW,YACX,MACA,6BAA6B,KAAK,gBAAgB,KAAK,YAAY,CACrE,IACE,WAAW;;EAGjB,MAAM,kBAAkB,SAAS,eAAe,KAAK;EAErD,OAAO;GACL,GAAG;GACH;GACA,aAAa,kBAAkB,EAAE,MAAM,gBAAgB,IAAI,KAAA;EAC7D;CACF;;;;;CAMA,MAAe,sBACb,UACA,SACA,YACqB;EACrB,MAAM,gBAAgB,KAAK;EAC3B,IACE,KAAK,+BAA+B,QACpC,KAAK,gCAAgC,IAErC,KAAK,QAAQ,KAAK;EAGpB,IAAI;GACF,OAAO,MAAM,MAAM,sBAAsB,UAAU,SAAS,UAAU;EACxE,UAAU;GACR,KAAK,QAAQ;EACf;CACF;;;;;;;;CASA,OAAgB,sBACd,UACA,SACA,YACqC;EACrC,MAAM,EAAE,kBAAkB,mBACxB,0BAA0B,QAAQ;EACpC,MAAM,SAAS,KAAK,iBAAiB,OAAO;EAE5C,IAAI,EAAE,gBAAgB;EACtB,IAAK,QAAoC,gBAAgB,KAAA,GACvD,cAAe,QAAoC;EAGrD,MAAM,UAAU,KAAK,WAAW;EAEhC,kCAAkC;GAChC,cAAc,QAAQ;GACtB,QAAQ;GACR,UAAU;GACV;GACA;EACF,CAAC;EAED,MAAM,UAAU,IAAI,sBAAsB;GACxC;GACA,UAAU;GACV,QAAQ;GACR,GAAI;EACN,CAAC;EAED,MAAM,wBAAwB,IAAI,gBAAgB;EAClD,MAAM,oBAA0B,sBAAsB,MAAM;EAC5D,QAAQ,QAAQ,iBAAiB,SAAS,aAAa,EAAE,MAAM,KAAK,CAAC;EACrE,IAAI,gBAAgB,QAAQ,MAAM,GAChC,YAAY;EAGd,IAAI;GAKF,MAAM,UACJ,MALqB,KAAK,OAAO,KAAK,SAAS,EAC/C,aAAa,sBAAsB,OACrC,CAAC,EAAA,CAGU;GACX,IAAI,CAAC,QACH;GAGF,MAAM,mCAAmB,IAAI,IAAY;GACzC,MAAM,sCAAsB,IAAI,IAAY;GAC5C,MAAM,eAAwC,CAAC;GAC/C,MAAM,gBAAoD,EAAE,MAAM,MAAM;GACxE,IAAI,mBAAmB;GACvB,IAAI,qBAAqB;GACzB,IAAI,iBAAiB;GACrB,IAAI;GACJ,IAAI;GACJ,IAAI,2BAA2B;GAC/B,IAAI;;;;;;;GAQJ,MAAM,oBACJ,QAAQ,mBAAmB,QAAQ,KAAK,mBAAmB;GAE7D,MAAM,+BAAqC;IACzC,iBAAiB;IACjB,iBAAiB,KAAA;GACnB;GAEA,MAAM,+BAAqC;IACzC,iBAAiB;IACjB,iBAAiB,KAAA;GACnB;GAEA,MAAM,wBACJ,mBAAmB,aAAa;GAElC,MAAM,4BACJ,aAAa,SAAS;GAExB,MAAM,0BACJ,oBAAoB,KAAK,2BACzB,sBAAsB;GAExB,MAAM,mBAAmB,YAA2B;IAClD,IACE,gBAAgB,KAChB,cAAc,QACd,cAAc,SAAS,MAEvB;IAEF,MAAM,IAAI,SAAe,YAAY;KACnC,iBAAiB;IACnB,CAAC;GACH;GAEA,MAAM,oBAAoB,YAA2B;IACnD,OACE,kBAAkB,KAClB,CAAC,kBACD,CAAC,gBAAgB,QAAQ,MAAM,GAE/B,MAAM,IAAI,SAAe,YAAY;KACnC,MAAM,SAAS,QAAQ;KACvB,MAAM,gBAAsB;MAC1B,QAAQ,oBAAoB,SAAS,OAAO;MAC5C,QAAQ;KACV;KACA,MAAM,gBAAsB;MAC1B,QAAQ,oBAAoB,SAAS,OAAO;MAC5C,QAAQ;KACV;KACA,iBAAiB;KACjB,QAAQ,iBAAiB,SAAS,SAAS,EAAE,MAAM,KAAK,CAAC;KACzD,IAAI,gBAAgB,MAAM,GACxB,QAAQ;IAEZ,CAAC;GAEL;GAEA,MAAM,gBAAmD;IACvD,IAAI,CAAC,gBAAgB,GACnB;IAEF,MAAM,cAAc,aAAa;IACjC;IACA,IACE,mBAAmB,OACnB,mBAAmB,KAAK,aAAa,QACrC;KACA,aAAa,OAAO,GAAG,gBAAgB;KACvC,mBAAmB;IACrB;IACA,OAAO;GACT;GAEA,MAAM,UAAU,OACd,gBACkB;IAClB,MAAM,kBAAkB;IACxB,IAAI,kBAAkB,gBAAgB,QAAQ,MAAM,GAAG;KACrD,YAAY;KACZ,MAAM,IAAI,MAAM,uCAAuC;IACzD;IACA,aAAa,KAAK,WAAW;IAC7B,IAAI,YAAY,QACd,sBAAsB,YAAY;IAEpC,uBAAuB;GACzB;GAEA,MAAM,eAAe,OAAO,EAC1B,OACA,eACA,gBAAgB,IAChB,SAAS,OACT,aAAa,QAOM;IACnB,MAAM,QAAQ;KACZ;KACA;KACA;KACA;KACA,YAAY,SAAS,aAAa;IACpC,CAAC;GACH;GAEA,MAAM,eAAe,OACnB,sBACkB;IAClB,MAAM,QAAQ,kBAAkB;IAChC,IAAI,SAAS,MACX,MAAM,IAAI,MAAM,kCAAkC;IAGpD,MAAM,MAAM,kBAAkB;IAC9B,IAAI,OAAO,MACT,iBAAiB,IAAI,GAAG;IAG1B,MAAM,OAAO,MAAM;IACnB,MAAM,mBAAmB,MAAM;IAC/B,MAAM,gBAAgB,kBAAkB;IACxC,MAAM,cAAc,mBAAmB,MAAM,aAAa;IAC1D,MAAM,SAAS,KAAK,mBAAmB,KAAK,gBAAgB;IAC5D,MAAM,cAAc,SAChB,iBAAiB,WAAW,IAC5B,CAAC,WAAW;IAEhB,KAAK,MAAM,SAAS,aAAa;KAC/B,IAAI,aAAa;KACjB,IAAI,OAAO,SAAS,UAClB,aAAa;MACX,GAAG;MACH,OAAO,EAAE,MAAM,MAAM;KACvB;UACK,IACL,OAAO,kBAAkB,YACzB,oBAAoB,MAEpB,aAAa;MACX,GAAG;MACH,OAAO,EACL,kBAAkB;OAChB,GAAG;OACH,MAAM;MACR,EACF;KACF;KAGF,MAAM,aAAa,sCAAsC,UAAU;KACnE,MAAM,gBAAgB,KAAK,YAAY,YAAY,gBAAgB;KACnE,IAAI,kBAAkB;;;;;;KAMpB,yBAAyB,WAAW,SAAS,cAAc,OAAO;KAEpE,MAAM,aAAa;MACjB,OAAO;MACP,eAAe;MACf,eAAe,WAAW;MAC1B;MACA,YAAY,MAAM;KACpB,CAAC;IACH;GACF;GAEA,MAAM,YAAY,YAA2B;IAC3C,IAAI;KACF,WAAW,MAAM,SAAS,QAAQ;MAChC,IAAI,gBAAgB,QAAQ,MAAM,GAAG;OACnC,YAAY;OACZ,MAAM,IAAI,MAAM,uCAAuC;MACzD;MAEA,IAAI,MAAM,qBAAqB,MAAM;OACnC,MAAM,aAAa,sCACjB,MAAM,iBACR;OACA,IAAI,cAAc,MAAM;QACtB,MAAM,MAAM,MAAM,kBAAkB;QACpC,IAAI,OAAO,MAAM;SACf,iBAAiB,IAAI,GAAG;SACxB,IAAI,MAAM,kBAAkB,OAAO,WAAW,MAC5C,oBAAoB,IAAI,GAAG;QAE/B;QACA,MAAM,gBAAgB,KAAK,YACzB,YACA,gBACF;QACA,IAAI,kBAAkB,YACpB,yBACE,WAAW,SACX,cAAc,OAChB;QAEF,MAAM,aAAa;SACjB,OAAO;SACP,eAAe;SACf,eAAe,WAAW;QAC5B,CAAC;OACH;MACF,OAAO,IAAI,MAAM,qBAAqB,MACpC,MAAM,aAAa,MAAM,iBAAiB;WACrC,IAAI,MAAM,YAAY,MAC3B,MAAM,aAAa,EACjB,OAAO,6BAA6B,MAAM,UAAU,EAClD,YACF,CAAC,EACH,CAAC;WACI,IAAI,MAAM,oBAAoB,MAAM;OACzC,MAAM,UAAU,MAAM,iBAAiB;OACvC,IAAI,WAAW,MAAM;QACnB,iBAAiB,IAAI,OAAO;QAC5B,IAAI,qBAAqB,oBAAoB,IAAI,OAAO,GAAG;SACzD,MAAM,YAAY,+BAA+B,OAAO;SACxD,MAAM,aAAa;UACjB,OAAO;UACP,eAAe;UACf,eAAe,UAAU;SAC3B,CAAC;QACH;OACF;MACF,OACE,MAAM,aAAa,EACjB,OAAO,IAAI,oBAAoB;OAC7B,MAAM;OACN,SAAS,IAAI,eAAe;QAC1B,SAAS;QACT,mBAAmB,EAAE,GAAG,MAAM;OAChC,CAAC;MACH,CAAC,EACH,CAAC;KAEL;IACF,SAAS,OAAO;KACd,cAAc,QAAQ;IACxB,UAAU;KACR,cAAc,OAAO;KACrB,uBAAuB;IACzB;GACF,EAAA,CAAG;GAEH,IAAI;IACF,IAAI,gBAAgB;IACpB,OAAO,eAAe;KACpB,IAAI,gBAAgB,QAAQ,MAAM,GAAG;MACnC,YAAY;MACZ,MAAM,IAAI,MAAM,uCAAuC;KACzD;KAEA,MAAM,iBAAiB;KACvB,MAAM,cAAc,QAAQ;KAE5B,IAAI,CAAC,aAAa;MAChB,IAAI,cAAc,SAAS,MACzB,MAAM,cAAc;MAEtB,IAAI,cAAc,MAChB,gBAAgB;MAElB;KACF;KAEA,IAAI,YAAY,QAAQ;MACtB,qBAAqB,KAAK,IACxB,GACA,qBAAqB,YAAY,UACnC;MACA,uBAAuB;MACvB,MAAM,mBACJ,uBAAuB;OACrB,aAAa,2BACT,KAAK,mBACL;OACJ;OACA,KAAK,KAAK,IAAI;MAChB,CAAC,GACD,QAAQ,MACV;MACA,IAAI,gBAAgB,QAAQ,MAAM,GAAG;OACnC,YAAY;OACZ,MAAM,IAAI,MAAM,uCAAuC;MACzD;MACA,2BAA2B;MAC3B,uBAAuB,KAAK,IAAI;KAClC,OACE,uBAAuB;KAGzB,MAAM,YAAY;KAElB,IAAI,YAAY,iBAAiB,MAC/B,MAAM,YAAY,kBAChB,YAAY,eACZ,KAAA,GACA,KAAA,GACA,KAAA,GACA,KAAA,GACA,EAAE,OAAO,YAAY,cAAc,CACrC;IAEJ;GACF,UAAU;IACR,iBAAiB;IACjB,IAAI,CAAC,cAAc,MAAM;KACvB,YAAY;KACZ,uBAAuB;IACzB;IACA,MAAM;GACR;EACF,UAAU;GACR,QAAQ,QAAQ,oBAAoB,SAAS,WAAW;GACxD,IAAI,CAAC,sBAAsB,OAAO,SAChC,sBAAsB,MAAM;EAEhC;CACF;;;;;;;;;CAUA,YACE,OACA,kBACqB;EACrB,MAAM,UAAU,MAAM;EACtB,IAAI,EAAE,mBAAmB,iBACvB,OAAO;EAGT,MAAM,WAAW,QAAQ;EACzB,MAAM,aAAa,KAAK,yBAAyB,QAAQ;EACzD,MAAM,mBAAmB,cAAc;EAEvC,IAAI,UAAqC,QAAQ;EACjD,IAAI,kBAAkB;EAEtB,IAAI,MAAM,QAAQ,OAAO,KAAK,cAAc,MAAM;GAChD,UAAU,QAAQ,KAAK,UACrB,OAAO,UAAU,YAAY,EAAE,WAAW,SACtC;IAAE,GAAG;IAAO,OAAO;GAAW,IAC9B,KACN;GACA,kBAAkB;EACpB,OAAO,IACL,OAAO,YAAY,YACnB,YAAY,MACZ,cAAc,QACd,iBAAiB,OAAO,GACxB;GACA,UAAU,CAAC;IAAE,MAAM;IAAQ,MAAM;IAAS,OAAO;GAAW,CAAC;GAC7D,kBAAkB;EACpB;EAEA,IAAI,CAAC,mBAAmB,CAAC,kBACvB,OAAO;EAGT,MAAM,kBAAkB,mBACnB,KAAK,wBAAwB,QAAQ,IACtC;EAEJ,OAAO,IAAI,oBAAoB;GAC7B,MAAM,MAAM;GACZ,SAAS,IAAI,eAAe;IAC1B,GAAG;IACH;IACA,mBAAmB;GACrB,CAAC;GACD,gBAAgB,MAAM;EACxB,CAAC;CACH;;;;;CAMA,yBACE,UACoB;EACpB,IACE,uBAAuB,YACvB,OAAO,SAAS,sBAAsB,UAEtC,OAAO,SAAS;CAGpB;CAEA,wBAAgC,KAAuB;EACrD,IAAI,QAAQ,QAAQ,QAAQ,KAAA,GAC1B,OAAO;EAGT,IAAI,MAAM,QAAQ,GAAG,GACnB,OAAO,IAAI,KAAK,SAAS,KAAK,wBAAwB,IAAI,CAAC;EAG7D,IAAI,OAAO,QAAQ,UAAU;GAC3B,MAAM,UAAmC,CAAC;GAC1C,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,GAAG,GAC3C,IAAI,QAAQ,qBACV,QAAQ,OAAO,KAAK,wBAAwB,KAAK;GAGrD,OAAO;EACT;EAEA,OAAO;CACT;AACF"}
|
package/dist/esm/llm/invoke.mjs
CHANGED
|
@@ -7,6 +7,7 @@ import { safeDispatchCustomEvent } from "../utils/events.mjs";
|
|
|
7
7
|
import { coalesceAdjacentUserTurns, strictAlternationProviders } from "../messages/alternation.mjs";
|
|
8
8
|
import { appendPredecessorHandoffCue, removePredecessorHandoffCue } from "../messages/handoffCue.mjs";
|
|
9
9
|
import "../messages/index.mjs";
|
|
10
|
+
import { STREAM_LIMIT_ATTEMPT_KEY, STREAM_LIMIT_REDISPATCH_KEY, StreamLimitExceededError, enforceStreamLimitsForWireChunk, registerActiveStreamLimitGeneration, releaseStreamLimitGeneration, resolveGenerationKey, streamLimitAccountingEnabled } from "./streamLimits.mjs";
|
|
10
11
|
import { annotateMessagesForLLM } from "../tools/toolOutputReferences.mjs";
|
|
11
12
|
import { isAnthropicLike, isOpenAILike } from "../utils/llm.mjs";
|
|
12
13
|
import { ChatModelStreamHandler, dispatchesChatModelStream } from "../stream.mjs";
|
|
@@ -20,6 +21,8 @@ import { AIMessageChunk } from "@langchain/core/messages";
|
|
|
20
21
|
import { CallbackManager, CallbackManagerForLLMRun } from "@langchain/core/callbacks/manager";
|
|
21
22
|
import { concat } from "@langchain/core/utils/stream";
|
|
22
23
|
//#region src/llm/invoke.ts
|
|
24
|
+
/** Unique per-model-attempt sequence; see the stamp in `attemptInvoke`. */
|
|
25
|
+
let streamLimitAttemptSeq = 0;
|
|
23
26
|
function usesNativeOpenAIResponses(model, provider, callOptions) {
|
|
24
27
|
if (!isOpenAILike(provider)) return false;
|
|
25
28
|
let candidate = model;
|
|
@@ -304,15 +307,44 @@ function appendStreamChunk({ current, next, provider }) {
|
|
|
304
307
|
}));
|
|
305
308
|
}
|
|
306
309
|
/**
|
|
307
|
-
*
|
|
308
|
-
*
|
|
309
|
-
*
|
|
310
|
-
*
|
|
311
|
-
*
|
|
312
|
-
* Pass an `onChunk` callback to override this with custom chunk processing
|
|
313
|
-
* (e.g. summarization delta events).
|
|
310
|
+
* One model attempt. Stamps the attempt identity into callback metadata
|
|
311
|
+
* (see the generation-key notes in `streamLimits.ts`), leases the attempt's
|
|
312
|
+
* accounting for its LIFETIME, and releases both from `finally`: retention
|
|
313
|
+
* must follow the attempt — a cancellation-ignoring straggler keeps its
|
|
314
|
+
* original budget no matter how many runs start and reset while it drains.
|
|
314
315
|
*/
|
|
315
|
-
async function attemptInvoke(
|
|
316
|
+
async function attemptInvoke(params, config) {
|
|
317
|
+
const stampedConfig = {
|
|
318
|
+
...config,
|
|
319
|
+
metadata: {
|
|
320
|
+
...config?.metadata ?? {},
|
|
321
|
+
["__invoked_provider"]: params.provider,
|
|
322
|
+
/**
|
|
323
|
+
* One `attemptInvoke` call is one model attempt; primary, fallback,
|
|
324
|
+
* and retry attempts within a node otherwise share the same langgraph
|
|
325
|
+
* metadata, so without a unique attempt stamp a fallback re-streaming
|
|
326
|
+
* a tool call from scratch would be charged the failed primary's
|
|
327
|
+
* partial bytes (or a same-named sibling fallback's) and could
|
|
328
|
+
* falsely trip the stream limits. The stamp rides the same metadata
|
|
329
|
+
* rebuild that already attributes the serving provider.
|
|
330
|
+
*/
|
|
331
|
+
[STREAM_LIMIT_ATTEMPT_KEY]: ++streamLimitAttemptSeq
|
|
332
|
+
}
|
|
333
|
+
};
|
|
334
|
+
const rawLeaseTarget = params.context ?? params.streamLimitState;
|
|
335
|
+
/** No lease when no guard can fire: the lease only protects accounting
|
|
336
|
+
* entries, and fully disabled guards must allocate no bookkeeping at
|
|
337
|
+
* all — per-attempt included. */
|
|
338
|
+
const leaseTarget = rawLeaseTarget != null && streamLimitAccountingEnabled(rawLeaseTarget) ? rawLeaseTarget : void 0;
|
|
339
|
+
const generationKey = leaseTarget != null ? resolveGenerationKey(stampedConfig.metadata) : void 0;
|
|
340
|
+
if (leaseTarget != null && generationKey != null) registerActiveStreamLimitGeneration(leaseTarget, generationKey);
|
|
341
|
+
try {
|
|
342
|
+
return await attemptInvokeBody(params, stampedConfig);
|
|
343
|
+
} finally {
|
|
344
|
+
if (leaseTarget != null && generationKey != null) releaseStreamLimitGeneration(leaseTarget, generationKey);
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
async function attemptInvokeBody({ model, messages, provider, context, onChunk }, config) {
|
|
316
348
|
/**
|
|
317
349
|
* Pull the run-scoped tool output registry off the graph (when one
|
|
318
350
|
* exists) and project ToolMessages carrying ref metadata into a
|
|
@@ -327,7 +359,7 @@ async function attemptInvoke({ model, messages, provider, context, onChunk }, co
|
|
|
327
359
|
callOptions: config
|
|
328
360
|
});
|
|
329
361
|
const registry = context?.getOrCreateToolOutputRegistry();
|
|
330
|
-
const runId = config
|
|
362
|
+
const runId = config.configurable?.run_id;
|
|
331
363
|
const annotated = annotateMessagesForLLM(invocationMessages, registry, runId);
|
|
332
364
|
/**
|
|
333
365
|
* Keyed on the provider ACTUALLY serving this call, not the agent's primary.
|
|
@@ -370,13 +402,6 @@ async function attemptInvoke({ model, messages, provider, context, onChunk }, co
|
|
|
370
402
|
* wrong for fallback-served calls — or `ls_provider` — which derived
|
|
371
403
|
* providers inherit from their base class.
|
|
372
404
|
*/
|
|
373
|
-
config = {
|
|
374
|
-
...config,
|
|
375
|
-
metadata: {
|
|
376
|
-
...config?.metadata ?? {},
|
|
377
|
-
["__invoked_provider"]: provider
|
|
378
|
-
}
|
|
379
|
-
};
|
|
380
405
|
if (model.stream) {
|
|
381
406
|
/**
|
|
382
407
|
* Observed, not dictated. `handleChatModelStart` fires with the chat
|
|
@@ -396,24 +421,64 @@ async function attemptInvoke({ model, messages, provider, context, onChunk }, co
|
|
|
396
421
|
let finalChunk;
|
|
397
422
|
let preempted = false;
|
|
398
423
|
const registeredStreamHandler = getRegisteredDefaultChatStreamHandler(context);
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
424
|
+
/** A sibling's trip aborts the composed signal, but an adapter that
|
|
425
|
+
* ignores cancellation keeps yielding — and text-only chunks with the
|
|
426
|
+
* event cap off never throw in enforcement, so nothing else would stop
|
|
427
|
+
* the drain. Checked on every yielded chunk in all three loops;
|
|
428
|
+
* throwing closes the iterator and tears down the provider stream. */
|
|
429
|
+
const throwIfBreakerTripped = () => {
|
|
430
|
+
const signal = config.signal;
|
|
431
|
+
if (signal?.aborted === true && signal.reason instanceof StreamLimitExceededError) throw signal.reason;
|
|
432
|
+
};
|
|
433
|
+
if (onChunk) {
|
|
434
|
+
const attemptMetadata = config.metadata;
|
|
435
|
+
for await (const chunk of stream) {
|
|
436
|
+
throwIfBreakerTripped();
|
|
437
|
+
/** An onChunk consumer replaces the stream handler entirely, so
|
|
438
|
+
* stream limits are enforced here for every such caller — public
|
|
439
|
+
* package consumers get no other accounting. The internal
|
|
440
|
+
* summarization onChunk charges producer-side itself and passes no
|
|
441
|
+
* context, precisely so this claim and its own never stack. */
|
|
442
|
+
if (context != null) enforceStreamLimitsForWireChunk({
|
|
443
|
+
graph: context,
|
|
444
|
+
metadata: attemptMetadata,
|
|
445
|
+
chunk
|
|
446
|
+
});
|
|
447
|
+
await onChunk(chunk, attemptMetadata);
|
|
448
|
+
finalChunk = appendStreamChunk({
|
|
449
|
+
current: finalChunk,
|
|
450
|
+
next: chunk,
|
|
451
|
+
provider
|
|
452
|
+
});
|
|
453
|
+
}
|
|
454
|
+
} else if (registeredStreamHandler == null) {
|
|
408
455
|
const metadata = config.metadata;
|
|
409
456
|
const streamHandler = new ChatModelStreamHandler();
|
|
410
457
|
for await (const chunk of stream) {
|
|
458
|
+
throwIfBreakerTripped();
|
|
411
459
|
const handlingChunk = getStreamHandlingChunk({
|
|
412
460
|
current: finalChunk,
|
|
413
461
|
next: chunk,
|
|
414
462
|
provider
|
|
415
463
|
});
|
|
416
464
|
if (handlingChunk != null) await streamHandler.handle("on_chat_model_stream", { chunk: handlingChunk }, metadata, context);
|
|
465
|
+
else if (context != null)
|
|
466
|
+
/**
|
|
467
|
+
* A replay-skipped chunk yields no handling chunk, and in this
|
|
468
|
+
* local branch no `streamEvents` consumer judges the wire event
|
|
469
|
+
* either — yet a cumulative OpenRouter replay can still carry
|
|
470
|
+
* `tool_call_chunks` or complete `tool_calls` that are appended
|
|
471
|
+
* below. Charge the full limits (event budget and argument bytes)
|
|
472
|
+
* directly so neither cap can be bypassed. Consumer side: the
|
|
473
|
+
* local handler.handle call above claims as consumer, and one
|
|
474
|
+
* reused chunk object can alternate between these two arms.
|
|
475
|
+
*/
|
|
476
|
+
enforceStreamLimitsForWireChunk({
|
|
477
|
+
graph: context,
|
|
478
|
+
metadata,
|
|
479
|
+
chunk,
|
|
480
|
+
side: "consumer"
|
|
481
|
+
});
|
|
417
482
|
finalChunk = appendStreamChunk({
|
|
418
483
|
current: finalChunk,
|
|
419
484
|
next: chunk,
|
|
@@ -438,13 +503,40 @@ async function attemptInvoke({ model, messages, provider, context, onChunk }, co
|
|
|
438
503
|
}
|
|
439
504
|
} else {
|
|
440
505
|
const metadata = config.metadata;
|
|
506
|
+
/**
|
|
507
|
+
* The original wire chunk still reaches the registered handler through
|
|
508
|
+
* `streamEvents` (where the late-reasoning skip discards it AFTER the
|
|
509
|
+
* event guard counts it), so this inline re-dispatch of the transformed
|
|
510
|
+
* chunk is marked to not consume a second event-budget slot. Allocated
|
|
511
|
+
* once per attempt, only when a transformation occurs.
|
|
512
|
+
*/
|
|
513
|
+
let redispatchMetadata;
|
|
441
514
|
for await (const chunk of stream) {
|
|
515
|
+
throwIfBreakerTripped();
|
|
516
|
+
/**
|
|
517
|
+
* Charged synchronously, ahead of the decoupled `streamEvents`
|
|
518
|
+
* reader that will echo this same chunk to the registered handler:
|
|
519
|
+
* a lagging reader would otherwise let an oversized complete call
|
|
520
|
+
* return to LangGraph and reach ToolNode before the queued handler
|
|
521
|
+
* throws. The chunk is marked so the echo skips accounting.
|
|
522
|
+
*/
|
|
523
|
+
if (context != null) enforceStreamLimitsForWireChunk({
|
|
524
|
+
graph: context,
|
|
525
|
+
metadata,
|
|
526
|
+
chunk
|
|
527
|
+
});
|
|
442
528
|
const handlingChunk = getStreamHandlingChunk({
|
|
443
529
|
current: finalChunk,
|
|
444
530
|
next: chunk,
|
|
445
531
|
provider
|
|
446
532
|
});
|
|
447
|
-
if (handlingChunk != null && handlingChunk !== chunk)
|
|
533
|
+
if (handlingChunk != null && handlingChunk !== chunk) {
|
|
534
|
+
redispatchMetadata ??= {
|
|
535
|
+
...metadata ?? {},
|
|
536
|
+
[STREAM_LIMIT_REDISPATCH_KEY]: true
|
|
537
|
+
};
|
|
538
|
+
await registeredStreamHandler.handle("on_chat_model_stream", { chunk: handlingChunk }, redispatchMetadata, context);
|
|
539
|
+
}
|
|
448
540
|
finalChunk = appendStreamChunk({
|
|
449
541
|
current: finalChunk,
|
|
450
542
|
next: chunk,
|
|
@@ -505,7 +597,7 @@ function extractClientOptionsModel(clientOptions) {
|
|
|
505
597
|
* behind a later unrelated error would surface a dead end instead. Ordinary
|
|
506
598
|
* failures still throw last-error-wins.
|
|
507
599
|
*/
|
|
508
|
-
async function tryFallbackProviders({ fallbacks, tools, messages, config, primaryError, context, onChunk, overflowContext, prepareProviderMessages }) {
|
|
600
|
+
async function tryFallbackProviders({ fallbacks, tools, messages, config, primaryError, context, onChunk, streamLimitState, overflowContext, prepareProviderMessages }) {
|
|
509
601
|
const isOverflow = (error, contextOverride = overflowContext) => getContextOverflowInfo(error, contextOverride) != null;
|
|
510
602
|
let lastError = primaryError;
|
|
511
603
|
/**
|
|
@@ -537,21 +629,39 @@ async function tryFallbackProviders({ fallbacks, tools, messages, config, primar
|
|
|
537
629
|
["__invoked_model"]: fbModelName
|
|
538
630
|
}
|
|
539
631
|
};
|
|
632
|
+
const fallbackMessages = await prepareProviderMessages?.({
|
|
633
|
+
model: fbModel,
|
|
634
|
+
messages,
|
|
635
|
+
provider: fb.provider,
|
|
636
|
+
clientOptions: fb.clientOptions,
|
|
637
|
+
maxContextTokens: fb.maxContextTokens,
|
|
638
|
+
config: fbConfig
|
|
639
|
+
}) ?? messages;
|
|
640
|
+
/** A sibling can trip the breaker while the preparation above is
|
|
641
|
+
* awaited — and the catch below only sees attempts that THROW, so a
|
|
642
|
+
* provider that ignores an aborted signal and succeeds would resolve
|
|
643
|
+
* a run that must reject. Check before every fallback invocation. */
|
|
644
|
+
if (config?.signal?.aborted === true && config.signal.reason instanceof StreamLimitExceededError) throw config.signal.reason;
|
|
540
645
|
return await attemptInvoke({
|
|
541
646
|
model: fbModel,
|
|
542
|
-
messages:
|
|
543
|
-
model: fbModel,
|
|
544
|
-
messages,
|
|
545
|
-
provider: fb.provider,
|
|
546
|
-
clientOptions: fb.clientOptions,
|
|
547
|
-
maxContextTokens: fb.maxContextTokens,
|
|
548
|
-
config: fbConfig
|
|
549
|
-
}) ?? messages,
|
|
647
|
+
messages: fallbackMessages,
|
|
550
648
|
provider: fb.provider,
|
|
551
649
|
context,
|
|
552
|
-
onChunk
|
|
650
|
+
onChunk,
|
|
651
|
+
streamLimitState
|
|
553
652
|
}, fbConfig);
|
|
554
653
|
} catch (e) {
|
|
654
|
+
/**
|
|
655
|
+
* A tripped stream circuit breaker is a deliberate abort, not a
|
|
656
|
+
* provider failure. Continuing would try the remaining fallbacks and a
|
|
657
|
+
* succeeding one would resolve a run that must reject.
|
|
658
|
+
*/
|
|
659
|
+
if (e instanceof StreamLimitExceededError) throw e;
|
|
660
|
+
/** A parallel sibling's trip aborts this branch's composed signal, and
|
|
661
|
+
* a provider can surface that as a generic abort error; advancing to
|
|
662
|
+
* the next fallback would start new provider work after the safety
|
|
663
|
+
* abort. Rethrow the breaker's own reason instead. */
|
|
664
|
+
if (config?.signal?.aborted === true && config.signal.reason instanceof StreamLimitExceededError) throw config.signal.reason;
|
|
555
665
|
lastError = e;
|
|
556
666
|
if (isOverflow(e, {
|
|
557
667
|
provider: fb.provider,
|