@librechat/agents 3.2.68 → 3.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/agents/AgentContext.cjs +117 -3
- package/dist/cjs/agents/AgentContext.cjs.map +1 -1
- package/dist/cjs/common/enum.cjs +4 -0
- package/dist/cjs/common/enum.cjs.map +1 -1
- package/dist/cjs/graphs/Graph.cjs +243 -49
- package/dist/cjs/graphs/Graph.cjs.map +1 -1
- package/dist/cjs/graphs/MultiAgentGraph.cjs +103 -28
- package/dist/cjs/graphs/MultiAgentGraph.cjs.map +1 -1
- package/dist/cjs/langfuseToolOutputTracing.cjs +4 -0
- package/dist/cjs/langfuseToolOutputTracing.cjs.map +1 -1
- package/dist/cjs/llm/bedrock/index.cjs +11 -2
- package/dist/cjs/llm/bedrock/index.cjs.map +1 -1
- package/dist/cjs/llm/contextOverflowRecovery.cjs +132 -0
- package/dist/cjs/llm/contextOverflowRecovery.cjs.map +1 -0
- package/dist/cjs/llm/google/index.cjs +1 -1
- package/dist/cjs/llm/invoke.cjs +60 -3
- package/dist/cjs/llm/invoke.cjs.map +1 -1
- package/dist/cjs/llm/openai/index.cjs +1 -1
- package/dist/cjs/main.cjs +21 -7
- package/dist/cjs/messages/format.cjs +136 -4
- package/dist/cjs/messages/format.cjs.map +1 -1
- package/dist/cjs/messages/prune.cjs +16 -5
- package/dist/cjs/messages/prune.cjs.map +1 -1
- package/dist/cjs/messages/recency.cjs +2 -0
- package/dist/cjs/messages/recency.cjs.map +1 -1
- package/dist/cjs/prompts/activityLabel.cjs +101 -0
- package/dist/cjs/prompts/activityLabel.cjs.map +1 -0
- package/dist/cjs/run.cjs +177 -3
- package/dist/cjs/run.cjs.map +1 -1
- package/dist/cjs/stream.cjs +205 -49
- package/dist/cjs/stream.cjs.map +1 -1
- package/dist/cjs/summarization/node.cjs +55 -7
- package/dist/cjs/summarization/node.cjs.map +1 -1
- package/dist/cjs/tools/BashExecutor.cjs +2 -2
- package/dist/cjs/tools/BashExecutor.cjs.map +1 -1
- package/dist/cjs/tools/BashProgrammaticToolCalling.cjs +2 -2
- package/dist/cjs/tools/BashProgrammaticToolCalling.cjs.map +1 -1
- package/dist/cjs/tools/CodeExecutor.cjs +69 -8
- package/dist/cjs/tools/CodeExecutor.cjs.map +1 -1
- package/dist/cjs/tools/ProgrammaticToolCalling.cjs +19 -15
- package/dist/cjs/tools/ProgrammaticToolCalling.cjs.map +1 -1
- package/dist/cjs/tools/ToolNode.cjs +32 -3
- package/dist/cjs/tools/ToolNode.cjs.map +1 -1
- package/dist/cjs/tools/subagent/SubagentExecutor.cjs +1 -1
- package/dist/cjs/utils/errors.cjs +317 -52
- package/dist/cjs/utils/errors.cjs.map +1 -1
- package/dist/esm/agents/AgentContext.mjs +118 -4
- package/dist/esm/agents/AgentContext.mjs.map +1 -1
- package/dist/esm/common/enum.mjs +4 -0
- package/dist/esm/common/enum.mjs.map +1 -1
- package/dist/esm/graphs/Graph.mjs +242 -48
- package/dist/esm/graphs/Graph.mjs.map +1 -1
- package/dist/esm/graphs/MultiAgentGraph.mjs +103 -28
- package/dist/esm/graphs/MultiAgentGraph.mjs.map +1 -1
- package/dist/esm/langfuseToolOutputTracing.mjs +4 -1
- package/dist/esm/langfuseToolOutputTracing.mjs.map +1 -1
- package/dist/esm/llm/bedrock/index.mjs +11 -2
- package/dist/esm/llm/bedrock/index.mjs.map +1 -1
- package/dist/esm/llm/contextOverflowRecovery.mjs +130 -0
- package/dist/esm/llm/contextOverflowRecovery.mjs.map +1 -0
- package/dist/esm/llm/google/index.mjs +1 -1
- package/dist/esm/llm/invoke.mjs +59 -4
- package/dist/esm/llm/invoke.mjs.map +1 -1
- package/dist/esm/llm/openai/index.mjs +1 -1
- package/dist/esm/main.mjs +10 -10
- package/dist/esm/messages/format.mjs +136 -5
- package/dist/esm/messages/format.mjs.map +1 -1
- package/dist/esm/messages/prune.mjs +14 -6
- package/dist/esm/messages/prune.mjs.map +1 -1
- package/dist/esm/messages/recency.mjs +2 -1
- package/dist/esm/messages/recency.mjs.map +1 -1
- package/dist/esm/prompts/activityLabel.mjs +100 -0
- package/dist/esm/prompts/activityLabel.mjs.map +1 -0
- package/dist/esm/run.mjs +178 -4
- package/dist/esm/run.mjs.map +1 -1
- package/dist/esm/stream.mjs +205 -49
- package/dist/esm/stream.mjs.map +1 -1
- package/dist/esm/summarization/node.mjs +55 -7
- package/dist/esm/summarization/node.mjs.map +1 -1
- package/dist/esm/tools/BashExecutor.mjs +3 -3
- package/dist/esm/tools/BashExecutor.mjs.map +1 -1
- package/dist/esm/tools/BashProgrammaticToolCalling.mjs +3 -3
- package/dist/esm/tools/BashProgrammaticToolCalling.mjs.map +1 -1
- package/dist/esm/tools/CodeExecutor.mjs +62 -9
- package/dist/esm/tools/CodeExecutor.mjs.map +1 -1
- package/dist/esm/tools/ProgrammaticToolCalling.mjs +20 -16
- package/dist/esm/tools/ProgrammaticToolCalling.mjs.map +1 -1
- package/dist/esm/tools/ToolNode.mjs +32 -3
- package/dist/esm/tools/ToolNode.mjs.map +1 -1
- package/dist/esm/tools/subagent/SubagentExecutor.mjs +1 -1
- package/dist/esm/utils/errors.mjs +317 -53
- package/dist/esm/utils/errors.mjs.map +1 -1
- package/dist/types/agents/AgentContext.d.ts +62 -3
- package/dist/types/common/enum.d.ts +5 -1
- package/dist/types/graphs/Graph.d.ts +16 -2
- package/dist/types/graphs/MultiAgentGraph.d.ts +1 -0
- package/dist/types/langfuseToolOutputTracing.d.ts +4 -0
- package/dist/types/llm/contextOverflowRecovery.d.ts +85 -0
- package/dist/types/llm/invoke.d.ts +35 -6
- package/dist/types/messages/format.d.ts +22 -0
- package/dist/types/messages/prune.d.ts +10 -2
- package/dist/types/messages/recency.d.ts +1 -0
- package/dist/types/prompts/activityLabel.d.ts +31 -0
- package/dist/types/run.d.ts +16 -0
- package/dist/types/tools/CodeExecutor.d.ts +14 -1
- package/dist/types/types/activityLabel.d.ts +53 -0
- package/dist/types/types/index.d.ts +1 -0
- package/dist/types/types/llm.d.ts +7 -4
- package/dist/types/types/stream.d.ts +7 -4
- package/dist/types/types/summarize.d.ts +22 -0
- package/dist/types/utils/__tests__/fixtures/contextOverflowSignatures.d.ts +40 -0
- package/dist/types/utils/errors.d.ts +65 -16
- package/dist/types/utils/redactSecrets.d.ts +3 -0
- package/package.json +7 -8
- package/src/agents/AgentContext.ts +188 -7
- package/src/agents/__tests__/AgentContext.overflow.test.ts +205 -0
- package/src/common/enum.ts +4 -0
- package/src/graphs/Graph.ts +409 -58
- package/src/graphs/MultiAgentGraph.ts +184 -46
- package/src/graphs/__tests__/Graph.contextOverflow.test.ts +631 -0
- package/src/langfuseToolOutputTracing.ts +4 -1
- package/src/llm/__tests__/contextOverflowRecovery.test.ts +401 -0
- package/src/llm/__tests__/fallbackOverflow.test.ts +287 -0
- package/src/llm/bedrock/index.ts +25 -12
- package/src/llm/contextOverflowRecovery.ts +292 -0
- package/src/llm/invoke.ts +119 -4
- package/src/messages/foldToollessToolBlocks.test.ts +438 -0
- package/src/messages/format.ts +233 -5
- package/src/messages/prune.ts +24 -11
- package/src/messages/recency.ts +3 -1
- package/src/prompts/activityLabel.ts +177 -0
- package/src/run.ts +322 -3
- package/src/scripts/context-overflow-probe.ts +997 -0
- package/src/specs/activity-label-prompt.test.ts +128 -0
- package/src/specs/activity-label-trace-seed.test.ts +47 -0
- package/src/specs/agent-handoffs.test.ts +903 -1
- package/src/specs/bedrock-toolless.live.test.ts +123 -0
- package/src/specs/context-overflow-recovery.live.test.ts +213 -0
- package/src/splitStream.test.ts +882 -0
- package/src/stream.ts +315 -51
- package/src/summarization/__tests__/aggregator.test.ts +83 -0
- package/src/summarization/__tests__/node.test.ts +139 -0
- package/src/summarization/node.ts +99 -14
- package/src/tools/BashExecutor.ts +4 -2
- package/src/tools/BashProgrammaticToolCalling.ts +4 -7
- package/src/tools/CodeExecutor.ts +119 -8
- package/src/tools/ProgrammaticToolCalling.ts +29 -27
- package/src/tools/ToolNode.ts +50 -8
- package/src/tools/__tests__/CodeApiAuthHeaders.test.ts +297 -3
- package/src/types/activityLabel.ts +55 -0
- package/src/types/index.ts +1 -0
- package/src/types/llm.ts +8 -1
- package/src/types/stream.ts +7 -4
- package/src/types/summarize.ts +22 -0
- package/src/utils/__tests__/errors.test.ts +270 -0
- package/src/utils/__tests__/fixtures/contextOverflowSignatures.ts +336 -0
- package/src/utils/__tests__/redactSecrets.test.ts +56 -0
- package/src/utils/errors.ts +484 -66
- package/src/utils/redactSecrets.ts +61 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"langfuseToolOutputTracing.cjs","names":["normalizeToolName","LangfuseOtelSpanAttributes","LangfuseSpanProcessor","shouldDropLangfuseSpan","resolveToolOutputTracingConfigForSpan","hasToolOutputTracingConfig","resolveToolOutputTracingConfig","resolveLangfuseConfig"],"sources":["../../src/langfuseToolOutputTracing.ts"],"sourcesContent":["import { LangfuseSpanProcessor } from '@langfuse/otel';\nimport { LangfuseOtelSpanAttributes } from '@langfuse/tracing';\nimport type {\n ReadableSpan,\n Span,\n SpanProcessor,\n} from '@opentelemetry/sdk-trace-base';\nimport type { LangfuseSpanProcessorParams } from '@langfuse/otel';\nimport type { Context } from '@opentelemetry/api';\nimport type { ResolvedLangfuseToolOutputTracingConfig } from '@/langfuseRuntimeContext';\nimport type * as t from '@/types';\nimport {\n LANGFUSE_TOOL_OUTPUT_REDACTION_TEXT,\n hasToolOutputTracingConfig,\n normalizeToolName,\n resolveLangfuseConfig,\n resolveToolOutputTracingConfig,\n} from '@/langfuseConfig';\nimport {\n shapeLangfuseSpan,\n shouldDropLangfuseSpan,\n} from '@/langfuseTraceShaping';\nimport { resolveToolOutputTracingConfigForSpan } from '@/langfuseRuntimeScope';\n\nexport { LANGFUSE_TOOL_OUTPUT_REDACTION_TEXT, resolveLangfuseConfig };\n\nconst LANGGRAPH_TOOL_NODE_PREFIX = 'tools=';\n\nconst CHAT_ROLES = new Set([\n 'assistant',\n 'developer',\n 'human',\n 'system',\n 'user',\n]);\n\ntype SpanWithAttributes = ReadableSpan & {\n attributes: Record<string, unknown>;\n};\n\ntype RedactionResult = {\n value: unknown;\n changed: boolean;\n};\n\ntype RedactionContext = {\n toolNamesByCallId: Map<string, string>;\n};\n\nconst TOOL_OUTPUT_FIELD_KEYS = ['content', 'artifact'];\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return value != null && typeof value === 'object' && !Array.isArray(value);\n}\n\nfunction isPresent(value: unknown): value is string {\n return typeof value === 'string' && value.trim() !== '';\n}\n\nfunction shouldApplyToolOutputRedaction(\n config: ResolvedLangfuseToolOutputTracingConfig\n): boolean {\n return config.enabled === false || config.redactedToolNames.size > 0;\n}\n\nfunction toolNameMatches(\n toolName: string | undefined,\n config: ResolvedLangfuseToolOutputTracingConfig\n): boolean {\n if (!isPresent(toolName)) {\n return false;\n }\n\n const normalizedToolName = normalizeToolName(toolName);\n if (config.redactedToolNameMatchMode === 'partial') {\n for (const redactedToolName of config.redactedToolNames) {\n if (normalizedToolName.includes(redactedToolName)) {\n return true;\n }\n }\n return false;\n }\n\n return config.redactedToolNames.has(normalizedToolName);\n}\n\nfunction shouldRedactTool(\n toolName: string | undefined,\n config: ResolvedLangfuseToolOutputTracingConfig\n): boolean {\n return config.enabled === false || toolNameMatches(toolName, config);\n}\n\nfunction getStringField(\n value: Record<string, unknown>,\n key: string\n): string | undefined {\n const field = value[key];\n return typeof field === 'string' ? field : undefined;\n}\n\nfunction getNestedStringField(\n value: Record<string, unknown>,\n objectKey: string,\n fieldKey: string\n): string | undefined {\n const nested = value[objectKey];\n if (!isRecord(nested)) {\n return undefined;\n }\n return getStringField(nested, fieldKey);\n}\n\nfunction getSerializedToolCallId(\n value: Record<string, unknown>\n): string | undefined {\n return (\n getStringField(value, 'tool_call_id') ??\n getNestedStringField(value, 'kwargs', 'tool_call_id') ??\n getNestedStringField(value, 'additional_kwargs', 'tool_call_id') ??\n getNestedStringField(value, 'data', 'tool_call_id') ??\n (typeof value.id === 'string' ? value.id : undefined)\n );\n}\n\nfunction getSerializedToolName(\n value: Record<string, unknown>,\n redactionContext?: RedactionContext\n): string | undefined {\n const role = getStringField(value, 'role');\n const explicitName =\n getStringField(value, 'name') ??\n getStringField(value, 'tool_name') ??\n getNestedStringField(value, 'function', 'name') ??\n getNestedStringField(value, 'kwargs', 'name') ??\n getNestedStringField(value, 'additional_kwargs', 'name') ??\n getNestedStringField(value, 'data', 'name') ??\n (role != null && role.toLowerCase() !== 'tool' ? role : undefined);\n\n if (explicitName != null) {\n return explicitName;\n }\n\n const toolCallId = getSerializedToolCallId(value);\n return toolCallId != null\n ? redactionContext?.toolNamesByCallId.get(toolCallId)\n : undefined;\n}\n\nfunction hasToolMessageIdentity(value: Record<string, unknown>): boolean {\n const type = getStringField(value, 'type') ?? getStringField(value, '_type');\n if (type === 'tool' || type === 'tool_message') {\n return true;\n }\n\n const id = value.id;\n if (\n Array.isArray(id) &&\n id.some((part) => typeof part === 'string' && part.includes('ToolMessage'))\n ) {\n return true;\n }\n\n if (\n 'tool_call_id' in value ||\n getNestedStringField(value, 'kwargs', 'tool_call_id') != null ||\n getNestedStringField(value, 'additional_kwargs', 'tool_call_id') != null\n ) {\n return true;\n }\n\n const role = getStringField(value, 'role');\n return (\n role != null &&\n !CHAT_ROLES.has(role.toLowerCase()) &&\n ('content' in value || isRecord(value.kwargs) || isRecord(value.data))\n );\n}\n\nfunction redactToolContentFields(\n value: Record<string, unknown>,\n config: ResolvedLangfuseToolOutputTracingConfig\n): Record<string, unknown> {\n const next = { ...value };\n\n for (const outputKey of TOOL_OUTPUT_FIELD_KEYS) {\n if (outputKey in next) {\n next[outputKey] = config.redactionText;\n }\n }\n\n for (const nestedKey of ['kwargs', 'data', 'additional_kwargs']) {\n const nested = next[nestedKey];\n if (!isRecord(nested)) {\n continue;\n }\n const nextNested = { ...nested };\n let changed = false;\n for (const outputKey of TOOL_OUTPUT_FIELD_KEYS) {\n if (outputKey in nextNested) {\n nextNested[outputKey] = config.redactionText;\n changed = true;\n }\n }\n if (changed) {\n next[nestedKey] = nextNested;\n }\n }\n\n return next;\n}\n\nfunction collectToolCallNames(\n value: unknown,\n redactionContext: RedactionContext\n): void {\n if (Array.isArray(value)) {\n for (const item of value) {\n collectToolCallNames(item, redactionContext);\n }\n return;\n }\n\n if (!isRecord(value)) {\n return;\n }\n\n const toolCallId = getSerializedToolCallId(value);\n const toolName = getSerializedToolName(value);\n if (toolCallId != null && toolName != null) {\n redactionContext.toolNamesByCallId.set(toolCallId, toolName);\n }\n\n for (const child of Object.values(value)) {\n collectToolCallNames(child, redactionContext);\n }\n}\n\nfunction redactValue(\n value: unknown,\n config: ResolvedLangfuseToolOutputTracingConfig,\n redactionContext: RedactionContext\n): RedactionResult {\n if (Array.isArray(value)) {\n let changed = false;\n const next: unknown[] = [];\n for (const item of value) {\n const result = redactValue(item, config, redactionContext);\n if (result.changed) {\n changed = true;\n }\n next.push(result.value);\n }\n return changed ? { value: next, changed } : { value, changed };\n }\n\n if (!isRecord(value)) {\n return { value, changed: false };\n }\n\n const toolName = getSerializedToolName(value, redactionContext);\n if (hasToolMessageIdentity(value) && shouldRedactTool(toolName, config)) {\n return {\n value: redactToolContentFields(value, config),\n changed: true,\n };\n }\n\n let changed = false;\n const next: Record<string, unknown> = {};\n for (const [key, child] of Object.entries(value)) {\n const result = redactValue(child, config, redactionContext);\n if (result.changed) {\n changed = true;\n }\n next[key] = result.value;\n }\n\n return changed ? { value: next, changed } : { value, changed };\n}\n\nfunction redactSerializedValue(\n value: unknown,\n config: ResolvedLangfuseToolOutputTracingConfig\n): RedactionResult {\n const redactionContext: RedactionContext = {\n toolNamesByCallId: new Map(),\n };\n if (typeof value !== 'string') {\n collectToolCallNames(value, redactionContext);\n return redactValue(value, config, redactionContext);\n }\n\n const trimmed = value.trim();\n if (!trimmed.startsWith('{') && !trimmed.startsWith('[')) {\n return { value, changed: false };\n }\n\n try {\n const parsed = JSON.parse(value) as unknown;\n collectToolCallNames(parsed, redactionContext);\n const result = redactValue(parsed, config, redactionContext);\n return result.changed\n ? { value: JSON.stringify(result.value), changed: true }\n : { value, changed: false };\n } catch {\n return { value, changed: false };\n }\n}\n\nfunction redactAttribute(\n attributes: Record<string, unknown>,\n key: string,\n config: ResolvedLangfuseToolOutputTracingConfig\n): void {\n if (!(key in attributes)) {\n return;\n }\n\n const result = redactSerializedValue(attributes[key], config);\n if (result.changed) {\n attributes[key] = result.value;\n }\n}\n\nfunction isToolObservation(attributes: Record<string, unknown>): boolean {\n const type = attributes[LangfuseOtelSpanAttributes.OBSERVATION_TYPE];\n return typeof type === 'string' && type.toLowerCase() === 'tool';\n}\n\nfunction classifyLangGraphToolNodeSpan(\n attributes: Record<string, unknown>\n): void {\n const type = attributes[LangfuseOtelSpanAttributes.OBSERVATION_TYPE];\n if (typeof type !== 'string' || type.toLowerCase() !== 'span') {\n return;\n }\n\n const langGraphNode =\n attributes[\n `${LangfuseOtelSpanAttributes.OBSERVATION_METADATA}.langgraph_node`\n ];\n if (\n typeof langGraphNode === 'string' &&\n langGraphNode.startsWith(LANGGRAPH_TOOL_NODE_PREFIX)\n ) {\n attributes[LangfuseOtelSpanAttributes.OBSERVATION_TYPE] = 'tool';\n }\n}\n\nexport function classifyLangfuseToolNodeSpan(span: ReadableSpan): void {\n classifyLangGraphToolNodeSpan((span as SpanWithAttributes).attributes);\n}\n\nfunction redactToolObservationOutput(\n span: ReadableSpan,\n attributes: Record<string, unknown>,\n config: ResolvedLangfuseToolOutputTracingConfig\n): void {\n if (\n !(\n isToolObservation(attributes) &&\n shouldRedactTool(span.name, config) &&\n LangfuseOtelSpanAttributes.OBSERVATION_OUTPUT in attributes\n )\n ) {\n return;\n }\n\n attributes[LangfuseOtelSpanAttributes.OBSERVATION_OUTPUT] =\n config.redactionText;\n}\n\nexport function redactLangfuseSpanToolOutputs(\n span: ReadableSpan,\n config: ResolvedLangfuseToolOutputTracingConfig\n): void {\n const attributes = (span as SpanWithAttributes).attributes;\n classifyLangfuseToolNodeSpan(span);\n\n if (!shouldApplyToolOutputRedaction(config)) {\n return;\n }\n\n redactToolObservationOutput(span, attributes, config);\n\n for (const key of [\n LangfuseOtelSpanAttributes.OBSERVATION_INPUT,\n LangfuseOtelSpanAttributes.OBSERVATION_OUTPUT,\n LangfuseOtelSpanAttributes.TRACE_INPUT,\n LangfuseOtelSpanAttributes.TRACE_OUTPUT,\n ]) {\n redactAttribute(attributes, key, config);\n }\n}\n\nclass ToolOutputRedactingLangfuseSpanProcessor implements SpanProcessor {\n private readonly processor: LangfuseSpanProcessor;\n private readonly fallbackConfig?: ResolvedLangfuseToolOutputTracingConfig;\n private readonly spanConfigs = new WeakMap<\n object,\n ResolvedLangfuseToolOutputTracingConfig\n >();\n\n constructor(\n params?: LangfuseSpanProcessorParams,\n fallbackConfig?: ResolvedLangfuseToolOutputTracingConfig\n ) {\n this.processor = new LangfuseSpanProcessor(params);\n this.fallbackConfig = fallbackConfig;\n }\n\n onStart(span: Span, parentContext: Context): void {\n if (shouldDropLangfuseSpan(span.name)) {\n return;\n }\n const config =\n resolveToolOutputTracingConfigForSpan(parentContext) ??\n this.fallbackConfig;\n if (config != null) {\n this.spanConfigs.set(span, config);\n }\n this.processor.onStart(span, parentContext);\n }\n\n onEnd(span: ReadableSpan): void {\n if (shouldDropLangfuseSpan(span.name)) {\n return;\n }\n classifyLangfuseToolNodeSpan(span);\n shapeLangfuseSpan(span);\n const config = this.spanConfigs.get(span) ?? this.fallbackConfig;\n if (config != null) {\n redactLangfuseSpanToolOutputs(span, config);\n }\n this.processor.onEnd(span);\n }\n\n forceFlush(): Promise<void> {\n return this.processor.forceFlush();\n }\n\n shutdown(): Promise<void> {\n return this.processor.shutdown();\n }\n}\n\nexport function createLangfuseSpanProcessor(\n params?: LangfuseSpanProcessorParams,\n runLangfuse?: t.LangfuseConfig,\n agentLangfuse?: t.LangfuseConfig\n): SpanProcessor {\n const fallbackConfig = hasToolOutputTracingConfig(runLangfuse, agentLangfuse)\n ? resolveToolOutputTracingConfig(runLangfuse, agentLangfuse)\n : undefined;\n return new ToolOutputRedactingLangfuseSpanProcessor(params, fallbackConfig);\n}\n\nfunction hasLangfuseEnvKeys(): boolean {\n return (\n isPresent(process.env.LANGFUSE_SECRET_KEY) &&\n isPresent(process.env.LANGFUSE_PUBLIC_KEY)\n );\n}\n\nfunction hasLangfuseConfigKeys(langfuse?: t.LangfuseConfig): boolean {\n if (langfuse == null) {\n return false;\n }\n return isPresent(langfuse.secretKey) && isPresent(langfuse.publicKey);\n}\n\nexport function shouldTraceToolNodeForLangfuse({\n runLangfuse,\n agentLangfuse,\n}: {\n runLangfuse?: t.LangfuseConfig;\n agentLangfuse?: t.LangfuseConfig;\n}): boolean {\n const langfuse = resolveLangfuseConfig(runLangfuse, agentLangfuse);\n if (langfuse?.enabled === false) {\n return false;\n }\n\n const explicit = langfuse?.toolNodeTracing?.enabled;\n if (explicit !== true) {\n return false;\n }\n\n return hasLangfuseConfigKeys(langfuse) || hasLangfuseEnvKeys();\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"langfuseToolOutputTracing.cjs","names":["normalizeToolName","LangfuseOtelSpanAttributes","LangfuseSpanProcessor","shouldDropLangfuseSpan","resolveToolOutputTracingConfigForSpan","hasToolOutputTracingConfig","resolveToolOutputTracingConfig","resolveLangfuseConfig"],"sources":["../../src/langfuseToolOutputTracing.ts"],"sourcesContent":["import { LangfuseSpanProcessor } from '@langfuse/otel';\nimport { LangfuseOtelSpanAttributes } from '@langfuse/tracing';\nimport type {\n ReadableSpan,\n Span,\n SpanProcessor,\n} from '@opentelemetry/sdk-trace-base';\nimport type { LangfuseSpanProcessorParams } from '@langfuse/otel';\nimport type { Context } from '@opentelemetry/api';\nimport type { ResolvedLangfuseToolOutputTracingConfig } from '@/langfuseRuntimeContext';\nimport type * as t from '@/types';\nimport {\n LANGFUSE_TOOL_OUTPUT_REDACTION_TEXT,\n hasToolOutputTracingConfig,\n normalizeToolName,\n resolveLangfuseConfig,\n resolveToolOutputTracingConfig,\n} from '@/langfuseConfig';\nimport {\n shapeLangfuseSpan,\n shouldDropLangfuseSpan,\n} from '@/langfuseTraceShaping';\nimport { resolveToolOutputTracingConfigForSpan } from '@/langfuseRuntimeScope';\n\nexport { LANGFUSE_TOOL_OUTPUT_REDACTION_TEXT, resolveLangfuseConfig };\n\nconst LANGGRAPH_TOOL_NODE_PREFIX = 'tools=';\n\nconst CHAT_ROLES = new Set([\n 'assistant',\n 'developer',\n 'human',\n 'system',\n 'user',\n]);\n\ntype SpanWithAttributes = ReadableSpan & {\n attributes: Record<string, unknown>;\n};\n\ntype RedactionResult = {\n value: unknown;\n changed: boolean;\n};\n\ntype RedactionContext = {\n toolNamesByCallId: Map<string, string>;\n};\n\nconst TOOL_OUTPUT_FIELD_KEYS = ['content', 'artifact'];\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return value != null && typeof value === 'object' && !Array.isArray(value);\n}\n\nfunction isPresent(value: unknown): value is string {\n return typeof value === 'string' && value.trim() !== '';\n}\n\nfunction shouldApplyToolOutputRedaction(\n config: ResolvedLangfuseToolOutputTracingConfig\n): boolean {\n return config.enabled === false || config.redactedToolNames.size > 0;\n}\n\nfunction toolNameMatches(\n toolName: string | undefined,\n config: ResolvedLangfuseToolOutputTracingConfig\n): boolean {\n if (!isPresent(toolName)) {\n return false;\n }\n\n const normalizedToolName = normalizeToolName(toolName);\n if (config.redactedToolNameMatchMode === 'partial') {\n for (const redactedToolName of config.redactedToolNames) {\n if (normalizedToolName.includes(redactedToolName)) {\n return true;\n }\n }\n return false;\n }\n\n return config.redactedToolNames.has(normalizedToolName);\n}\n\n/** Whether a tool's outputs are excluded from tracing (global disable or\n * `redactedToolNames` match). Exported for the activity-label prompt\n * builder, whose prompt becomes Langfuse generation input. */\nexport function shouldRedactTool(\n toolName: string | undefined,\n config: ResolvedLangfuseToolOutputTracingConfig\n): boolean {\n return config.enabled === false || toolNameMatches(toolName, config);\n}\n\nfunction getStringField(\n value: Record<string, unknown>,\n key: string\n): string | undefined {\n const field = value[key];\n return typeof field === 'string' ? field : undefined;\n}\n\nfunction getNestedStringField(\n value: Record<string, unknown>,\n objectKey: string,\n fieldKey: string\n): string | undefined {\n const nested = value[objectKey];\n if (!isRecord(nested)) {\n return undefined;\n }\n return getStringField(nested, fieldKey);\n}\n\nfunction getSerializedToolCallId(\n value: Record<string, unknown>\n): string | undefined {\n return (\n getStringField(value, 'tool_call_id') ??\n getNestedStringField(value, 'kwargs', 'tool_call_id') ??\n getNestedStringField(value, 'additional_kwargs', 'tool_call_id') ??\n getNestedStringField(value, 'data', 'tool_call_id') ??\n (typeof value.id === 'string' ? value.id : undefined)\n );\n}\n\nfunction getSerializedToolName(\n value: Record<string, unknown>,\n redactionContext?: RedactionContext\n): string | undefined {\n const role = getStringField(value, 'role');\n const explicitName =\n getStringField(value, 'name') ??\n getStringField(value, 'tool_name') ??\n getNestedStringField(value, 'function', 'name') ??\n getNestedStringField(value, 'kwargs', 'name') ??\n getNestedStringField(value, 'additional_kwargs', 'name') ??\n getNestedStringField(value, 'data', 'name') ??\n (role != null && role.toLowerCase() !== 'tool' ? role : undefined);\n\n if (explicitName != null) {\n return explicitName;\n }\n\n const toolCallId = getSerializedToolCallId(value);\n return toolCallId != null\n ? redactionContext?.toolNamesByCallId.get(toolCallId)\n : undefined;\n}\n\nfunction hasToolMessageIdentity(value: Record<string, unknown>): boolean {\n const type = getStringField(value, 'type') ?? getStringField(value, '_type');\n if (type === 'tool' || type === 'tool_message') {\n return true;\n }\n\n const id = value.id;\n if (\n Array.isArray(id) &&\n id.some((part) => typeof part === 'string' && part.includes('ToolMessage'))\n ) {\n return true;\n }\n\n if (\n 'tool_call_id' in value ||\n getNestedStringField(value, 'kwargs', 'tool_call_id') != null ||\n getNestedStringField(value, 'additional_kwargs', 'tool_call_id') != null\n ) {\n return true;\n }\n\n const role = getStringField(value, 'role');\n return (\n role != null &&\n !CHAT_ROLES.has(role.toLowerCase()) &&\n ('content' in value || isRecord(value.kwargs) || isRecord(value.data))\n );\n}\n\nfunction redactToolContentFields(\n value: Record<string, unknown>,\n config: ResolvedLangfuseToolOutputTracingConfig\n): Record<string, unknown> {\n const next = { ...value };\n\n for (const outputKey of TOOL_OUTPUT_FIELD_KEYS) {\n if (outputKey in next) {\n next[outputKey] = config.redactionText;\n }\n }\n\n for (const nestedKey of ['kwargs', 'data', 'additional_kwargs']) {\n const nested = next[nestedKey];\n if (!isRecord(nested)) {\n continue;\n }\n const nextNested = { ...nested };\n let changed = false;\n for (const outputKey of TOOL_OUTPUT_FIELD_KEYS) {\n if (outputKey in nextNested) {\n nextNested[outputKey] = config.redactionText;\n changed = true;\n }\n }\n if (changed) {\n next[nestedKey] = nextNested;\n }\n }\n\n return next;\n}\n\nfunction collectToolCallNames(\n value: unknown,\n redactionContext: RedactionContext\n): void {\n if (Array.isArray(value)) {\n for (const item of value) {\n collectToolCallNames(item, redactionContext);\n }\n return;\n }\n\n if (!isRecord(value)) {\n return;\n }\n\n const toolCallId = getSerializedToolCallId(value);\n const toolName = getSerializedToolName(value);\n if (toolCallId != null && toolName != null) {\n redactionContext.toolNamesByCallId.set(toolCallId, toolName);\n }\n\n for (const child of Object.values(value)) {\n collectToolCallNames(child, redactionContext);\n }\n}\n\nfunction redactValue(\n value: unknown,\n config: ResolvedLangfuseToolOutputTracingConfig,\n redactionContext: RedactionContext\n): RedactionResult {\n if (Array.isArray(value)) {\n let changed = false;\n const next: unknown[] = [];\n for (const item of value) {\n const result = redactValue(item, config, redactionContext);\n if (result.changed) {\n changed = true;\n }\n next.push(result.value);\n }\n return changed ? { value: next, changed } : { value, changed };\n }\n\n if (!isRecord(value)) {\n return { value, changed: false };\n }\n\n const toolName = getSerializedToolName(value, redactionContext);\n if (hasToolMessageIdentity(value) && shouldRedactTool(toolName, config)) {\n return {\n value: redactToolContentFields(value, config),\n changed: true,\n };\n }\n\n let changed = false;\n const next: Record<string, unknown> = {};\n for (const [key, child] of Object.entries(value)) {\n const result = redactValue(child, config, redactionContext);\n if (result.changed) {\n changed = true;\n }\n next[key] = result.value;\n }\n\n return changed ? { value: next, changed } : { value, changed };\n}\n\nfunction redactSerializedValue(\n value: unknown,\n config: ResolvedLangfuseToolOutputTracingConfig\n): RedactionResult {\n const redactionContext: RedactionContext = {\n toolNamesByCallId: new Map(),\n };\n if (typeof value !== 'string') {\n collectToolCallNames(value, redactionContext);\n return redactValue(value, config, redactionContext);\n }\n\n const trimmed = value.trim();\n if (!trimmed.startsWith('{') && !trimmed.startsWith('[')) {\n return { value, changed: false };\n }\n\n try {\n const parsed = JSON.parse(value) as unknown;\n collectToolCallNames(parsed, redactionContext);\n const result = redactValue(parsed, config, redactionContext);\n return result.changed\n ? { value: JSON.stringify(result.value), changed: true }\n : { value, changed: false };\n } catch {\n return { value, changed: false };\n }\n}\n\nfunction redactAttribute(\n attributes: Record<string, unknown>,\n key: string,\n config: ResolvedLangfuseToolOutputTracingConfig\n): void {\n if (!(key in attributes)) {\n return;\n }\n\n const result = redactSerializedValue(attributes[key], config);\n if (result.changed) {\n attributes[key] = result.value;\n }\n}\n\nfunction isToolObservation(attributes: Record<string, unknown>): boolean {\n const type = attributes[LangfuseOtelSpanAttributes.OBSERVATION_TYPE];\n return typeof type === 'string' && type.toLowerCase() === 'tool';\n}\n\nfunction classifyLangGraphToolNodeSpan(\n attributes: Record<string, unknown>\n): void {\n const type = attributes[LangfuseOtelSpanAttributes.OBSERVATION_TYPE];\n if (typeof type !== 'string' || type.toLowerCase() !== 'span') {\n return;\n }\n\n const langGraphNode =\n attributes[\n `${LangfuseOtelSpanAttributes.OBSERVATION_METADATA}.langgraph_node`\n ];\n if (\n typeof langGraphNode === 'string' &&\n langGraphNode.startsWith(LANGGRAPH_TOOL_NODE_PREFIX)\n ) {\n attributes[LangfuseOtelSpanAttributes.OBSERVATION_TYPE] = 'tool';\n }\n}\n\nexport function classifyLangfuseToolNodeSpan(span: ReadableSpan): void {\n classifyLangGraphToolNodeSpan((span as SpanWithAttributes).attributes);\n}\n\nfunction redactToolObservationOutput(\n span: ReadableSpan,\n attributes: Record<string, unknown>,\n config: ResolvedLangfuseToolOutputTracingConfig\n): void {\n if (\n !(\n isToolObservation(attributes) &&\n shouldRedactTool(span.name, config) &&\n LangfuseOtelSpanAttributes.OBSERVATION_OUTPUT in attributes\n )\n ) {\n return;\n }\n\n attributes[LangfuseOtelSpanAttributes.OBSERVATION_OUTPUT] =\n config.redactionText;\n}\n\nexport function redactLangfuseSpanToolOutputs(\n span: ReadableSpan,\n config: ResolvedLangfuseToolOutputTracingConfig\n): void {\n const attributes = (span as SpanWithAttributes).attributes;\n classifyLangfuseToolNodeSpan(span);\n\n if (!shouldApplyToolOutputRedaction(config)) {\n return;\n }\n\n redactToolObservationOutput(span, attributes, config);\n\n for (const key of [\n LangfuseOtelSpanAttributes.OBSERVATION_INPUT,\n LangfuseOtelSpanAttributes.OBSERVATION_OUTPUT,\n LangfuseOtelSpanAttributes.TRACE_INPUT,\n LangfuseOtelSpanAttributes.TRACE_OUTPUT,\n ]) {\n redactAttribute(attributes, key, config);\n }\n}\n\nclass ToolOutputRedactingLangfuseSpanProcessor implements SpanProcessor {\n private readonly processor: LangfuseSpanProcessor;\n private readonly fallbackConfig?: ResolvedLangfuseToolOutputTracingConfig;\n private readonly spanConfigs = new WeakMap<\n object,\n ResolvedLangfuseToolOutputTracingConfig\n >();\n\n constructor(\n params?: LangfuseSpanProcessorParams,\n fallbackConfig?: ResolvedLangfuseToolOutputTracingConfig\n ) {\n this.processor = new LangfuseSpanProcessor(params);\n this.fallbackConfig = fallbackConfig;\n }\n\n onStart(span: Span, parentContext: Context): void {\n if (shouldDropLangfuseSpan(span.name)) {\n return;\n }\n const config =\n resolveToolOutputTracingConfigForSpan(parentContext) ??\n this.fallbackConfig;\n if (config != null) {\n this.spanConfigs.set(span, config);\n }\n this.processor.onStart(span, parentContext);\n }\n\n onEnd(span: ReadableSpan): void {\n if (shouldDropLangfuseSpan(span.name)) {\n return;\n }\n classifyLangfuseToolNodeSpan(span);\n shapeLangfuseSpan(span);\n const config = this.spanConfigs.get(span) ?? this.fallbackConfig;\n if (config != null) {\n redactLangfuseSpanToolOutputs(span, config);\n }\n this.processor.onEnd(span);\n }\n\n forceFlush(): Promise<void> {\n return this.processor.forceFlush();\n }\n\n shutdown(): Promise<void> {\n return this.processor.shutdown();\n }\n}\n\nexport function createLangfuseSpanProcessor(\n params?: LangfuseSpanProcessorParams,\n runLangfuse?: t.LangfuseConfig,\n agentLangfuse?: t.LangfuseConfig\n): SpanProcessor {\n const fallbackConfig = hasToolOutputTracingConfig(runLangfuse, agentLangfuse)\n ? resolveToolOutputTracingConfig(runLangfuse, agentLangfuse)\n : undefined;\n return new ToolOutputRedactingLangfuseSpanProcessor(params, fallbackConfig);\n}\n\nfunction hasLangfuseEnvKeys(): boolean {\n return (\n isPresent(process.env.LANGFUSE_SECRET_KEY) &&\n isPresent(process.env.LANGFUSE_PUBLIC_KEY)\n );\n}\n\nfunction hasLangfuseConfigKeys(langfuse?: t.LangfuseConfig): boolean {\n if (langfuse == null) {\n return false;\n }\n return isPresent(langfuse.secretKey) && isPresent(langfuse.publicKey);\n}\n\nexport function shouldTraceToolNodeForLangfuse({\n runLangfuse,\n agentLangfuse,\n}: {\n runLangfuse?: t.LangfuseConfig;\n agentLangfuse?: t.LangfuseConfig;\n}): boolean {\n const langfuse = resolveLangfuseConfig(runLangfuse, agentLangfuse);\n if (langfuse?.enabled === false) {\n return false;\n }\n\n const explicit = langfuse?.toolNodeTracing?.enabled;\n if (explicit !== true) {\n return false;\n }\n\n return hasLangfuseConfigKeys(langfuse) || hasLangfuseEnvKeys();\n}\n"],"mappings":";;;;;;AA0BA,MAAM,6BAA6B;AAEnC,MAAM,aAAa,IAAI,IAAI;CACzB;CACA;CACA;CACA;CACA;AACF,CAAC;AAeD,MAAM,yBAAyB,CAAC,WAAW,UAAU;AAErD,SAAS,SAAS,OAAkD;CAClE,OAAO,SAAS,QAAQ,OAAO,UAAU,YAAY,CAAC,MAAM,QAAQ,KAAK;AAC3E;AAEA,SAAS,UAAU,OAAiC;CAClD,OAAO,OAAO,UAAU,YAAY,MAAM,KAAK,MAAM;AACvD;AAEA,SAAS,+BACP,QACS;CACT,OAAO,OAAO,YAAY,SAAS,OAAO,kBAAkB,OAAO;AACrE;AAEA,SAAS,gBACP,UACA,QACS;CACT,IAAI,CAAC,UAAU,QAAQ,GACrB,OAAO;CAGT,MAAM,qBAAqBA,uBAAAA,kBAAkB,QAAQ;CACrD,IAAI,OAAO,8BAA8B,WAAW;EAClD,KAAK,MAAM,oBAAoB,OAAO,mBACpC,IAAI,mBAAmB,SAAS,gBAAgB,GAC9C,OAAO;EAGX,OAAO;CACT;CAEA,OAAO,OAAO,kBAAkB,IAAI,kBAAkB;AACxD;;;;AAKA,SAAgB,iBACd,UACA,QACS;CACT,OAAO,OAAO,YAAY,SAAS,gBAAgB,UAAU,MAAM;AACrE;AAEA,SAAS,eACP,OACA,KACoB;CACpB,MAAM,QAAQ,MAAM;CACpB,OAAO,OAAO,UAAU,WAAW,QAAQ,KAAA;AAC7C;AAEA,SAAS,qBACP,OACA,WACA,UACoB;CACpB,MAAM,SAAS,MAAM;CACrB,IAAI,CAAC,SAAS,MAAM,GAClB;CAEF,OAAO,eAAe,QAAQ,QAAQ;AACxC;AAEA,SAAS,wBACP,OACoB;CACpB,OACE,eAAe,OAAO,cAAc,KACpC,qBAAqB,OAAO,UAAU,cAAc,KACpD,qBAAqB,OAAO,qBAAqB,cAAc,KAC/D,qBAAqB,OAAO,QAAQ,cAAc,MACjD,OAAO,MAAM,OAAO,WAAW,MAAM,KAAK,KAAA;AAE/C;AAEA,SAAS,sBACP,OACA,kBACoB;CACpB,MAAM,OAAO,eAAe,OAAO,MAAM;CACzC,MAAM,eACJ,eAAe,OAAO,MAAM,KAC5B,eAAe,OAAO,WAAW,KACjC,qBAAqB,OAAO,YAAY,MAAM,KAC9C,qBAAqB,OAAO,UAAU,MAAM,KAC5C,qBAAqB,OAAO,qBAAqB,MAAM,KACvD,qBAAqB,OAAO,QAAQ,MAAM,MACzC,QAAQ,QAAQ,KAAK,YAAY,MAAM,SAAS,OAAO,KAAA;CAE1D,IAAI,gBAAgB,MAClB,OAAO;CAGT,MAAM,aAAa,wBAAwB,KAAK;CAChD,OAAO,cAAc,OACjB,kBAAkB,kBAAkB,IAAI,UAAU,IAClD,KAAA;AACN;AAEA,SAAS,uBAAuB,OAAyC;CACvE,MAAM,OAAO,eAAe,OAAO,MAAM,KAAK,eAAe,OAAO,OAAO;CAC3E,IAAI,SAAS,UAAU,SAAS,gBAC9B,OAAO;CAGT,MAAM,KAAK,MAAM;CACjB,IACE,MAAM,QAAQ,EAAE,KAChB,GAAG,MAAM,SAAS,OAAO,SAAS,YAAY,KAAK,SAAS,aAAa,CAAC,GAE1E,OAAO;CAGT,IACE,kBAAkB,SAClB,qBAAqB,OAAO,UAAU,cAAc,KAAK,QACzD,qBAAqB,OAAO,qBAAqB,cAAc,KAAK,MAEpE,OAAO;CAGT,MAAM,OAAO,eAAe,OAAO,MAAM;CACzC,OACE,QAAQ,QACR,CAAC,WAAW,IAAI,KAAK,YAAY,CAAC,MACjC,aAAa,SAAS,SAAS,MAAM,MAAM,KAAK,SAAS,MAAM,IAAI;AAExE;AAEA,SAAS,wBACP,OACA,QACyB;CACzB,MAAM,OAAO,EAAE,GAAG,MAAM;CAExB,KAAK,MAAM,aAAa,wBACtB,IAAI,aAAa,MACf,KAAK,aAAa,OAAO;CAI7B,KAAK,MAAM,aAAa;EAAC;EAAU;EAAQ;CAAmB,GAAG;EAC/D,MAAM,SAAS,KAAK;EACpB,IAAI,CAAC,SAAS,MAAM,GAClB;EAEF,MAAM,aAAa,EAAE,GAAG,OAAO;EAC/B,IAAI,UAAU;EACd,KAAK,MAAM,aAAa,wBACtB,IAAI,aAAa,YAAY;GAC3B,WAAW,aAAa,OAAO;GAC/B,UAAU;EACZ;EAEF,IAAI,SACF,KAAK,aAAa;CAEtB;CAEA,OAAO;AACT;AAEA,SAAS,qBACP,OACA,kBACM;CACN,IAAI,MAAM,QAAQ,KAAK,GAAG;EACxB,KAAK,MAAM,QAAQ,OACjB,qBAAqB,MAAM,gBAAgB;EAE7C;CACF;CAEA,IAAI,CAAC,SAAS,KAAK,GACjB;CAGF,MAAM,aAAa,wBAAwB,KAAK;CAChD,MAAM,WAAW,sBAAsB,KAAK;CAC5C,IAAI,cAAc,QAAQ,YAAY,MACpC,iBAAiB,kBAAkB,IAAI,YAAY,QAAQ;CAG7D,KAAK,MAAM,SAAS,OAAO,OAAO,KAAK,GACrC,qBAAqB,OAAO,gBAAgB;AAEhD;AAEA,SAAS,YACP,OACA,QACA,kBACiB;CACjB,IAAI,MAAM,QAAQ,KAAK,GAAG;EACxB,IAAI,UAAU;EACd,MAAM,OAAkB,CAAC;EACzB,KAAK,MAAM,QAAQ,OAAO;GACxB,MAAM,SAAS,YAAY,MAAM,QAAQ,gBAAgB;GACzD,IAAI,OAAO,SACT,UAAU;GAEZ,KAAK,KAAK,OAAO,KAAK;EACxB;EACA,OAAO,UAAU;GAAE,OAAO;GAAM;EAAQ,IAAI;GAAE;GAAO;EAAQ;CAC/D;CAEA,IAAI,CAAC,SAAS,KAAK,GACjB,OAAO;EAAE;EAAO,SAAS;CAAM;CAGjC,MAAM,WAAW,sBAAsB,OAAO,gBAAgB;CAC9D,IAAI,uBAAuB,KAAK,KAAK,iBAAiB,UAAU,MAAM,GACpE,OAAO;EACL,OAAO,wBAAwB,OAAO,MAAM;EAC5C,SAAS;CACX;CAGF,IAAI,UAAU;CACd,MAAM,OAAgC,CAAC;CACvC,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,KAAK,GAAG;EAChD,MAAM,SAAS,YAAY,OAAO,QAAQ,gBAAgB;EAC1D,IAAI,OAAO,SACT,UAAU;EAEZ,KAAK,OAAO,OAAO;CACrB;CAEA,OAAO,UAAU;EAAE,OAAO;EAAM;CAAQ,IAAI;EAAE;EAAO;CAAQ;AAC/D;AAEA,SAAS,sBACP,OACA,QACiB;CACjB,MAAM,mBAAqC,EACzC,mCAAmB,IAAI,IAAI,EAC7B;CACA,IAAI,OAAO,UAAU,UAAU;EAC7B,qBAAqB,OAAO,gBAAgB;EAC5C,OAAO,YAAY,OAAO,QAAQ,gBAAgB;CACpD;CAEA,MAAM,UAAU,MAAM,KAAK;CAC3B,IAAI,CAAC,QAAQ,WAAW,GAAG,KAAK,CAAC,QAAQ,WAAW,GAAG,GACrD,OAAO;EAAE;EAAO,SAAS;CAAM;CAGjC,IAAI;EACF,MAAM,SAAS,KAAK,MAAM,KAAK;EAC/B,qBAAqB,QAAQ,gBAAgB;EAC7C,MAAM,SAAS,YAAY,QAAQ,QAAQ,gBAAgB;EAC3D,OAAO,OAAO,UACV;GAAE,OAAO,KAAK,UAAU,OAAO,KAAK;GAAG,SAAS;EAAK,IACrD;GAAE;GAAO,SAAS;EAAM;CAC9B,QAAQ;EACN,OAAO;GAAE;GAAO,SAAS;EAAM;CACjC;AACF;AAEA,SAAS,gBACP,YACA,KACA,QACM;CACN,IAAI,EAAE,OAAO,aACX;CAGF,MAAM,SAAS,sBAAsB,WAAW,MAAM,MAAM;CAC5D,IAAI,OAAO,SACT,WAAW,OAAO,OAAO;AAE7B;AAEA,SAAS,kBAAkB,YAA8C;CACvE,MAAM,OAAO,WAAWC,kBAAAA,2BAA2B;CACnD,OAAO,OAAO,SAAS,YAAY,KAAK,YAAY,MAAM;AAC5D;AAEA,SAAS,8BACP,YACM;CACN,MAAM,OAAO,WAAWA,kBAAAA,2BAA2B;CACnD,IAAI,OAAO,SAAS,YAAY,KAAK,YAAY,MAAM,QACrD;CAGF,MAAM,gBACJ,WACE,GAAGA,kBAAAA,2BAA2B,qBAAqB;CAEvD,IACE,OAAO,kBAAkB,YACzB,cAAc,WAAW,0BAA0B,GAEnD,WAAWA,kBAAAA,2BAA2B,oBAAoB;AAE9D;AAEA,SAAgB,6BAA6B,MAA0B;CACrE,8BAA+B,KAA4B,UAAU;AACvE;AAEA,SAAS,4BACP,MACA,YACA,QACM;CACN,IACE,EACE,kBAAkB,UAAU,KAC5B,iBAAiB,KAAK,MAAM,MAAM,KAClCA,kBAAAA,2BAA2B,sBAAsB,aAGnD;CAGF,WAAWA,kBAAAA,2BAA2B,sBACpC,OAAO;AACX;AAEA,SAAgB,8BACd,MACA,QACM;CACN,MAAM,aAAc,KAA4B;CAChD,6BAA6B,IAAI;CAEjC,IAAI,CAAC,+BAA+B,MAAM,GACxC;CAGF,4BAA4B,MAAM,YAAY,MAAM;CAEpD,KAAK,MAAM,OAAO;EAChBA,kBAAAA,2BAA2B;EAC3BA,kBAAAA,2BAA2B;EAC3BA,kBAAAA,2BAA2B;EAC3BA,kBAAAA,2BAA2B;CAC7B,GACE,gBAAgB,YAAY,KAAK,MAAM;AAE3C;AAEA,IAAM,2CAAN,MAAwE;CACtE;CACA;CACA,8BAA+B,IAAI,QAGjC;CAEF,YACE,QACA,gBACA;EACA,KAAK,YAAY,IAAIC,eAAAA,sBAAsB,MAAM;EACjD,KAAK,iBAAiB;CACxB;CAEA,QAAQ,MAAY,eAA8B;EAChD,IAAIC,6BAAAA,uBAAuB,KAAK,IAAI,GAClC;EAEF,MAAM,SACJC,6BAAAA,sCAAsC,aAAa,KACnD,KAAK;EACP,IAAI,UAAU,MACZ,KAAK,YAAY,IAAI,MAAM,MAAM;EAEnC,KAAK,UAAU,QAAQ,MAAM,aAAa;CAC5C;CAEA,MAAM,MAA0B;EAC9B,IAAID,6BAAAA,uBAAuB,KAAK,IAAI,GAClC;EAEF,6BAA6B,IAAI;EACjC,6BAAA,kBAAkB,IAAI;EACtB,MAAM,SAAS,KAAK,YAAY,IAAI,IAAI,KAAK,KAAK;EAClD,IAAI,UAAU,MACZ,8BAA8B,MAAM,MAAM;EAE5C,KAAK,UAAU,MAAM,IAAI;CAC3B;CAEA,aAA4B;EAC1B,OAAO,KAAK,UAAU,WAAW;CACnC;CAEA,WAA0B;EACxB,OAAO,KAAK,UAAU,SAAS;CACjC;AACF;AAEA,SAAgB,4BACd,QACA,aACA,eACe;CAIf,OAAO,IAAI,yCAAyC,QAH7BE,uBAAAA,2BAA2B,aAAa,aAAa,IACxEC,uBAAAA,+BAA+B,aAAa,aAAa,IACzD,KAAA,CACsE;AAC5E;AAEA,SAAS,qBAA8B;CACrC,OACE,UAAU,QAAQ,IAAI,mBAAmB,KACzC,UAAU,QAAQ,IAAI,mBAAmB;AAE7C;AAEA,SAAS,sBAAsB,UAAsC;CACnE,IAAI,YAAY,MACd,OAAO;CAET,OAAO,UAAU,SAAS,SAAS,KAAK,UAAU,SAAS,SAAS;AACtE;AAEA,SAAgB,+BAA+B,EAC7C,aACA,iBAIU;CACV,MAAM,WAAWC,uBAAAA,sBAAsB,aAAa,aAAa;CACjE,IAAI,UAAU,YAAY,OACxB,OAAO;CAIT,IADiB,UAAU,iBAAiB,YAC3B,MACf,OAAO;CAGT,OAAO,sBAAsB,QAAQ,KAAK,mBAAmB;AAC/D"}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
const require_cache = require("../../messages/cache.cjs");
|
|
2
|
-
const require_toolCache = require("./toolCache.cjs");
|
|
3
2
|
const require_message_inputs = require("./utils/message_inputs.cjs");
|
|
4
3
|
const require_message_outputs = require("./utils/message_outputs.cjs");
|
|
5
4
|
require("./utils/index.cjs");
|
|
6
5
|
const require_cachePoints = require("./cachePoints.cjs");
|
|
6
|
+
const require_toolCache = require("./toolCache.cjs");
|
|
7
7
|
let _langchain_core_messages = require("@langchain/core/messages");
|
|
8
8
|
let _langchain_core_outputs = require("@langchain/core/outputs");
|
|
9
9
|
let _langchain_aws = require("@langchain/aws");
|
|
@@ -58,6 +58,15 @@ function splitStreamToken(text) {
|
|
|
58
58
|
}
|
|
59
59
|
return chunks;
|
|
60
60
|
}
|
|
61
|
+
/**
|
|
62
|
+
* Resolves the text a delta contributes to the smoothing cadence, preferring a
|
|
63
|
+
* text delta over a reasoning delta and ignoring non-string payloads.
|
|
64
|
+
*/
|
|
65
|
+
function resolveVisibleText(text, reasoningText) {
|
|
66
|
+
if (typeof text === "string") return text;
|
|
67
|
+
if (typeof reasoningText === "string") return reasoningText;
|
|
68
|
+
return "";
|
|
69
|
+
}
|
|
61
70
|
function getCadencedStreamDelay({ targetDelay, lastVisibleContentAt, now }) {
|
|
62
71
|
if (targetDelay <= 0 || lastVisibleContentAt == null) return 0;
|
|
63
72
|
return Math.max(0, targetDelay - (now - lastVisibleContentAt));
|
|
@@ -274,7 +283,7 @@ var CustomChatBedrockConverse = class extends _langchain_aws.ChatBedrockConverse
|
|
|
274
283
|
const text = delta.text;
|
|
275
284
|
const reasoningContent = delta.reasoningContent;
|
|
276
285
|
const reasoningText = reasoningContent?.text;
|
|
277
|
-
const visibleText =
|
|
286
|
+
const visibleText = resolveVisibleText(text, reasoningText);
|
|
278
287
|
const smooth = this._lc_stream_delay > 0 && visibleText !== "";
|
|
279
288
|
const tokenChunks = smooth ? splitStreamToken(visibleText) : [visibleText];
|
|
280
289
|
for (const token of tokenChunks) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":["ChatBedrockConverse","supportsBedrockToolCache","insertBedrockToolCachePoint","resolveBedrockPromptCacheTtl","convertToConverseMessages","ConverseStreamCommand","handleConverseStreamContentBlockDelta","handleConverseStreamContentBlockStart","handleConverseStreamMetadata","createConverseToolUseStopChunk","ChatGenerationChunk","AIMessageChunk"],"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 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 {\n convertToConverseMessages,\n createConverseToolUseStopChunk,\n handleConverseStreamContentBlockStart,\n handleConverseStreamContentBlockDelta,\n handleConverseStreamMetadata,\n} from './utils';\nimport type { ContentBlockDeltaEvent } from './types';\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\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 = 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 =\n typeof text === 'string'\n ? text\n : typeof reasoningText === 'string'\n ? reasoningText\n : '';\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.cjs","names":["ChatBedrockConverse","supportsBedrockToolCache","insertBedrockToolCachePoint","resolveBedrockPromptCacheTtl","convertToConverseMessages","ConverseStreamCommand","handleConverseStreamContentBlockDelta","handleConverseStreamContentBlockStart","handleConverseStreamMetadata","createConverseToolUseStopChunk","ChatGenerationChunk","AIMessageChunk"],"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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"}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
const require_errors = require("../utils/errors.cjs");
|
|
2
|
+
//#region src/llm/contextOverflowRecovery.ts
|
|
3
|
+
/** Fraction of the previous budget used when the provider named no ceiling. */
|
|
4
|
+
const BLIND_SHRINK_RATIO = .7;
|
|
5
|
+
/** Slack left below a known ceiling so the retry is not sized to the edge. */
|
|
6
|
+
const CEILING_HEADROOM_RATIO = .95;
|
|
7
|
+
/**
|
|
8
|
+
* Fallback floor, used only when the instruction size is unknown. When it is
|
|
9
|
+
* known the floor is derived from it instead, so a genuinely small model — a
|
|
10
|
+
* 4k window, where a 95%-of-ceiling budget lands below this constant — can
|
|
11
|
+
* still recover.
|
|
12
|
+
*/
|
|
13
|
+
const MIN_RECOVERY_BUDGET_TOKENS = 4e3;
|
|
14
|
+
/**
|
|
15
|
+
* Room a corrected budget must leave above the instructions for the messages
|
|
16
|
+
* themselves. Without it, a budget that merely clears the system prompt and
|
|
17
|
+
* tool schemas is not a budget anything can be compacted into.
|
|
18
|
+
*/
|
|
19
|
+
const MIN_MESSAGE_HEADROOM_TOKENS = 2e3;
|
|
20
|
+
function isUsable(value) {
|
|
21
|
+
return value != null && Number.isFinite(value) && value > 0;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Converts a provider-space retry budget into the units consumed by a pruner
|
|
25
|
+
* calibrated for another provider while preserving the same raw-token limit.
|
|
26
|
+
*/
|
|
27
|
+
function translateRecoveryBudget(budgetTokens, sourceCalibrationRatio, targetCalibrationRatio) {
|
|
28
|
+
if (!isUsable(budgetTokens) || !isUsable(sourceCalibrationRatio) || !isUsable(targetCalibrationRatio)) return budgetTokens;
|
|
29
|
+
return Math.floor(budgetTokens * targetCalibrationRatio / sourceCalibrationRatio);
|
|
30
|
+
}
|
|
31
|
+
/** Applies the conservative shrink used when no provider-space ceiling is usable. */
|
|
32
|
+
function getBlindRecoveryBudget(maxContextTokens) {
|
|
33
|
+
return isUsable(maxContextTokens) ? Math.floor(maxContextTokens * BLIND_SHRINK_RATIO) : void 0;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* The completion allowance the provider counted against the same ceiling,
|
|
37
|
+
* when it reported both the total and the prompt portion. The retry budget
|
|
38
|
+
* governs the prompt only, so this has to come off the ceiling first —
|
|
39
|
+
* otherwise a large `maxTokens` keeps the request over the limit no matter
|
|
40
|
+
* how far the prompt is compacted.
|
|
41
|
+
*/
|
|
42
|
+
function reservedForCompletion(info, configuredCompletionTokens) {
|
|
43
|
+
if (isUsable(info.requestedTokens) && isUsable(info.promptTokens)) {
|
|
44
|
+
const difference = info.requestedTokens - info.promptTokens;
|
|
45
|
+
if (difference > 0) return difference;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* No breakdown on offer. Fall back to what the caller configured, because
|
|
49
|
+
* the provider still counts it: targeting the whole ceiling would leave the
|
|
50
|
+
* retry at `prompt + maxTokens` and over the limit however far the prompt
|
|
51
|
+
* is compacted.
|
|
52
|
+
*/
|
|
53
|
+
return isUsable(configuredCompletionTokens) ? configuredCompletionTokens : 0;
|
|
54
|
+
}
|
|
55
|
+
function resolveTargetBudget(info, maxContextTokens, estimatedPromptTokens, configuredCompletionTokens) {
|
|
56
|
+
if (isUsable(info.limitTokens)) {
|
|
57
|
+
const promptCeiling = info.limitTokens - reservedForCompletion(info, configuredCompletionTokens);
|
|
58
|
+
/**
|
|
59
|
+
* A completion allowance at or above the ceiling leaves nothing for the
|
|
60
|
+
* prompt: even an empty one plus the requested output overruns the limit.
|
|
61
|
+
* Compaction cannot fix that, so declining surfaces the real problem
|
|
62
|
+
* instead of burning the recovery budget on retries that must fail.
|
|
63
|
+
*/
|
|
64
|
+
if (promptCeiling <= 0) return null;
|
|
65
|
+
return promptCeiling * CEILING_HEADROOM_RATIO;
|
|
66
|
+
}
|
|
67
|
+
if (isUsable(estimatedPromptTokens)) return estimatedPromptTokens * BLIND_SHRINK_RATIO;
|
|
68
|
+
if (isUsable(maxContextTokens)) return maxContextTokens * BLIND_SHRINK_RATIO;
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Decides whether a failed model call is a recoverable context overflow and,
|
|
73
|
+
* if so, what budget the retry should be re-pruned against.
|
|
74
|
+
*
|
|
75
|
+
* Returns `null` when the error is something compaction cannot fix, or when
|
|
76
|
+
* the per-run recovery budget is spent — in both cases the caller should let
|
|
77
|
+
* its normal failure handling proceed.
|
|
78
|
+
*/
|
|
79
|
+
function planContextOverflowRecovery({ error, provider, maxContextTokens, estimatedPromptTokens, calibrationRatio, instructionTokens, canSummarize = false, configuredCompletionTokens, attemptsSoFar, maxAttempts = 2 }) {
|
|
80
|
+
if (attemptsSoFar >= maxAttempts) return null;
|
|
81
|
+
const info = require_errors.getContextOverflowInfo(error, {
|
|
82
|
+
provider,
|
|
83
|
+
estimatedPromptTokens,
|
|
84
|
+
maxContextTokens
|
|
85
|
+
});
|
|
86
|
+
if (info == null) return null;
|
|
87
|
+
const currentCalibrationRatio = isUsable(calibrationRatio) ? calibrationRatio : 1;
|
|
88
|
+
const estimatedMessageTokens = isUsable(estimatedPromptTokens) && isUsable(instructionTokens) ? estimatedPromptTokens - instructionTokens : estimatedPromptTokens;
|
|
89
|
+
const observedMessageTokens = isUsable(info.promptTokens) && isUsable(instructionTokens) ? info.promptTokens - instructionTokens : info.promptTokens;
|
|
90
|
+
const observedCalibrationRatio = isUsable(observedMessageTokens) && isUsable(estimatedMessageTokens) ? observedMessageTokens / estimatedMessageTokens * currentCalibrationRatio : void 0;
|
|
91
|
+
const target = resolveTargetBudget(info, maxContextTokens, estimatedPromptTokens, configuredCompletionTokens);
|
|
92
|
+
if (target == null) return isUsable(info.limitTokens) || isUsable(maxContextTokens) || isUsable(estimatedPromptTokens) ? null : {
|
|
93
|
+
info,
|
|
94
|
+
observedCalibrationRatio,
|
|
95
|
+
budgetTokens: void 0
|
|
96
|
+
};
|
|
97
|
+
/**
|
|
98
|
+
* A retry that does not actually shrink the prompt would just reproduce the
|
|
99
|
+
* same rejection, so a ceiling that lands at or above the budget we already
|
|
100
|
+
* had is replaced by a blind shrink.
|
|
101
|
+
*/
|
|
102
|
+
const bounded = isUsable(maxContextTokens) && target >= maxContextTokens ? getBlindRecoveryBudget(maxContextTokens) ?? target : target;
|
|
103
|
+
const budgetTokens = Math.floor(bounded);
|
|
104
|
+
/**
|
|
105
|
+
* Below the floor there is no usable budget left: either nothing survives
|
|
106
|
+
* pruning, or the instructions alone fill the window, in which case the
|
|
107
|
+
* summarize node refuses to run and the detour would bounce between the
|
|
108
|
+
* agent and summarize nodes without ever shrinking the prompt. Declining
|
|
109
|
+
* lets the existing "instructions exceed context budget" guidance surface
|
|
110
|
+
* instead.
|
|
111
|
+
*/
|
|
112
|
+
let floorTokens = MIN_RECOVERY_BUDGET_TOKENS;
|
|
113
|
+
if (isUsable(instructionTokens)) floorTokens = instructionTokens + MIN_MESSAGE_HEADROOM_TOKENS;
|
|
114
|
+
else if (canSummarize) floorTokens = 1;
|
|
115
|
+
if (budgetTokens < floorTokens) return null;
|
|
116
|
+
/**
|
|
117
|
+
* Refuse to report a "recovery" that changes nothing: when the budget in
|
|
118
|
+
* force is already at or below the target, re-pruning cannot free space.
|
|
119
|
+
*/
|
|
120
|
+
if (isUsable(maxContextTokens) && budgetTokens >= maxContextTokens) return null;
|
|
121
|
+
return {
|
|
122
|
+
budgetTokens,
|
|
123
|
+
info,
|
|
124
|
+
observedCalibrationRatio
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
//#endregion
|
|
128
|
+
exports.getBlindRecoveryBudget = getBlindRecoveryBudget;
|
|
129
|
+
exports.planContextOverflowRecovery = planContextOverflowRecovery;
|
|
130
|
+
exports.translateRecoveryBudget = translateRecoveryBudget;
|
|
131
|
+
|
|
132
|
+
//# sourceMappingURL=contextOverflowRecovery.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"contextOverflowRecovery.cjs","names":["getContextOverflowInfo"],"sources":["../../../src/llm/contextOverflowRecovery.ts"],"sourcesContent":["/**\n * Recovery policy for provider context-overflow rejections.\n *\n * Detection (`@/utils/errors`) answers \"was this an overflow, and what did\n * the provider disclose?\". This module answers the follow-up: \"what budget\n * should the retry target?\" — deliberately kept as pure functions so the\n * policy can be reasoned about and tested without a graph.\n *\n * On units: `maxContextTokens` is a **provider-space** budget. The pruner\n * converts it into its own raw estimate space by dividing by the\n * `calibrationRatio` it learns from reported usage. A provider-reported\n * ceiling is therefore applied verbatim — converting it here as well would\n * apply the same correction twice and prune toward roughly `limit / ratio²`,\n * silently discarding far more history than the overflow called for.\n *\n * `observedCalibrationRatio` is returned separately so the caller can seed\n * the pruner's conversion without folding the same correction into the\n * provider-space budget.\n */\nimport type { ContextOverflowInfo } from '@/utils/errors';\nimport type { Providers } from '@/common';\nimport { getContextOverflowInfo } from '@/utils/errors';\n\n/** Fraction of the previous budget used when the provider named no ceiling. */\nconst BLIND_SHRINK_RATIO = 0.7;\n\n/** Slack left below a known ceiling so the retry is not sized to the edge. */\nconst CEILING_HEADROOM_RATIO = 0.95;\n\n/**\n * Fallback floor, used only when the instruction size is unknown. When it is\n * known the floor is derived from it instead, so a genuinely small model — a\n * 4k window, where a 95%-of-ceiling budget lands below this constant — can\n * still recover.\n */\nconst MIN_RECOVERY_BUDGET_TOKENS = 4_000;\n\n/**\n * Room a corrected budget must leave above the instructions for the messages\n * themselves. Without it, a budget that merely clears the system prompt and\n * tool schemas is not a budget anything can be compacted into.\n */\nconst MIN_MESSAGE_HEADROOM_TOKENS = 2_000;\n\n/** Bound on forced-compaction retries per agent, per run. */\nexport const DEFAULT_MAX_OVERFLOW_RECOVERIES = 2;\n\nexport interface OverflowRecoveryPlan {\n /** Budget the retry should target, in provider token units when known. */\n budgetTokens?: number;\n /** What the provider disclosed. Carried through for logging. */\n info: ContextOverflowInfo;\n /**\n * Provider-reported message tokens divided by our own message estimate,\n * when both are known. Greater than 1 means we under-count relative to this\n * provider.\n *\n * Returned separately so the graph can seed the pruner's calibration;\n * applying it to this plan's budget as well would double-count. Fixed\n * instruction overhead is removed before deriving it from\n * `info.promptTokens`, never `info.requestedTokens`, since several providers\n * fold the completion allowance into the latter.\n */\n observedCalibrationRatio?: number;\n}\n\nexport interface OverflowRecoveryParams {\n error: unknown;\n provider: Providers;\n /** Budget in force when the rejected prompt was built. */\n maxContextTokens?: number;\n /** Our own estimate of the prompt we actually sent. */\n estimatedPromptTokens?: number;\n /** Provider/local calibration already applied to the prompt estimate. */\n calibrationRatio?: number;\n /**\n * System prompt plus tool schemas — the part of the budget compaction\n * cannot touch. A corrected budget at or below this leaves no room for\n * messages, and the summarize node refuses to run, so recovery is declined\n * rather than entered.\n */\n instructionTokens?: number;\n /** Whether a model-backed summary can compact messages without a pruner. */\n canSummarize?: boolean;\n /**\n * Completion allowance the caller configured. Providers count it against\n * the same ceiling, so it has to come off the top when the error itself did\n * not break the total down.\n */\n configuredCompletionTokens?: number;\n /** Recoveries already attempted for this agent in this run. */\n attemptsSoFar: number;\n maxAttempts?: number;\n}\n\nfunction isUsable(value: number | undefined): value is number {\n return value != null && Number.isFinite(value) && value > 0;\n}\n\n/**\n * Converts a provider-space retry budget into the units consumed by a pruner\n * calibrated for another provider while preserving the same raw-token limit.\n */\nexport function translateRecoveryBudget(\n budgetTokens: number | undefined,\n sourceCalibrationRatio: number | undefined,\n targetCalibrationRatio: number | undefined\n): number | undefined {\n if (\n !isUsable(budgetTokens) ||\n !isUsable(sourceCalibrationRatio) ||\n !isUsable(targetCalibrationRatio)\n ) {\n return budgetTokens;\n }\n return Math.floor(\n (budgetTokens * targetCalibrationRatio) / sourceCalibrationRatio\n );\n}\n\n/** Applies the conservative shrink used when no provider-space ceiling is usable. */\nexport function getBlindRecoveryBudget(\n maxContextTokens: number | undefined\n): number | undefined {\n return isUsable(maxContextTokens)\n ? Math.floor(maxContextTokens * BLIND_SHRINK_RATIO)\n : undefined;\n}\n\n/**\n * The completion allowance the provider counted against the same ceiling,\n * when it reported both the total and the prompt portion. The retry budget\n * governs the prompt only, so this has to come off the ceiling first —\n * otherwise a large `maxTokens` keeps the request over the limit no matter\n * how far the prompt is compacted.\n */\nfunction reservedForCompletion(\n info: ContextOverflowInfo,\n configuredCompletionTokens: number | undefined\n): number {\n if (isUsable(info.requestedTokens) && isUsable(info.promptTokens)) {\n const difference = info.requestedTokens - info.promptTokens;\n if (difference > 0) {\n return difference;\n }\n }\n /**\n * No breakdown on offer. Fall back to what the caller configured, because\n * the provider still counts it: targeting the whole ceiling would leave the\n * retry at `prompt + maxTokens` and over the limit however far the prompt\n * is compacted.\n */\n return isUsable(configuredCompletionTokens) ? configuredCompletionTokens : 0;\n}\n\nfunction resolveTargetBudget(\n info: ContextOverflowInfo,\n maxContextTokens: number | undefined,\n estimatedPromptTokens: number | undefined,\n configuredCompletionTokens: number | undefined\n): number | null {\n if (isUsable(info.limitTokens)) {\n const promptCeiling =\n info.limitTokens -\n reservedForCompletion(info, configuredCompletionTokens);\n /**\n * A completion allowance at or above the ceiling leaves nothing for the\n * prompt: even an empty one plus the requested output overruns the limit.\n * Compaction cannot fix that, so declining surfaces the real problem\n * instead of burning the recovery budget on retries that must fail.\n */\n if (promptCeiling <= 0) {\n return null;\n }\n\n return promptCeiling * CEILING_HEADROOM_RATIO;\n }\n if (isUsable(estimatedPromptTokens)) {\n return estimatedPromptTokens * BLIND_SHRINK_RATIO;\n }\n if (isUsable(maxContextTokens)) {\n return maxContextTokens * BLIND_SHRINK_RATIO;\n }\n return null;\n}\n\n/**\n * Decides whether a failed model call is a recoverable context overflow and,\n * if so, what budget the retry should be re-pruned against.\n *\n * Returns `null` when the error is something compaction cannot fix, or when\n * the per-run recovery budget is spent — in both cases the caller should let\n * its normal failure handling proceed.\n */\nexport function planContextOverflowRecovery({\n error,\n provider,\n maxContextTokens,\n estimatedPromptTokens,\n calibrationRatio,\n instructionTokens,\n canSummarize = false,\n configuredCompletionTokens,\n attemptsSoFar,\n maxAttempts = DEFAULT_MAX_OVERFLOW_RECOVERIES,\n}: OverflowRecoveryParams): OverflowRecoveryPlan | null {\n if (attemptsSoFar >= maxAttempts) {\n return null;\n }\n\n const info = getContextOverflowInfo(error, {\n provider,\n estimatedPromptTokens,\n maxContextTokens,\n });\n if (info == null) {\n return null;\n }\n\n const currentCalibrationRatio = isUsable(calibrationRatio)\n ? calibrationRatio\n : 1;\n const estimatedMessageTokens =\n isUsable(estimatedPromptTokens) && isUsable(instructionTokens)\n ? estimatedPromptTokens - instructionTokens\n : estimatedPromptTokens;\n const observedMessageTokens =\n isUsable(info.promptTokens) && isUsable(instructionTokens)\n ? info.promptTokens - instructionTokens\n : info.promptTokens;\n const observedCalibrationRatio =\n isUsable(observedMessageTokens) && isUsable(estimatedMessageTokens)\n ? (observedMessageTokens / estimatedMessageTokens) *\n currentCalibrationRatio\n : undefined;\n\n const target = resolveTargetBudget(\n info,\n maxContextTokens,\n estimatedPromptTokens,\n configuredCompletionTokens\n );\n if (target == null) {\n const hasNumericBasis =\n isUsable(info.limitTokens) ||\n isUsable(maxContextTokens) ||\n isUsable(estimatedPromptTokens);\n return hasNumericBasis\n ? null\n : { info, observedCalibrationRatio, budgetTokens: undefined };\n }\n\n /**\n * A retry that does not actually shrink the prompt would just reproduce the\n * same rejection, so a ceiling that lands at or above the budget we already\n * had is replaced by a blind shrink.\n */\n const bounded =\n isUsable(maxContextTokens) && target >= maxContextTokens\n ? (getBlindRecoveryBudget(maxContextTokens) ?? target)\n : target;\n\n const budgetTokens = Math.floor(bounded);\n\n /**\n * Below the floor there is no usable budget left: either nothing survives\n * pruning, or the instructions alone fill the window, in which case the\n * summarize node refuses to run and the detour would bounce between the\n * agent and summarize nodes without ever shrinking the prompt. Declining\n * lets the existing \"instructions exceed context budget\" guidance surface\n * instead.\n */\n let floorTokens = MIN_RECOVERY_BUDGET_TOKENS;\n if (isUsable(instructionTokens)) {\n floorTokens = instructionTokens + MIN_MESSAGE_HEADROOM_TOKENS;\n } else if (canSummarize) {\n floorTokens = 1;\n }\n if (budgetTokens < floorTokens) {\n return null;\n }\n\n /**\n * Refuse to report a \"recovery\" that changes nothing: when the budget in\n * force is already at or below the target, re-pruning cannot free space.\n */\n if (isUsable(maxContextTokens) && budgetTokens >= maxContextTokens) {\n return null;\n }\n\n return { budgetTokens, info, observedCalibrationRatio };\n}\n"],"mappings":";;;AAwBA,MAAM,qBAAqB;;AAG3B,MAAM,yBAAyB;;;;;;;AAQ/B,MAAM,6BAA6B;;;;;;AAOnC,MAAM,8BAA8B;AAqDpC,SAAS,SAAS,OAA4C;CAC5D,OAAO,SAAS,QAAQ,OAAO,SAAS,KAAK,KAAK,QAAQ;AAC5D;;;;;AAMA,SAAgB,wBACd,cACA,wBACA,wBACoB;CACpB,IACE,CAAC,SAAS,YAAY,KACtB,CAAC,SAAS,sBAAsB,KAChC,CAAC,SAAS,sBAAsB,GAEhC,OAAO;CAET,OAAO,KAAK,MACT,eAAe,yBAA0B,sBAC5C;AACF;;AAGA,SAAgB,uBACd,kBACoB;CACpB,OAAO,SAAS,gBAAgB,IAC5B,KAAK,MAAM,mBAAmB,kBAAkB,IAChD,KAAA;AACN;;;;;;;;AASA,SAAS,sBACP,MACA,4BACQ;CACR,IAAI,SAAS,KAAK,eAAe,KAAK,SAAS,KAAK,YAAY,GAAG;EACjE,MAAM,aAAa,KAAK,kBAAkB,KAAK;EAC/C,IAAI,aAAa,GACf,OAAO;CAEX;;;;;;;CAOA,OAAO,SAAS,0BAA0B,IAAI,6BAA6B;AAC7E;AAEA,SAAS,oBACP,MACA,kBACA,uBACA,4BACe;CACf,IAAI,SAAS,KAAK,WAAW,GAAG;EAC9B,MAAM,gBACJ,KAAK,cACL,sBAAsB,MAAM,0BAA0B;;;;;;;EAOxD,IAAI,iBAAiB,GACnB,OAAO;EAGT,OAAO,gBAAgB;CACzB;CACA,IAAI,SAAS,qBAAqB,GAChC,OAAO,wBAAwB;CAEjC,IAAI,SAAS,gBAAgB,GAC3B,OAAO,mBAAmB;CAE5B,OAAO;AACT;;;;;;;;;AAUA,SAAgB,4BAA4B,EAC1C,OACA,UACA,kBACA,uBACA,kBACA,mBACA,eAAe,OACf,4BACA,eACA,cAAA,KACsD;CACtD,IAAI,iBAAiB,aACnB,OAAO;CAGT,MAAM,OAAOA,eAAAA,uBAAuB,OAAO;EACzC;EACA;EACA;CACF,CAAC;CACD,IAAI,QAAQ,MACV,OAAO;CAGT,MAAM,0BAA0B,SAAS,gBAAgB,IACrD,mBACA;CACJ,MAAM,yBACJ,SAAS,qBAAqB,KAAK,SAAS,iBAAiB,IACzD,wBAAwB,oBACxB;CACN,MAAM,wBACJ,SAAS,KAAK,YAAY,KAAK,SAAS,iBAAiB,IACrD,KAAK,eAAe,oBACpB,KAAK;CACX,MAAM,2BACJ,SAAS,qBAAqB,KAAK,SAAS,sBAAsB,IAC7D,wBAAwB,yBACzB,0BACA,KAAA;CAEN,MAAM,SAAS,oBACb,MACA,kBACA,uBACA,0BACF;CACA,IAAI,UAAU,MAKZ,OAHE,SAAS,KAAK,WAAW,KACzB,SAAS,gBAAgB,KACzB,SAAS,qBAAqB,IAE5B,OACA;EAAE;EAAM;EAA0B,cAAc,KAAA;CAAU;;;;;;CAQhE,MAAM,UACJ,SAAS,gBAAgB,KAAK,UAAU,mBACnC,uBAAuB,gBAAgB,KAAK,SAC7C;CAEN,MAAM,eAAe,KAAK,MAAM,OAAO;;;;;;;;;CAUvC,IAAI,cAAc;CAClB,IAAI,SAAS,iBAAiB,GAC5B,cAAc,oBAAoB;MAC7B,IAAI,cACT,cAAc;CAEhB,IAAI,eAAe,aACjB,OAAO;;;;;CAOT,IAAI,SAAS,gBAAgB,KAAK,gBAAgB,kBAChD,OAAO;CAGT,OAAO;EAAE;EAAc;EAAM;CAAyB;AACxD"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
const require_common = require("./utils/common.cjs");
|
|
2
2
|
let _langchain_core_messages = require("@langchain/core/messages");
|
|
3
3
|
let _langchain_core_outputs = require("@langchain/core/outputs");
|
|
4
|
-
let _langchain_core_utils_env = require("@langchain/core/utils/env");
|
|
5
4
|
let _langchain_google_genai = require("@langchain/google-genai");
|
|
5
|
+
let _langchain_core_utils_env = require("@langchain/core/utils/env");
|
|
6
6
|
let _google_generative_ai = require("@google/generative-ai");
|
|
7
7
|
//#region src/llm/google/index.ts
|
|
8
8
|
var CustomChatGoogleGenerativeAI = class extends _langchain_google_genai.ChatGoogleGenerativeAI {
|
package/dist/cjs/llm/invoke.cjs
CHANGED
|
@@ -4,6 +4,7 @@ const require_core = require("../messages/core.cjs");
|
|
|
4
4
|
require("../messages/index.cjs");
|
|
5
5
|
const require_toolOutputReferences = require("../tools/toolOutputReferences.cjs");
|
|
6
6
|
const require_stream = require("../stream.cjs");
|
|
7
|
+
const require_errors = require("../utils/errors.cjs");
|
|
7
8
|
const require_truncation = require("./truncation.cjs");
|
|
8
9
|
const require_providers = require("./providers.cjs");
|
|
9
10
|
const require_init = require("./init.cjs");
|
|
@@ -146,6 +147,22 @@ async function attemptInvoke({ model, messages, provider, context, onChunk }, co
|
|
|
146
147
|
require_truncation.assertNotTruncatedToolCall(finalMessage, provider);
|
|
147
148
|
return { messages: [finalMessage] };
|
|
148
149
|
}
|
|
150
|
+
const fallbackErrorContexts = /* @__PURE__ */ new WeakMap();
|
|
151
|
+
const fallbackOverflowCandidates = /* @__PURE__ */ new WeakMap();
|
|
152
|
+
function attachFallbackErrorContext(error, fallbackContext) {
|
|
153
|
+
if (typeof error !== "object" || error === null) return;
|
|
154
|
+
fallbackErrorContexts.set(error, fallbackContext);
|
|
155
|
+
}
|
|
156
|
+
/** Reads back the fallback attribution attached by `tryFallbackProviders`. */
|
|
157
|
+
function getFallbackErrorContext(error) {
|
|
158
|
+
if (typeof error !== "object" || error === null) return;
|
|
159
|
+
return fallbackErrorContexts.get(error);
|
|
160
|
+
}
|
|
161
|
+
/** Returns every fallback overflow retained from an exhausted provider chain. */
|
|
162
|
+
function getFallbackOverflowCandidates(error) {
|
|
163
|
+
if (typeof error !== "object" || error === null) return [];
|
|
164
|
+
return [...fallbackOverflowCandidates.get(error) ?? []];
|
|
165
|
+
}
|
|
149
166
|
/**
|
|
150
167
|
* Best-effort read of the configured model name from client options.
|
|
151
168
|
* Providers disagree on the key (`model` vs `modelName`).
|
|
@@ -157,10 +174,24 @@ function extractClientOptionsModel(clientOptions) {
|
|
|
157
174
|
}
|
|
158
175
|
/**
|
|
159
176
|
* Attempts each fallback provider in order until one succeeds.
|
|
160
|
-
*
|
|
177
|
+
*
|
|
178
|
+
* When every fallback fails, a context overflow among them is thrown in
|
|
179
|
+
* preference to whichever failure happened to come last. An overflow is the
|
|
180
|
+
* one failure the caller can act on — it compacts and retries — and losing it
|
|
181
|
+
* behind a later unrelated error would surface a dead end instead. Ordinary
|
|
182
|
+
* failures still throw last-error-wins.
|
|
161
183
|
*/
|
|
162
|
-
async function tryFallbackProviders({ fallbacks, tools, messages, config, primaryError, context, onChunk }) {
|
|
184
|
+
async function tryFallbackProviders({ fallbacks, tools, messages, config, primaryError, context, onChunk, overflowContext }) {
|
|
185
|
+
const isOverflow = (error, contextOverride = overflowContext) => require_errors.getContextOverflowInfo(error, contextOverride) != null;
|
|
163
186
|
let lastError = primaryError;
|
|
187
|
+
/**
|
|
188
|
+
* Tracked apart from the primary's overflow. A caller reaching this
|
|
189
|
+
* function with an overflowing primary has already failed to recover from
|
|
190
|
+
* it, so a fallback overflow — which may sit against a different window and
|
|
191
|
+
* output allowance — is the more useful of the two to surface.
|
|
192
|
+
*/
|
|
193
|
+
const overflowCandidates = [];
|
|
194
|
+
const primaryOverflowError = isOverflow(primaryError) ? primaryError : void 0;
|
|
164
195
|
for (const fb of fallbacks) try {
|
|
165
196
|
const fbModel = require_init.initializeModel({
|
|
166
197
|
provider: fb.provider,
|
|
@@ -191,12 +222,38 @@ async function tryFallbackProviders({ fallbacks, tools, messages, config, primar
|
|
|
191
222
|
}, fbConfig);
|
|
192
223
|
} catch (e) {
|
|
193
224
|
lastError = e;
|
|
225
|
+
if (isOverflow(e, {
|
|
226
|
+
provider: fb.provider,
|
|
227
|
+
maxContextTokens: fb.maxContextTokens,
|
|
228
|
+
...overflowContext?.provider === fb.provider ? { estimatedPromptTokens: overflowContext.estimatedPromptTokens } : {}
|
|
229
|
+
})) {
|
|
230
|
+
const errorContext = {
|
|
231
|
+
provider: fb.provider,
|
|
232
|
+
clientOptions: fb.clientOptions,
|
|
233
|
+
maxContextTokens: fb.maxContextTokens
|
|
234
|
+
};
|
|
235
|
+
attachFallbackErrorContext(e, errorContext);
|
|
236
|
+
overflowCandidates.push({
|
|
237
|
+
error: e,
|
|
238
|
+
context: errorContext
|
|
239
|
+
});
|
|
240
|
+
}
|
|
194
241
|
continue;
|
|
195
242
|
}
|
|
196
|
-
|
|
243
|
+
/**
|
|
244
|
+
* Preference order: a fallback overflow, then the primary's overflow, then
|
|
245
|
+
* whichever failure came last. An overflow is the only one of the three a
|
|
246
|
+
* caller can act on, and the fallback's carries the client attribution that
|
|
247
|
+
* makes a correct retry budget possible.
|
|
248
|
+
*/
|
|
249
|
+
const preferred = overflowCandidates[0]?.error ?? primaryOverflowError ?? lastError;
|
|
250
|
+
if (overflowCandidates.length > 0 && typeof preferred === "object" && preferred !== null) fallbackOverflowCandidates.set(preferred, overflowCandidates);
|
|
251
|
+
if (preferred !== void 0) throw preferred;
|
|
197
252
|
}
|
|
198
253
|
//#endregion
|
|
199
254
|
exports.attemptInvoke = attemptInvoke;
|
|
255
|
+
exports.getFallbackErrorContext = getFallbackErrorContext;
|
|
256
|
+
exports.getFallbackOverflowCandidates = getFallbackOverflowCandidates;
|
|
200
257
|
exports.tryFallbackProviders = tryFallbackProviders;
|
|
201
258
|
|
|
202
259
|
//# sourceMappingURL=invoke.cjs.map
|