@librechat/agents 3.3.12 → 3.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (109) hide show
  1. package/dist/cjs/graphs/Graph.cjs +10 -0
  2. package/dist/cjs/graphs/Graph.cjs.map +1 -1
  3. package/dist/cjs/instrumentation.cjs +1 -0
  4. package/dist/cjs/instrumentation.cjs.map +1 -1
  5. package/dist/cjs/langfuseSpanRegistry.cjs +6 -3
  6. package/dist/cjs/langfuseSpanRegistry.cjs.map +1 -1
  7. package/dist/cjs/llm/anthropic/index.cjs +35 -206
  8. package/dist/cjs/llm/anthropic/index.cjs.map +1 -1
  9. package/dist/cjs/llm/bedrock/index.cjs +121 -241
  10. package/dist/cjs/llm/bedrock/index.cjs.map +1 -1
  11. package/dist/cjs/llm/google/index.cjs +19 -12
  12. package/dist/cjs/llm/google/index.cjs.map +1 -1
  13. package/dist/cjs/llm/mistral/index.cjs +26 -0
  14. package/dist/cjs/llm/mistral/index.cjs.map +1 -0
  15. package/dist/cjs/llm/openai/index.cjs +82 -80
  16. package/dist/cjs/llm/openai/index.cjs.map +1 -1
  17. package/dist/cjs/llm/openrouter/index.cjs.map +1 -1
  18. package/dist/cjs/llm/providers.cjs +19 -19
  19. package/dist/cjs/llm/providers.cjs.map +1 -1
  20. package/dist/cjs/llm/stream/chunkAdapters.cjs +198 -0
  21. package/dist/cjs/llm/stream/chunkAdapters.cjs.map +1 -0
  22. package/dist/cjs/llm/stream/smoother.cjs +369 -0
  23. package/dist/cjs/llm/stream/smoother.cjs.map +1 -0
  24. package/dist/cjs/llm/vertexai/index.cjs +13 -1
  25. package/dist/cjs/llm/vertexai/index.cjs.map +1 -1
  26. package/dist/cjs/main.cjs +18 -10
  27. package/dist/cjs/tools/subagent/SubagentExecutor.cjs +34 -2
  28. package/dist/cjs/tools/subagent/SubagentExecutor.cjs.map +1 -1
  29. package/dist/cjs/utils/tokens.cjs +76 -29
  30. package/dist/cjs/utils/tokens.cjs.map +1 -1
  31. package/dist/esm/graphs/Graph.mjs +10 -0
  32. package/dist/esm/graphs/Graph.mjs.map +1 -1
  33. package/dist/esm/instrumentation.mjs +1 -0
  34. package/dist/esm/instrumentation.mjs.map +1 -1
  35. package/dist/esm/langfuseSpanRegistry.mjs +6 -3
  36. package/dist/esm/langfuseSpanRegistry.mjs.map +1 -1
  37. package/dist/esm/llm/anthropic/index.mjs +34 -205
  38. package/dist/esm/llm/anthropic/index.mjs.map +1 -1
  39. package/dist/esm/llm/bedrock/index.mjs +120 -240
  40. package/dist/esm/llm/bedrock/index.mjs.map +1 -1
  41. package/dist/esm/llm/google/index.mjs +19 -12
  42. package/dist/esm/llm/google/index.mjs.map +1 -1
  43. package/dist/esm/llm/mistral/index.mjs +26 -0
  44. package/dist/esm/llm/mistral/index.mjs.map +1 -0
  45. package/dist/esm/llm/openai/index.mjs +82 -80
  46. package/dist/esm/llm/openai/index.mjs.map +1 -1
  47. package/dist/esm/llm/openrouter/index.mjs.map +1 -1
  48. package/dist/esm/llm/providers.mjs +3 -3
  49. package/dist/esm/llm/providers.mjs.map +1 -1
  50. package/dist/esm/llm/stream/chunkAdapters.mjs +195 -0
  51. package/dist/esm/llm/stream/chunkAdapters.mjs.map +1 -0
  52. package/dist/esm/llm/stream/smoother.mjs +365 -0
  53. package/dist/esm/llm/stream/smoother.mjs.map +1 -0
  54. package/dist/esm/llm/vertexai/index.mjs +13 -1
  55. package/dist/esm/llm/vertexai/index.mjs.map +1 -1
  56. package/dist/esm/main.mjs +4 -2
  57. package/dist/esm/tools/subagent/SubagentExecutor.mjs +34 -2
  58. package/dist/esm/tools/subagent/SubagentExecutor.mjs.map +1 -1
  59. package/dist/esm/utils/tokens.mjs +76 -30
  60. package/dist/esm/utils/tokens.mjs.map +1 -1
  61. package/dist/types/graphs/Graph.d.ts +3 -0
  62. package/dist/types/index.d.ts +3 -0
  63. package/dist/types/llm/google/index.d.ts +2 -0
  64. package/dist/types/llm/mistral/index.d.ts +11 -0
  65. package/dist/types/llm/openai/index.d.ts +20 -4
  66. package/dist/types/llm/openrouter/index.d.ts +4 -1
  67. package/dist/types/llm/stream/chunkAdapters.d.ts +48 -0
  68. package/dist/types/llm/stream/smoother.d.ts +95 -0
  69. package/dist/types/llm/vertexai/index.d.ts +2 -0
  70. package/dist/types/tools/subagent/SubagentExecutor.d.ts +3 -0
  71. package/dist/types/types/graph.d.ts +5 -0
  72. package/dist/types/types/llm.d.ts +21 -14
  73. package/dist/types/utils/tokens.d.ts +10 -0
  74. package/package.json +2 -2
  75. package/src/graphs/Graph.ts +11 -0
  76. package/src/index.ts +8 -0
  77. package/src/instrumentation.ts +1 -0
  78. package/src/langfuseSpanRegistry.ts +9 -0
  79. package/src/llm/anthropic/index.ts +85 -354
  80. package/src/llm/bedrock/index.ts +174 -390
  81. package/src/llm/bedrock/llm.spec.ts +2 -0
  82. package/src/llm/bedrock/streamSealDispatch.test.ts +76 -0
  83. package/src/llm/custom-chat-models.smoke.test.ts +16 -1
  84. package/src/llm/google/index.ts +17 -18
  85. package/src/llm/google/streamSmoothing.test.ts +121 -0
  86. package/src/llm/mistral/index.ts +33 -0
  87. package/src/llm/mistral/streamSmoothing.test.ts +97 -0
  88. package/src/llm/openai/deepseek.test.ts +56 -0
  89. package/src/llm/openai/index.ts +119 -126
  90. package/src/llm/openrouter/index.ts +4 -1
  91. package/src/llm/providers.ts +3 -3
  92. package/src/llm/stream/chunkAdapters.test.ts +202 -0
  93. package/src/llm/stream/chunkAdapters.ts +317 -0
  94. package/src/llm/stream/reassembly.test.ts +241 -0
  95. package/src/llm/stream/smoother.bench.test.ts +155 -0
  96. package/src/llm/stream/smoother.test.ts +519 -0
  97. package/src/llm/stream/smoother.ts +574 -0
  98. package/src/llm/vertexai/index.ts +17 -1
  99. package/src/llm/vertexai/streamSmoothing.test.ts +109 -0
  100. package/src/specs/langfuse-instrumentation.test.ts +26 -0
  101. package/src/specs/langfuse-span-registry.test.ts +17 -0
  102. package/src/specs/subagent.test.ts +44 -0
  103. package/src/specs/summarization.test.ts +5 -2
  104. package/src/specs/tokens.test.ts +159 -14
  105. package/src/tools/__tests__/SubagentExecutor.test.ts +48 -1
  106. package/src/tools/subagent/SubagentExecutor.ts +69 -5
  107. package/src/types/graph.ts +5 -0
  108. package/src/types/llm.ts +53 -36
  109. package/src/utils/tokens.ts +115 -30
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../../../../src/llm/openrouter/index.ts"],"sourcesContent":["import type {\n ChatOpenAICallOptions,\n OpenAIChatInput,\n OpenAIClient,\n} from '@langchain/openai';\nimport type { CallbackManagerForLLMRun } from '@langchain/core/callbacks/manager';\nimport type { ChatGenerationChunk } from '@langchain/core/outputs';\nimport type { BaseMessage } from '@langchain/core/messages';\nimport type { SeenScalarMetadata } from '@/llm/openai/streamMetadata';\nimport type { PromptCacheTtl } from '@/messages/cache';\nimport { dropRepeatedScalarMetadata } from '@/llm/openai/streamMetadata';\nimport { ChatOpenAI, emitStreamChunkCallback } from '@/llm/openai';\n\nexport type OpenRouterReasoningEffort =\n | 'xhigh'\n | 'high'\n | 'medium'\n | 'low'\n | 'minimal'\n | 'none';\n\nexport interface OpenRouterReasoning {\n effort?: OpenRouterReasoningEffort;\n max_tokens?: number;\n exclude?: boolean;\n enabled?: boolean;\n}\n\nexport interface ChatOpenRouterCallOptions\n extends Omit<ChatOpenAICallOptions, 'reasoning'> {\n /** @deprecated Use `reasoning` object instead */\n include_reasoning?: boolean;\n reasoning?: OpenRouterReasoning;\n modelKwargs?: OpenAIChatInput['modelKwargs'];\n useResponsesApi?: boolean;\n promptCache?: boolean;\n /**\n * Prompt-cache breakpoint TTL. Defaults to `'1h'` (extended cache) when\n * `promptCache` is enabled; set `'5m'` for the legacy 5-minute behavior.\n * OpenRouter forwards this to Claude upstreams (Anthropic / Bedrock / Vertex),\n * which downgrade to 5m where the extended TTL isn't supported.\n */\n promptCacheTtl?: PromptCacheTtl;\n}\n\nexport type ChatOpenRouterInput = Partial<\n ChatOpenRouterCallOptions & OpenAIChatInput\n>;\n\n/** invocationParams return type extended with OpenRouter reasoning */\nexport type OpenRouterInvocationParams = Omit<\n OpenAIClient.Chat.ChatCompletionCreateParams,\n 'messages'\n> & {\n reasoning?: OpenRouterReasoning;\n cache_control?: { type: 'ephemeral'; ttl?: '1h' };\n};\n\ntype InvocationParamsExtra = {\n streaming?: boolean;\n};\n\ninterface OpenRouterReasoningTextDetail {\n type: 'reasoning.text';\n text?: string;\n format?: string;\n index?: number;\n}\n\ninterface OpenRouterReasoningEncryptedDetail {\n type: 'reasoning.encrypted';\n id?: string;\n data?: string;\n format?: string;\n index?: number;\n}\n\ntype OpenRouterReasoningDetail =\n | OpenRouterReasoningTextDetail\n | OpenRouterReasoningEncryptedDetail;\n\nfunction isReasoningTextDetail(\n value: unknown\n): value is OpenRouterReasoningTextDetail {\n return (\n typeof value === 'object' &&\n value !== null &&\n 'type' in value &&\n value.type === 'reasoning.text'\n );\n}\n\nfunction isReasoningEncryptedDetail(\n value: unknown\n): value is OpenRouterReasoningEncryptedDetail {\n return (\n typeof value === 'object' &&\n value !== null &&\n 'type' in value &&\n value.type === 'reasoning.encrypted'\n );\n}\n\nfunction getReasoningDetails(value: unknown): OpenRouterReasoningDetail[] {\n if (!Array.isArray(value)) {\n return [];\n }\n return value.filter(\n (detail): detail is OpenRouterReasoningDetail =>\n isReasoningTextDetail(detail) || isReasoningEncryptedDetail(detail)\n );\n}\n\nexport class ChatOpenRouter extends ChatOpenAI {\n private openRouterReasoning?: OpenRouterReasoning;\n /** @deprecated Use `reasoning` object instead */\n private includeReasoning?: boolean;\n\n constructor(_fields: ChatOpenRouterInput) {\n const fieldsWithoutPromptCache: ChatOpenRouterInput = { ..._fields };\n delete fieldsWithoutPromptCache.promptCache;\n delete fieldsWithoutPromptCache.promptCacheTtl;\n\n const {\n include_reasoning,\n reasoning: openRouterReasoning,\n modelKwargs = {},\n ...fields\n } = fieldsWithoutPromptCache;\n\n // Extract reasoning from modelKwargs if provided there (e.g., from LLMConfig)\n const { reasoning: mkReasoning, ...restModelKwargs } = modelKwargs as {\n reasoning?: OpenRouterReasoning;\n } & Record<string, unknown>;\n const mergedReasoning =\n mkReasoning != null || openRouterReasoning != null\n ? {\n ...mkReasoning,\n ...openRouterReasoning,\n }\n : undefined;\n const runtimeReasoning =\n mergedReasoning ??\n (include_reasoning === true ? { enabled: true } : undefined);\n const parentModelKwargs =\n runtimeReasoning == null\n ? restModelKwargs\n : { ...restModelKwargs, reasoning: runtimeReasoning };\n\n super({\n ...fields,\n modelKwargs: parentModelKwargs,\n includeReasoningDetails: true,\n convertReasoningDetailsToContent: true,\n preserveToolCacheControl: true,\n responsesPromptCache: _fields.promptCache,\n responsesPromptCacheTtl: _fields.promptCacheTtl,\n });\n\n // Merge reasoning config: modelKwargs.reasoning < constructor reasoning\n if (mergedReasoning != null) {\n this.openRouterReasoning = mergedReasoning;\n }\n\n this.includeReasoning = include_reasoning;\n }\n static lc_name(): 'LibreChatOpenRouter' {\n return 'LibreChatOpenRouter';\n }\n\n // OpenRouter widens OpenAI reasoning with extra effort levels ('xhigh' | 'none' | 'minimal').\n override invocationParams(\n options?: this['ParsedCallOptions'],\n extra?: InvocationParamsExtra\n ): OpenRouterInvocationParams {\n type MutableParams = Omit<\n OpenAIClient.Chat.ChatCompletionCreateParams,\n 'messages'\n > & {\n reasoning_effort?: string;\n reasoning?: OpenRouterReasoning;\n cache_control?: { type: 'ephemeral'; ttl?: '1h' };\n };\n\n const optionsWithDefaults = this._combineCallOptions(options);\n const useResponsesApi = this._useResponsesApi(options);\n const params = (\n useResponsesApi\n ? this.responses.invocationParams(optionsWithDefaults)\n : this.completions.invocationParams(optionsWithDefaults, extra)\n ) as MutableParams;\n\n // Remove the OpenAI-native reasoning_effort that the parent sets;\n // OpenRouter uses a `reasoning` object instead\n delete params.reasoning_effort;\n\n // Build the OpenRouter reasoning config\n const reasoning = this.buildOpenRouterReasoning(optionsWithDefaults);\n if (reasoning != null) {\n params.reasoning = reasoning;\n } else {\n delete params.reasoning;\n }\n\n return params;\n }\n\n private buildOpenRouterReasoning(\n options?: this['ParsedCallOptions']\n ): OpenRouterReasoning | undefined {\n let reasoning: OpenRouterReasoning | undefined;\n\n // 1. Instance-level reasoning config (from constructor)\n if (this.openRouterReasoning != null) {\n reasoning = { ...this.openRouterReasoning };\n }\n\n // 2. LangChain-style reasoning params (from parent's `this.reasoning`)\n const lcReasoning = this.getReasoningParams(options);\n if (lcReasoning?.effort != null) {\n reasoning = {\n ...reasoning,\n effort: lcReasoning.effort as OpenRouterReasoningEffort,\n };\n }\n\n // 3. Call-level reasoning override\n const callReasoning = (options as ChatOpenRouterCallOptions | undefined)\n ?.reasoning;\n if (callReasoning != null) {\n reasoning = { ...reasoning, ...callReasoning };\n }\n\n // 4. Legacy include_reasoning backward compatibility\n if (reasoning == null && this.includeReasoning === true) {\n reasoning = { enabled: true };\n }\n\n return reasoning;\n }\n\n override async *_streamResponseChunks(\n messages: BaseMessage[],\n options: this['ParsedCallOptions'],\n runManager?: CallbackManagerForLLMRun\n ): AsyncGenerator<ChatGenerationChunk> {\n const reasoningTextByIndex = new Map<\n number,\n OpenRouterReasoningTextDetail\n >();\n const reasoningEncryptedById = new Map<\n string,\n OpenRouterReasoningEncryptedDetail\n >();\n // Consume the raw (un-deduped) stream so `finish_reason` survives as the\n // reasoning-flush signal below; de-duplicate after that, before emitting.\n const seenScalarMetadata: SeenScalarMetadata = new Map();\n\n for await (const generationChunk of super._streamRawResponseChunks(\n messages,\n options,\n undefined\n )) {\n let currentReasoningText = '';\n const reasoningDetails = getReasoningDetails(\n generationChunk.message.additional_kwargs.reasoning_details\n );\n\n for (const detail of reasoningDetails) {\n if (detail.type === 'reasoning.text') {\n currentReasoningText += detail.text ?? '';\n const index = detail.index ?? 0;\n const existing = reasoningTextByIndex.get(index);\n if (existing != null) {\n existing.text = `${existing.text ?? ''}${detail.text ?? ''}`;\n continue;\n }\n reasoningTextByIndex.set(index, {\n ...detail,\n text: detail.text ?? '',\n });\n continue;\n }\n if (detail.id != null) {\n reasoningEncryptedById.set(detail.id, { ...detail });\n }\n }\n\n if (\n currentReasoningText.length > 0 &&\n generationChunk.message.additional_kwargs.reasoning == null\n ) {\n generationChunk.message.additional_kwargs.reasoning =\n currentReasoningText;\n }\n\n if (generationChunk.generationInfo?.finish_reason != null) {\n const finalReasoningDetails = [\n ...reasoningTextByIndex.values(),\n ...reasoningEncryptedById.values(),\n ];\n if (finalReasoningDetails.length > 0) {\n generationChunk.message.additional_kwargs.reasoning_details =\n finalReasoningDetails;\n } else {\n delete generationChunk.message.additional_kwargs.reasoning_details;\n }\n } else {\n delete generationChunk.message.additional_kwargs.reasoning_details;\n }\n\n dropRepeatedScalarMetadata(generationChunk, seenScalarMetadata);\n await emitStreamChunkCallback(generationChunk, runManager);\n yield generationChunk;\n }\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../../../src/llm/openrouter/index.ts"],"sourcesContent":["import type {\n ChatOpenAICallOptions,\n OpenAIChatInput,\n OpenAIClient,\n} from '@langchain/openai';\nimport type { CallbackManagerForLLMRun } from '@langchain/core/callbacks/manager';\nimport type { ChatGenerationChunk } from '@langchain/core/outputs';\nimport type { BaseMessage } from '@langchain/core/messages';\nimport type { SeenScalarMetadata } from '@/llm/openai/streamMetadata';\nimport type { PromptCacheTtl } from '@/messages/cache';\nimport { dropRepeatedScalarMetadata } from '@/llm/openai/streamMetadata';\nimport { ChatOpenAI, emitStreamChunkCallback } from '@/llm/openai';\n\nexport type OpenRouterReasoningEffort =\n | 'xhigh'\n | 'high'\n | 'medium'\n | 'low'\n | 'minimal'\n | 'none';\n\nexport interface OpenRouterReasoning {\n effort?: OpenRouterReasoningEffort;\n max_tokens?: number;\n exclude?: boolean;\n enabled?: boolean;\n}\n\nexport interface ChatOpenRouterCallOptions\n extends Omit<ChatOpenAICallOptions, 'reasoning'> {\n /** @deprecated Use `reasoning` object instead */\n include_reasoning?: boolean;\n reasoning?: OpenRouterReasoning;\n modelKwargs?: OpenAIChatInput['modelKwargs'];\n useResponsesApi?: boolean;\n promptCache?: boolean;\n /**\n * Prompt-cache breakpoint TTL. Defaults to `'1h'` (extended cache) when\n * `promptCache` is enabled; set `'5m'` for the legacy 5-minute behavior.\n * OpenRouter forwards this to Claude upstreams (Anthropic / Bedrock / Vertex),\n * which downgrade to 5m where the extended TTL isn't supported.\n */\n promptCacheTtl?: PromptCacheTtl;\n}\n\nexport type ChatOpenRouterInput = Partial<\n ChatOpenRouterCallOptions & OpenAIChatInput\n> & {\n /** Minimum delay in ms between visible streamed deltas (default 25; 0 disables). */\n _lc_stream_delay?: number;\n};\n\n/** invocationParams return type extended with OpenRouter reasoning */\nexport type OpenRouterInvocationParams = Omit<\n OpenAIClient.Chat.ChatCompletionCreateParams,\n 'messages'\n> & {\n reasoning?: OpenRouterReasoning;\n cache_control?: { type: 'ephemeral'; ttl?: '1h' };\n};\n\ntype InvocationParamsExtra = {\n streaming?: boolean;\n};\n\ninterface OpenRouterReasoningTextDetail {\n type: 'reasoning.text';\n text?: string;\n format?: string;\n index?: number;\n}\n\ninterface OpenRouterReasoningEncryptedDetail {\n type: 'reasoning.encrypted';\n id?: string;\n data?: string;\n format?: string;\n index?: number;\n}\n\ntype OpenRouterReasoningDetail =\n | OpenRouterReasoningTextDetail\n | OpenRouterReasoningEncryptedDetail;\n\nfunction isReasoningTextDetail(\n value: unknown\n): value is OpenRouterReasoningTextDetail {\n return (\n typeof value === 'object' &&\n value !== null &&\n 'type' in value &&\n value.type === 'reasoning.text'\n );\n}\n\nfunction isReasoningEncryptedDetail(\n value: unknown\n): value is OpenRouterReasoningEncryptedDetail {\n return (\n typeof value === 'object' &&\n value !== null &&\n 'type' in value &&\n value.type === 'reasoning.encrypted'\n );\n}\n\nfunction getReasoningDetails(value: unknown): OpenRouterReasoningDetail[] {\n if (!Array.isArray(value)) {\n return [];\n }\n return value.filter(\n (detail): detail is OpenRouterReasoningDetail =>\n isReasoningTextDetail(detail) || isReasoningEncryptedDetail(detail)\n );\n}\n\nexport class ChatOpenRouter extends ChatOpenAI {\n private openRouterReasoning?: OpenRouterReasoning;\n /** @deprecated Use `reasoning` object instead */\n private includeReasoning?: boolean;\n\n constructor(_fields: ChatOpenRouterInput) {\n const fieldsWithoutPromptCache: ChatOpenRouterInput = { ..._fields };\n delete fieldsWithoutPromptCache.promptCache;\n delete fieldsWithoutPromptCache.promptCacheTtl;\n\n const {\n include_reasoning,\n reasoning: openRouterReasoning,\n modelKwargs = {},\n ...fields\n } = fieldsWithoutPromptCache;\n\n // Extract reasoning from modelKwargs if provided there (e.g., from LLMConfig)\n const { reasoning: mkReasoning, ...restModelKwargs } = modelKwargs as {\n reasoning?: OpenRouterReasoning;\n } & Record<string, unknown>;\n const mergedReasoning =\n mkReasoning != null || openRouterReasoning != null\n ? {\n ...mkReasoning,\n ...openRouterReasoning,\n }\n : undefined;\n const runtimeReasoning =\n mergedReasoning ??\n (include_reasoning === true ? { enabled: true } : undefined);\n const parentModelKwargs =\n runtimeReasoning == null\n ? restModelKwargs\n : { ...restModelKwargs, reasoning: runtimeReasoning };\n\n super({\n ...fields,\n modelKwargs: parentModelKwargs,\n includeReasoningDetails: true,\n convertReasoningDetailsToContent: true,\n preserveToolCacheControl: true,\n responsesPromptCache: _fields.promptCache,\n responsesPromptCacheTtl: _fields.promptCacheTtl,\n });\n\n // Merge reasoning config: modelKwargs.reasoning < constructor reasoning\n if (mergedReasoning != null) {\n this.openRouterReasoning = mergedReasoning;\n }\n\n this.includeReasoning = include_reasoning;\n }\n static lc_name(): 'LibreChatOpenRouter' {\n return 'LibreChatOpenRouter';\n }\n\n // OpenRouter widens OpenAI reasoning with extra effort levels ('xhigh' | 'none' | 'minimal').\n override invocationParams(\n options?: this['ParsedCallOptions'],\n extra?: InvocationParamsExtra\n ): OpenRouterInvocationParams {\n type MutableParams = Omit<\n OpenAIClient.Chat.ChatCompletionCreateParams,\n 'messages'\n > & {\n reasoning_effort?: string;\n reasoning?: OpenRouterReasoning;\n cache_control?: { type: 'ephemeral'; ttl?: '1h' };\n };\n\n const optionsWithDefaults = this._combineCallOptions(options);\n const useResponsesApi = this._useResponsesApi(options);\n const params = (\n useResponsesApi\n ? this.responses.invocationParams(optionsWithDefaults)\n : this.completions.invocationParams(optionsWithDefaults, extra)\n ) as MutableParams;\n\n // Remove the OpenAI-native reasoning_effort that the parent sets;\n // OpenRouter uses a `reasoning` object instead\n delete params.reasoning_effort;\n\n // Build the OpenRouter reasoning config\n const reasoning = this.buildOpenRouterReasoning(optionsWithDefaults);\n if (reasoning != null) {\n params.reasoning = reasoning;\n } else {\n delete params.reasoning;\n }\n\n return params;\n }\n\n private buildOpenRouterReasoning(\n options?: this['ParsedCallOptions']\n ): OpenRouterReasoning | undefined {\n let reasoning: OpenRouterReasoning | undefined;\n\n // 1. Instance-level reasoning config (from constructor)\n if (this.openRouterReasoning != null) {\n reasoning = { ...this.openRouterReasoning };\n }\n\n // 2. LangChain-style reasoning params (from parent's `this.reasoning`)\n const lcReasoning = this.getReasoningParams(options);\n if (lcReasoning?.effort != null) {\n reasoning = {\n ...reasoning,\n effort: lcReasoning.effort as OpenRouterReasoningEffort,\n };\n }\n\n // 3. Call-level reasoning override\n const callReasoning = (options as ChatOpenRouterCallOptions | undefined)\n ?.reasoning;\n if (callReasoning != null) {\n reasoning = { ...reasoning, ...callReasoning };\n }\n\n // 4. Legacy include_reasoning backward compatibility\n if (reasoning == null && this.includeReasoning === true) {\n reasoning = { enabled: true };\n }\n\n return reasoning;\n }\n\n override async *_streamResponseChunks(\n messages: BaseMessage[],\n options: this['ParsedCallOptions'],\n runManager?: CallbackManagerForLLMRun\n ): AsyncGenerator<ChatGenerationChunk> {\n const reasoningTextByIndex = new Map<\n number,\n OpenRouterReasoningTextDetail\n >();\n const reasoningEncryptedById = new Map<\n string,\n OpenRouterReasoningEncryptedDetail\n >();\n // Consume the raw (un-deduped) stream so `finish_reason` survives as the\n // reasoning-flush signal below; de-duplicate after that, before emitting.\n const seenScalarMetadata: SeenScalarMetadata = new Map();\n\n for await (const generationChunk of super._streamRawResponseChunks(\n messages,\n options,\n undefined\n )) {\n let currentReasoningText = '';\n const reasoningDetails = getReasoningDetails(\n generationChunk.message.additional_kwargs.reasoning_details\n );\n\n for (const detail of reasoningDetails) {\n if (detail.type === 'reasoning.text') {\n currentReasoningText += detail.text ?? '';\n const index = detail.index ?? 0;\n const existing = reasoningTextByIndex.get(index);\n if (existing != null) {\n existing.text = `${existing.text ?? ''}${detail.text ?? ''}`;\n continue;\n }\n reasoningTextByIndex.set(index, {\n ...detail,\n text: detail.text ?? '',\n });\n continue;\n }\n if (detail.id != null) {\n reasoningEncryptedById.set(detail.id, { ...detail });\n }\n }\n\n if (\n currentReasoningText.length > 0 &&\n generationChunk.message.additional_kwargs.reasoning == null\n ) {\n generationChunk.message.additional_kwargs.reasoning =\n currentReasoningText;\n }\n\n if (generationChunk.generationInfo?.finish_reason != null) {\n const finalReasoningDetails = [\n ...reasoningTextByIndex.values(),\n ...reasoningEncryptedById.values(),\n ];\n if (finalReasoningDetails.length > 0) {\n generationChunk.message.additional_kwargs.reasoning_details =\n finalReasoningDetails;\n } else {\n delete generationChunk.message.additional_kwargs.reasoning_details;\n }\n } else {\n delete generationChunk.message.additional_kwargs.reasoning_details;\n }\n\n dropRepeatedScalarMetadata(generationChunk, seenScalarMetadata);\n await emitStreamChunkCallback(generationChunk, runManager);\n yield generationChunk;\n }\n }\n}\n"],"mappings":";;;AAoFA,SAAS,sBACP,OACwC;CACxC,OACE,OAAO,UAAU,YACjB,UAAU,QACV,UAAU,SACV,MAAM,SAAS;AAEnB;AAEA,SAAS,2BACP,OAC6C;CAC7C,OACE,OAAO,UAAU,YACjB,UAAU,QACV,UAAU,SACV,MAAM,SAAS;AAEnB;AAEA,SAAS,oBAAoB,OAA6C;CACxE,IAAI,CAAC,MAAM,QAAQ,KAAK,GACtB,OAAO,CAAC;CAEV,OAAO,MAAM,QACV,WACC,sBAAsB,MAAM,KAAK,2BAA2B,MAAM,CACtE;AACF;AAEA,IAAa,iBAAb,cAAoC,WAAW;CAC7C;;CAEA;CAEA,YAAY,SAA8B;EACxC,MAAM,2BAAgD,EAAE,GAAG,QAAQ;EACnE,OAAO,yBAAyB;EAChC,OAAO,yBAAyB;EAEhC,MAAM,EACJ,mBACA,WAAW,qBACX,cAAc,CAAC,GACf,GAAG,WACD;EAGJ,MAAM,EAAE,WAAW,aAAa,GAAG,oBAAoB;EAGvD,MAAM,kBACJ,eAAe,QAAQ,uBAAuB,OAC1C;GACA,GAAG;GACH,GAAG;EACL,IACE,KAAA;EACN,MAAM,mBACJ,oBACC,sBAAsB,OAAO,EAAE,SAAS,KAAK,IAAI,KAAA;EACpD,MAAM,oBACJ,oBAAoB,OAChB,kBACA;GAAE,GAAG;GAAiB,WAAW;EAAiB;EAExD,MAAM;GACJ,GAAG;GACH,aAAa;GACb,yBAAyB;GACzB,kCAAkC;GAClC,0BAA0B;GAC1B,sBAAsB,QAAQ;GAC9B,yBAAyB,QAAQ;EACnC,CAAC;EAGD,IAAI,mBAAmB,MACrB,KAAK,sBAAsB;EAG7B,KAAK,mBAAmB;CAC1B;CACA,OAAO,UAAiC;EACtC,OAAO;CACT;CAGA,iBACE,SACA,OAC4B;EAU5B,MAAM,sBAAsB,KAAK,oBAAoB,OAAO;EAE5D,MAAM,SADkB,KAAK,iBAAiB,OAE9B,IACV,KAAK,UAAU,iBAAiB,mBAAmB,IACnD,KAAK,YAAY,iBAAiB,qBAAqB,KAAK;EAKlE,OAAO,OAAO;EAGd,MAAM,YAAY,KAAK,yBAAyB,mBAAmB;EACnE,IAAI,aAAa,MACf,OAAO,YAAY;OAEnB,OAAO,OAAO;EAGhB,OAAO;CACT;CAEA,yBACE,SACiC;EACjC,IAAI;EAGJ,IAAI,KAAK,uBAAuB,MAC9B,YAAY,EAAE,GAAG,KAAK,oBAAoB;EAI5C,MAAM,cAAc,KAAK,mBAAmB,OAAO;EACnD,IAAI,aAAa,UAAU,MACzB,YAAY;GACV,GAAG;GACH,QAAQ,YAAY;EACtB;EAIF,MAAM,gBAAiB,SACnB;EACJ,IAAI,iBAAiB,MACnB,YAAY;GAAE,GAAG;GAAW,GAAG;EAAc;EAI/C,IAAI,aAAa,QAAQ,KAAK,qBAAqB,MACjD,YAAY,EAAE,SAAS,KAAK;EAG9B,OAAO;CACT;CAEA,OAAgB,sBACd,UACA,SACA,YACqC;EACrC,MAAM,uCAAuB,IAAI,IAG/B;EACF,MAAM,yCAAyB,IAAI,IAGjC;EAGF,MAAM,qCAAyC,IAAI,IAAI;EAEvD,WAAW,MAAM,mBAAmB,MAAM,yBACxC,UACA,SACA,KAAA,CACF,GAAG;GACD,IAAI,uBAAuB;GAC3B,MAAM,mBAAmB,oBACvB,gBAAgB,QAAQ,kBAAkB,iBAC5C;GAEA,KAAK,MAAM,UAAU,kBAAkB;IACrC,IAAI,OAAO,SAAS,kBAAkB;KACpC,wBAAwB,OAAO,QAAQ;KACvC,MAAM,QAAQ,OAAO,SAAS;KAC9B,MAAM,WAAW,qBAAqB,IAAI,KAAK;KAC/C,IAAI,YAAY,MAAM;MACpB,SAAS,OAAO,GAAG,SAAS,QAAQ,KAAK,OAAO,QAAQ;MACxD;KACF;KACA,qBAAqB,IAAI,OAAO;MAC9B,GAAG;MACH,MAAM,OAAO,QAAQ;KACvB,CAAC;KACD;IACF;IACA,IAAI,OAAO,MAAM,MACf,uBAAuB,IAAI,OAAO,IAAI,EAAE,GAAG,OAAO,CAAC;GAEvD;GAEA,IACE,qBAAqB,SAAS,KAC9B,gBAAgB,QAAQ,kBAAkB,aAAa,MAEvD,gBAAgB,QAAQ,kBAAkB,YACxC;GAGJ,IAAI,gBAAgB,gBAAgB,iBAAiB,MAAM;IACzD,MAAM,wBAAwB,CAC5B,GAAG,qBAAqB,OAAO,GAC/B,GAAG,uBAAuB,OAAO,CACnC;IACA,IAAI,sBAAsB,SAAS,GACjC,gBAAgB,QAAQ,kBAAkB,oBACxC;SAEF,OAAO,gBAAgB,QAAQ,kBAAkB;GAErD,OACE,OAAO,gBAAgB,QAAQ,kBAAkB;GAGnD,2BAA2B,iBAAiB,kBAAkB;GAC9D,MAAM,wBAAwB,iBAAiB,UAAU;GACzD,MAAM;EACR;CACF;AACF"}
@@ -1,12 +1,12 @@
1
1
  import "../common/enum.mjs";
2
2
  import "../common/index.mjs";
3
+ import { CustomChatMistralAI } from "./mistral/index.mjs";
3
4
  import { AzureChatOpenAI, ChatDeepSeek, ChatMoonshot, ChatOpenAI, ChatXAI } from "./openai/index.mjs";
4
5
  import { CustomChatGoogleGenerativeAI } from "./google/index.mjs";
5
6
  import { CustomChatBedrockConverse } from "./bedrock/index.mjs";
6
7
  import { CustomAnthropic } from "./anthropic/index.mjs";
7
8
  import { ChatOpenRouter } from "./openrouter/index.mjs";
8
9
  import { ChatVertexAI } from "./vertexai/index.mjs";
9
- import { ChatMistralAI } from "@langchain/mistralai";
10
10
  //#region src/llm/providers.ts
11
11
  const llmProviders = {
12
12
  ["xai"]: ChatXAI,
@@ -14,8 +14,8 @@ const llmProviders = {
14
14
  ["azureOpenAI"]: AzureChatOpenAI,
15
15
  ["vertexai"]: ChatVertexAI,
16
16
  ["deepseek"]: ChatDeepSeek,
17
- ["mistralai"]: ChatMistralAI,
18
- ["mistral"]: ChatMistralAI,
17
+ ["mistralai"]: CustomChatMistralAI,
18
+ ["mistral"]: CustomChatMistralAI,
19
19
  ["anthropic"]: CustomAnthropic,
20
20
  ["openrouter"]: ChatOpenRouter,
21
21
  ["bedrock"]: CustomChatBedrockConverse,
@@ -1 +1 @@
1
- {"version":3,"file":"providers.mjs","names":[],"sources":["../../../src/llm/providers.ts"],"sourcesContent":["// src/llm/providers.ts\nimport { ChatMistralAI } from '@langchain/mistralai';\nimport type {\n ChatModelConstructorMap,\n ProviderOptionsMap,\n ChatModelMap,\n} from '@/types';\nimport {\n AzureChatOpenAI,\n ChatDeepSeek,\n ChatMoonshot,\n ChatOpenAI,\n ChatXAI,\n} from '@/llm/openai';\nimport { CustomChatGoogleGenerativeAI } from '@/llm/google';\nimport { CustomChatBedrockConverse } from '@/llm/bedrock';\nimport { CustomAnthropic } from '@/llm/anthropic';\nimport { ChatOpenRouter } from '@/llm/openrouter';\nimport { ChatVertexAI } from '@/llm/vertexai';\nimport { Providers } from '@/common';\n\nexport const llmProviders: Partial<ChatModelConstructorMap> = {\n [Providers.XAI]: ChatXAI,\n [Providers.OPENAI]: ChatOpenAI,\n [Providers.AZURE]: AzureChatOpenAI,\n [Providers.VERTEXAI]: ChatVertexAI,\n [Providers.DEEPSEEK]: ChatDeepSeek,\n [Providers.MISTRALAI]: ChatMistralAI,\n [Providers.MISTRAL]: ChatMistralAI,\n [Providers.ANTHROPIC]: CustomAnthropic,\n [Providers.OPENROUTER]: ChatOpenRouter,\n [Providers.BEDROCK]: CustomChatBedrockConverse,\n // [Providers.ANTHROPIC]: ChatAnthropic,\n [Providers.GOOGLE]: CustomChatGoogleGenerativeAI,\n [Providers.MOONSHOT]: ChatMoonshot,\n};\n\nexport const manualToolStreamProviders = new Set<Providers | string>([\n Providers.ANTHROPIC,\n Providers.BEDROCK,\n]);\n\nexport const getChatModelClass = <P extends Providers>(\n provider: P\n): new (config: ProviderOptionsMap[P]) => ChatModelMap[P] => {\n const ChatModelClass = llmProviders[provider];\n if (!ChatModelClass) {\n throw new Error(`Unsupported LLM provider: ${provider}`);\n }\n\n return ChatModelClass;\n};\n"],"mappings":";;;;;;;;;;AAqBA,MAAa,eAAiD;UAC3C;aACG;kBACD;eACG;eACA;gBACC;cACF;gBACE;iBACC;cACH;aAED;eACE;AACxB;AAEA,MAAa,4BAA4B,IAAI,IAAwB,CAAA,aAAA,SAGrE,CAAC;AAED,MAAa,qBACX,aAC2D;CAC3D,MAAM,iBAAiB,aAAa;CACpC,IAAI,CAAC,gBACH,MAAM,IAAI,MAAM,6BAA6B,UAAU;CAGzD,OAAO;AACT"}
1
+ {"version":3,"file":"providers.mjs","names":[],"sources":["../../../src/llm/providers.ts"],"sourcesContent":["// src/llm/providers.ts\nimport { CustomChatMistralAI } from '@/llm/mistral';\nimport type {\n ChatModelConstructorMap,\n ProviderOptionsMap,\n ChatModelMap,\n} from '@/types';\nimport {\n AzureChatOpenAI,\n ChatDeepSeek,\n ChatMoonshot,\n ChatOpenAI,\n ChatXAI,\n} from '@/llm/openai';\nimport { CustomChatGoogleGenerativeAI } from '@/llm/google';\nimport { CustomChatBedrockConverse } from '@/llm/bedrock';\nimport { CustomAnthropic } from '@/llm/anthropic';\nimport { ChatOpenRouter } from '@/llm/openrouter';\nimport { ChatVertexAI } from '@/llm/vertexai';\nimport { Providers } from '@/common';\n\nexport const llmProviders: Partial<ChatModelConstructorMap> = {\n [Providers.XAI]: ChatXAI,\n [Providers.OPENAI]: ChatOpenAI,\n [Providers.AZURE]: AzureChatOpenAI,\n [Providers.VERTEXAI]: ChatVertexAI,\n [Providers.DEEPSEEK]: ChatDeepSeek,\n [Providers.MISTRALAI]: CustomChatMistralAI,\n [Providers.MISTRAL]: CustomChatMistralAI,\n [Providers.ANTHROPIC]: CustomAnthropic,\n [Providers.OPENROUTER]: ChatOpenRouter,\n [Providers.BEDROCK]: CustomChatBedrockConverse,\n // [Providers.ANTHROPIC]: ChatAnthropic,\n [Providers.GOOGLE]: CustomChatGoogleGenerativeAI,\n [Providers.MOONSHOT]: ChatMoonshot,\n};\n\nexport const manualToolStreamProviders = new Set<Providers | string>([\n Providers.ANTHROPIC,\n Providers.BEDROCK,\n]);\n\nexport const getChatModelClass = <P extends Providers>(\n provider: P\n): new (config: ProviderOptionsMap[P]) => ChatModelMap[P] => {\n const ChatModelClass = llmProviders[provider];\n if (!ChatModelClass) {\n throw new Error(`Unsupported LLM provider: ${provider}`);\n }\n\n return ChatModelClass;\n};\n"],"mappings":";;;;;;;;;;AAqBA,MAAa,eAAiD;UAC3C;aACG;kBACD;eACG;eACA;gBACC;cACF;gBACE;iBACC;cACH;aAED;eACE;AACxB;AAEA,MAAa,4BAA4B,IAAI,IAAwB,CAAA,aAAA,SAGrE,CAAC;AAED,MAAa,qBACX,aAC2D;CAC3D,MAAM,iBAAiB,aAAa;CACpC,IAAI,CAAC,gBACH,MAAM,IAAI,MAAM,6BAA6B,UAAU;CAGzD,OAAO;AACT"}
@@ -0,0 +1,195 @@
1
+ import { smoothStream } from "./smoother.mjs";
2
+ import { AIMessageChunk } from "@langchain/core/messages";
3
+ import { ChatGenerationChunk } from "@langchain/core/outputs";
4
+ //#region src/llm/stream/chunkAdapters.ts
5
+ /**
6
+ * Rebuilds a generation chunk carrying one piece of a split plain-text chunk.
7
+ * Unsplit pieces return the original chunk untouched, so disabled smoothing is
8
+ * byte-identical to no smoothing. Usage metadata, additional kwargs and
9
+ * response metadata survive only on the first piece: the aggregator merges
10
+ * dicts by concatenating string fields, so replicating them across pieces
11
+ * would duplicate reasoning text and scalar metadata once per piece.
12
+ */
13
+ function cloneGenerationChunkPiece(chunk, piece) {
14
+ if (piece.isFirst && piece.isLast) return chunk;
15
+ const message = chunk.message;
16
+ return new ChatGenerationChunk({
17
+ text: piece.text,
18
+ generationInfo: piece.isFirst ? chunk.generationInfo : void 0,
19
+ message: new AIMessageChunk(Object.assign({}, message, {
20
+ content: piece.text,
21
+ usage_metadata: piece.isFirst ? message.usage_metadata : void 0,
22
+ additional_kwargs: piece.isFirst ? message.additional_kwargs : {},
23
+ response_metadata: piece.isFirst ? message.response_metadata : {}
24
+ }))
25
+ });
26
+ }
27
+ const SPLITTABLE_TEXT_PART_KEYS = new Set([
28
+ "type",
29
+ "text",
30
+ "index"
31
+ ]);
32
+ /**
33
+ * Returns the sole plain text part of a single-element complex content array
34
+ * (the shape google-common emits for text deltas), or null when the part
35
+ * carries anything beyond `type`/`text`/`index` (thought signatures, media,
36
+ * function calls) and must not be sliced. The part must carry a numeric
37
+ * `index`: it is the aggregator's list-merge key, and without it each split
38
+ * piece would append as a separate content part instead of merging back
39
+ * into one — index-less parts pace whole instead.
40
+ */
41
+ function getSplittableTextPart(message, chunkText) {
42
+ const content = message.content;
43
+ if (!Array.isArray(content) || content.length !== 1) return null;
44
+ const part = content[0];
45
+ if (part == null || typeof part !== "object") return null;
46
+ const record = part;
47
+ if (typeof record.text !== "string" || record.text !== chunkText) return null;
48
+ if (record.type != null && record.type !== "text") return null;
49
+ if (typeof record.index !== "number") return null;
50
+ if (!Object.keys(record).every((key) => SPLITTABLE_TEXT_PART_KEYS.has(key))) return null;
51
+ return record;
52
+ }
53
+ function clonePartGenerationChunkPiece(chunk, part, piece) {
54
+ if (piece.isFirst && piece.isLast) return chunk;
55
+ const message = chunk.message;
56
+ return new ChatGenerationChunk({
57
+ text: piece.text,
58
+ generationInfo: piece.isFirst ? chunk.generationInfo : void 0,
59
+ message: new AIMessageChunk(Object.assign({}, message, {
60
+ content: [Object.assign({}, part, { text: piece.text })],
61
+ usage_metadata: piece.isFirst ? message.usage_metadata : void 0,
62
+ additional_kwargs: piece.isFirst ? message.additional_kwargs : {},
63
+ response_metadata: piece.isFirst ? message.response_metadata : {}
64
+ }))
65
+ });
66
+ }
67
+ /**
68
+ * Provider-agnostic classification of a `ChatGenerationChunk` for the
69
+ * smoothing engine:
70
+ * - splittable: plain visible text — string content equal to `chunk.text`, or
71
+ * a single plain text part (google-common's delta shape) — with no
72
+ * logprobs / finish_reason; sliced adaptively at the pacing cadence.
73
+ * - atomic: any other text-bearing chunk (complex content arrays, logprobs,
74
+ * finish_reason, provider-specific reasoning payloads surfaced via
75
+ * `getAtomicText`) — paced as one piece, never split.
76
+ * - passthrough: tool-call deltas, usage-only and metadata chunks — strict
77
+ * FIFO, zero delay.
78
+ */
79
+ /**
80
+ * google-common stamps `logprobs: { content: [] }` on every chunk, so only
81
+ * logprobs that actually carry data may block splitting here. This is looser
82
+ * than the OpenAI-family adapter in `llm/openai/index.ts`, which treats ANY
83
+ * logprobs as atomic (its providers only attach logprobs when requested, and
84
+ * the DeepSeek suite pins that contract) — do not unify the two predicates.
85
+ */
86
+ function hasMeaningfulLogprobs(generationInfo) {
87
+ const logprobs = generationInfo?.logprobs;
88
+ if (logprobs == null) return false;
89
+ const content = logprobs.content;
90
+ if (Array.isArray(content)) return content.length > 0;
91
+ return true;
92
+ }
93
+ /** google-common reports the terminal reason camelCase (`finishReason`). */
94
+ function hasFinishReason(generationInfo) {
95
+ return generationInfo?.finish_reason != null || generationInfo?.finishReason != null;
96
+ }
97
+ /**
98
+ * Chunks that pair visible text with reasoning payloads in
99
+ * `additional_kwargs` (Gemini thought summaries, reasoning_content deltas,
100
+ * OpenRouter reasoning_details) must pace whole: split pieces would each
101
+ * carry the same kwargs and downstream merging — the aggregator's dict merge
102
+ * or OpenRouter's reasoning_details accumulation — duplicates them once per
103
+ * piece.
104
+ */
105
+ /**
106
+ * Mixed text/tool-call deltas must never split: cloned pieces would each
107
+ * carry the same tool_call_chunks and downstream accumulation would corrupt
108
+ * the assembled tool arguments.
109
+ */
110
+ function hasToolCallChunks(message) {
111
+ return (message.tool_call_chunks?.length ?? 0) > 0;
112
+ }
113
+ function hasReasoningKwargs(message) {
114
+ const kwargs = message.additional_kwargs;
115
+ if (typeof kwargs.reasoning_content === "string" && kwargs.reasoning_content !== "") return true;
116
+ if (Array.isArray(kwargs.reasoning_details) && kwargs.reasoning_details.length > 0) return true;
117
+ return kwargs.reasoning != null;
118
+ }
119
+ /**
120
+ * Extracts the visible reasoning text a kwargs-borne delta contributes, so
121
+ * reasoning-only chunks (Gemini thoughts, DeepSeek reasoning_content,
122
+ * OpenAI reasoning summaries, OpenRouter reasoning_details) pace atomically
123
+ * at the cadence instead of passing through unsmoothed.
124
+ */
125
+ function getReasoningKwargsText(message) {
126
+ const kwargs = message.additional_kwargs;
127
+ if (typeof kwargs.reasoning_content === "string" && kwargs.reasoning_content !== "") return kwargs.reasoning_content;
128
+ const reasoning = kwargs.reasoning;
129
+ if (typeof reasoning === "string" && reasoning !== "") return reasoning;
130
+ if (reasoning != null && typeof reasoning === "object") {
131
+ const summaryText = reasoning.summary?.[0]?.text;
132
+ if (typeof summaryText === "string" && summaryText !== "") return summaryText;
133
+ }
134
+ const details = kwargs.reasoning_details;
135
+ if (Array.isArray(details)) {
136
+ let text = "";
137
+ for (const detail of details) if (detail != null && typeof detail === "object") {
138
+ const detailText = detail.text;
139
+ if (typeof detailText === "string") text += detailText;
140
+ }
141
+ if (text !== "") return text;
142
+ }
143
+ return "";
144
+ }
145
+ function toGenerationSmoothItem(chunk, getAtomicText) {
146
+ const { message } = chunk;
147
+ const isMessageChunk = message instanceof AIMessageChunk;
148
+ const cleanGenerationInfo = !hasMeaningfulLogprobs(chunk.generationInfo) && !hasFinishReason(chunk.generationInfo);
149
+ if (Boolean(chunk.text) && isMessageChunk && typeof message.content === "string" && message.content === chunk.text && cleanGenerationInfo && !hasReasoningKwargs(message) && !hasToolCallChunks(message)) return {
150
+ text: chunk.text,
151
+ smooth: true,
152
+ emit: (piece) => cloneGenerationChunkPiece(chunk, piece)
153
+ };
154
+ const splittablePart = Boolean(chunk.text) && isMessageChunk && cleanGenerationInfo && !hasReasoningKwargs(message) && !hasToolCallChunks(message) ? getSplittableTextPart(message, chunk.text) : null;
155
+ if (splittablePart != null) return {
156
+ text: chunk.text,
157
+ smooth: true,
158
+ emit: (piece) => clonePartGenerationChunkPiece(chunk, splittablePart, piece)
159
+ };
160
+ const pacedText = chunk.text || (isMessageChunk && getAtomicText != null ? getAtomicText(message) : "");
161
+ if (pacedText !== "") return {
162
+ text: pacedText,
163
+ smooth: true,
164
+ atomic: true,
165
+ emit: () => chunk
166
+ };
167
+ return {
168
+ text: "",
169
+ smooth: false,
170
+ emit: () => chunk
171
+ };
172
+ }
173
+ /**
174
+ * Wraps a provider's raw chunk stream with adaptive smoothing and per-piece
175
+ * `handleLLMNewToken` dispatch. The raw stream must NOT dispatch runManager
176
+ * callbacks itself — callback-echo consumers would otherwise observe the
177
+ * unsmoothed deltas.
178
+ */
179
+ async function* smoothGenerationChunks({ chunks, delayMs, signal, runManager }) {
180
+ const source = (async function* () {
181
+ for await (const chunk of chunks) yield toGenerationSmoothItem(chunk, getReasoningKwargsText);
182
+ })();
183
+ for await (const outputChunk of smoothStream({
184
+ source,
185
+ delayMs,
186
+ signal
187
+ })) {
188
+ yield outputChunk;
189
+ await runManager?.handleLLMNewToken(outputChunk.text || "", void 0, void 0, void 0, void 0, { chunk: outputChunk });
190
+ }
191
+ }
192
+ //#endregion
193
+ export { getReasoningKwargsText, hasReasoningKwargs, hasToolCallChunks, smoothGenerationChunks };
194
+
195
+ //# sourceMappingURL=chunkAdapters.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chunkAdapters.mjs","names":[],"sources":["../../../../src/llm/stream/chunkAdapters.ts"],"sourcesContent":["import { AIMessageChunk } from '@langchain/core/messages';\nimport { ChatGenerationChunk } from '@langchain/core/outputs';\nimport type { CallbackManagerForLLMRun } from '@langchain/core/callbacks/manager';\nimport type { SmoothItem, SmoothPiece } from '@/llm/stream/smoother';\nimport { smoothStream } from '@/llm/stream/smoother';\n\n/**\n * Rebuilds a generation chunk carrying one piece of a split plain-text chunk.\n * Unsplit pieces return the original chunk untouched, so disabled smoothing is\n * byte-identical to no smoothing. Usage metadata, additional kwargs and\n * response metadata survive only on the first piece: the aggregator merges\n * dicts by concatenating string fields, so replicating them across pieces\n * would duplicate reasoning text and scalar metadata once per piece.\n */\nexport function cloneGenerationChunkPiece(\n chunk: ChatGenerationChunk,\n piece: SmoothPiece\n): ChatGenerationChunk {\n if (piece.isFirst && piece.isLast) {\n return chunk;\n }\n const message = chunk.message as AIMessageChunk;\n return new ChatGenerationChunk({\n text: piece.text,\n generationInfo: piece.isFirst ? chunk.generationInfo : undefined,\n message: new AIMessageChunk(\n Object.assign({}, message, {\n content: piece.text,\n usage_metadata: piece.isFirst ? message.usage_metadata : undefined,\n additional_kwargs: piece.isFirst ? message.additional_kwargs : {},\n response_metadata: piece.isFirst ? message.response_metadata : {},\n })\n ),\n });\n}\n\nconst SPLITTABLE_TEXT_PART_KEYS: ReadonlySet<string> = new Set([\n 'type',\n 'text',\n 'index',\n]);\n\n/**\n * Returns the sole plain text part of a single-element complex content array\n * (the shape google-common emits for text deltas), or null when the part\n * carries anything beyond `type`/`text`/`index` (thought signatures, media,\n * function calls) and must not be sliced. The part must carry a numeric\n * `index`: it is the aggregator's list-merge key, and without it each split\n * piece would append as a separate content part instead of merging back\n * into one — index-less parts pace whole instead.\n */\nfunction getSplittableTextPart(\n message: AIMessageChunk,\n chunkText: string\n): Record<string, unknown> | null {\n const content = message.content;\n if (!Array.isArray(content) || content.length !== 1) {\n return null;\n }\n const part: unknown = content[0];\n if (part == null || typeof part !== 'object') {\n return null;\n }\n const record = part as Record<string, unknown>;\n if (typeof record.text !== 'string' || record.text !== chunkText) {\n return null;\n }\n if (record.type != null && record.type !== 'text') {\n return null;\n }\n if (typeof record.index !== 'number') {\n return null;\n }\n if (!Object.keys(record).every((key) => SPLITTABLE_TEXT_PART_KEYS.has(key))) {\n return null;\n }\n return record;\n}\n\nfunction clonePartGenerationChunkPiece(\n chunk: ChatGenerationChunk,\n part: Record<string, unknown>,\n piece: SmoothPiece\n): ChatGenerationChunk {\n if (piece.isFirst && piece.isLast) {\n return chunk;\n }\n const message = chunk.message as AIMessageChunk;\n return new ChatGenerationChunk({\n text: piece.text,\n generationInfo: piece.isFirst ? chunk.generationInfo : undefined,\n message: new AIMessageChunk(\n Object.assign({}, message, {\n content: [Object.assign({}, part, { text: piece.text })],\n usage_metadata: piece.isFirst ? message.usage_metadata : undefined,\n additional_kwargs: piece.isFirst ? message.additional_kwargs : {},\n response_metadata: piece.isFirst ? message.response_metadata : {},\n })\n ),\n });\n}\n\n/**\n * Provider-agnostic classification of a `ChatGenerationChunk` for the\n * smoothing engine:\n * - splittable: plain visible text — string content equal to `chunk.text`, or\n * a single plain text part (google-common's delta shape) — with no\n * logprobs / finish_reason; sliced adaptively at the pacing cadence.\n * - atomic: any other text-bearing chunk (complex content arrays, logprobs,\n * finish_reason, provider-specific reasoning payloads surfaced via\n * `getAtomicText`) — paced as one piece, never split.\n * - passthrough: tool-call deltas, usage-only and metadata chunks — strict\n * FIFO, zero delay.\n */\n/**\n * google-common stamps `logprobs: { content: [] }` on every chunk, so only\n * logprobs that actually carry data may block splitting here. This is looser\n * than the OpenAI-family adapter in `llm/openai/index.ts`, which treats ANY\n * logprobs as atomic (its providers only attach logprobs when requested, and\n * the DeepSeek suite pins that contract) — do not unify the two predicates.\n */\nfunction hasMeaningfulLogprobs(\n generationInfo: ChatGenerationChunk['generationInfo']\n): boolean {\n const logprobs = generationInfo?.logprobs;\n if (logprobs == null) {\n return false;\n }\n const content = (logprobs as { content?: unknown }).content;\n if (Array.isArray(content)) {\n return content.length > 0;\n }\n return true;\n}\n\n/** google-common reports the terminal reason camelCase (`finishReason`). */\nfunction hasFinishReason(\n generationInfo: ChatGenerationChunk['generationInfo']\n): boolean {\n return (\n generationInfo?.finish_reason != null || generationInfo?.finishReason != null\n );\n}\n\n/**\n * Chunks that pair visible text with reasoning payloads in\n * `additional_kwargs` (Gemini thought summaries, reasoning_content deltas,\n * OpenRouter reasoning_details) must pace whole: split pieces would each\n * carry the same kwargs and downstream merging — the aggregator's dict merge\n * or OpenRouter's reasoning_details accumulation — duplicates them once per\n * piece.\n */\n/**\n * Mixed text/tool-call deltas must never split: cloned pieces would each\n * carry the same tool_call_chunks and downstream accumulation would corrupt\n * the assembled tool arguments.\n */\nexport function hasToolCallChunks(message: AIMessageChunk): boolean {\n return (message.tool_call_chunks?.length ?? 0) > 0;\n}\n\nexport function hasReasoningKwargs(message: AIMessageChunk): boolean {\n const kwargs = message.additional_kwargs;\n if (\n typeof kwargs.reasoning_content === 'string' &&\n kwargs.reasoning_content !== ''\n ) {\n return true;\n }\n if (\n Array.isArray(kwargs.reasoning_details) &&\n kwargs.reasoning_details.length > 0\n ) {\n return true;\n }\n return kwargs.reasoning != null;\n}\n\n/**\n * Extracts the visible reasoning text a kwargs-borne delta contributes, so\n * reasoning-only chunks (Gemini thoughts, DeepSeek reasoning_content,\n * OpenAI reasoning summaries, OpenRouter reasoning_details) pace atomically\n * at the cadence instead of passing through unsmoothed.\n */\nexport function getReasoningKwargsText(message: AIMessageChunk): string {\n const kwargs = message.additional_kwargs;\n if (\n typeof kwargs.reasoning_content === 'string' &&\n kwargs.reasoning_content !== ''\n ) {\n return kwargs.reasoning_content;\n }\n const reasoning = kwargs.reasoning;\n if (typeof reasoning === 'string' && reasoning !== '') {\n return reasoning;\n }\n if (reasoning != null && typeof reasoning === 'object') {\n const summaryText = (\n reasoning as { summary?: { text?: unknown }[] }\n ).summary?.[0]?.text;\n if (typeof summaryText === 'string' && summaryText !== '') {\n return summaryText;\n }\n }\n const details = kwargs.reasoning_details;\n if (Array.isArray(details)) {\n let text = '';\n for (const detail of details) {\n if (detail != null && typeof detail === 'object') {\n const detailText = (detail as { text?: unknown }).text;\n if (typeof detailText === 'string') {\n text += detailText;\n }\n }\n }\n if (text !== '') {\n return text;\n }\n }\n return '';\n}\n\nexport function toGenerationSmoothItem(\n chunk: ChatGenerationChunk,\n getAtomicText?: (message: AIMessageChunk) => string\n): SmoothItem<ChatGenerationChunk> {\n const { message } = chunk;\n const isMessageChunk = message instanceof AIMessageChunk;\n const cleanGenerationInfo =\n !hasMeaningfulLogprobs(chunk.generationInfo) &&\n !hasFinishReason(chunk.generationInfo);\n const splittable =\n Boolean(chunk.text) &&\n isMessageChunk &&\n typeof message.content === 'string' &&\n message.content === chunk.text &&\n cleanGenerationInfo &&\n !hasReasoningKwargs(message) &&\n !hasToolCallChunks(message);\n\n if (splittable) {\n return {\n text: chunk.text,\n smooth: true,\n emit: (piece) => cloneGenerationChunkPiece(chunk, piece),\n };\n }\n\n const splittablePart =\n Boolean(chunk.text) &&\n isMessageChunk &&\n cleanGenerationInfo &&\n !hasReasoningKwargs(message) &&\n !hasToolCallChunks(message)\n ? getSplittableTextPart(message, chunk.text)\n : null;\n if (splittablePart != null) {\n return {\n text: chunk.text,\n smooth: true,\n emit: (piece) =>\n clonePartGenerationChunkPiece(chunk, splittablePart, piece),\n };\n }\n\n const pacedText =\n chunk.text ||\n (isMessageChunk && getAtomicText != null ? getAtomicText(message) : '');\n if (pacedText !== '') {\n return {\n text: pacedText,\n smooth: true,\n atomic: true,\n emit: () => chunk,\n };\n }\n\n return { text: '', smooth: false, emit: () => chunk };\n}\n\n/**\n * Wraps a provider's raw chunk stream with adaptive smoothing and per-piece\n * `handleLLMNewToken` dispatch. The raw stream must NOT dispatch runManager\n * callbacks itself — callback-echo consumers would otherwise observe the\n * unsmoothed deltas.\n */\nexport async function* smoothGenerationChunks({\n chunks,\n delayMs,\n signal,\n runManager,\n}: {\n chunks: AsyncGenerator<ChatGenerationChunk>;\n delayMs: number;\n signal?: AbortSignal;\n runManager?: CallbackManagerForLLMRun;\n}): AsyncGenerator<ChatGenerationChunk> {\n const source = (async function* (): AsyncGenerator<\n SmoothItem<ChatGenerationChunk>\n > {\n for await (const chunk of chunks) {\n yield toGenerationSmoothItem(chunk, getReasoningKwargsText);\n }\n })();\n\n for await (const outputChunk of smoothStream({ source, delayMs, signal })) {\n yield outputChunk;\n await runManager?.handleLLMNewToken(\n outputChunk.text || '',\n undefined,\n undefined,\n undefined,\n undefined,\n { chunk: outputChunk }\n );\n }\n}\n"],"mappings":";;;;;;;;;;;;AAcA,SAAgB,0BACd,OACA,OACqB;CACrB,IAAI,MAAM,WAAW,MAAM,QACzB,OAAO;CAET,MAAM,UAAU,MAAM;CACtB,OAAO,IAAI,oBAAoB;EAC7B,MAAM,MAAM;EACZ,gBAAgB,MAAM,UAAU,MAAM,iBAAiB,KAAA;EACvD,SAAS,IAAI,eACX,OAAO,OAAO,CAAC,GAAG,SAAS;GACzB,SAAS,MAAM;GACf,gBAAgB,MAAM,UAAU,QAAQ,iBAAiB,KAAA;GACzD,mBAAmB,MAAM,UAAU,QAAQ,oBAAoB,CAAC;GAChE,mBAAmB,MAAM,UAAU,QAAQ,oBAAoB,CAAC;EAClE,CAAC,CACH;CACF,CAAC;AACH;AAEA,MAAM,4BAAiD,IAAI,IAAI;CAC7D;CACA;CACA;AACF,CAAC;;;;;;;;;;AAWD,SAAS,sBACP,SACA,WACgC;CAChC,MAAM,UAAU,QAAQ;CACxB,IAAI,CAAC,MAAM,QAAQ,OAAO,KAAK,QAAQ,WAAW,GAChD,OAAO;CAET,MAAM,OAAgB,QAAQ;CAC9B,IAAI,QAAQ,QAAQ,OAAO,SAAS,UAClC,OAAO;CAET,MAAM,SAAS;CACf,IAAI,OAAO,OAAO,SAAS,YAAY,OAAO,SAAS,WACrD,OAAO;CAET,IAAI,OAAO,QAAQ,QAAQ,OAAO,SAAS,QACzC,OAAO;CAET,IAAI,OAAO,OAAO,UAAU,UAC1B,OAAO;CAET,IAAI,CAAC,OAAO,KAAK,MAAM,CAAC,CAAC,OAAO,QAAQ,0BAA0B,IAAI,GAAG,CAAC,GACxE,OAAO;CAET,OAAO;AACT;AAEA,SAAS,8BACP,OACA,MACA,OACqB;CACrB,IAAI,MAAM,WAAW,MAAM,QACzB,OAAO;CAET,MAAM,UAAU,MAAM;CACtB,OAAO,IAAI,oBAAoB;EAC7B,MAAM,MAAM;EACZ,gBAAgB,MAAM,UAAU,MAAM,iBAAiB,KAAA;EACvD,SAAS,IAAI,eACX,OAAO,OAAO,CAAC,GAAG,SAAS;GACzB,SAAS,CAAC,OAAO,OAAO,CAAC,GAAG,MAAM,EAAE,MAAM,MAAM,KAAK,CAAC,CAAC;GACvD,gBAAgB,MAAM,UAAU,QAAQ,iBAAiB,KAAA;GACzD,mBAAmB,MAAM,UAAU,QAAQ,oBAAoB,CAAC;GAChE,mBAAmB,MAAM,UAAU,QAAQ,oBAAoB,CAAC;EAClE,CAAC,CACH;CACF,CAAC;AACH;;;;;;;;;;;;;;;;;;;;AAqBA,SAAS,sBACP,gBACS;CACT,MAAM,WAAW,gBAAgB;CACjC,IAAI,YAAY,MACd,OAAO;CAET,MAAM,UAAW,SAAmC;CACpD,IAAI,MAAM,QAAQ,OAAO,GACvB,OAAO,QAAQ,SAAS;CAE1B,OAAO;AACT;;AAGA,SAAS,gBACP,gBACS;CACT,OACE,gBAAgB,iBAAiB,QAAQ,gBAAgB,gBAAgB;AAE7E;;;;;;;;;;;;;;AAeA,SAAgB,kBAAkB,SAAkC;CAClE,QAAQ,QAAQ,kBAAkB,UAAU,KAAK;AACnD;AAEA,SAAgB,mBAAmB,SAAkC;CACnE,MAAM,SAAS,QAAQ;CACvB,IACE,OAAO,OAAO,sBAAsB,YACpC,OAAO,sBAAsB,IAE7B,OAAO;CAET,IACE,MAAM,QAAQ,OAAO,iBAAiB,KACtC,OAAO,kBAAkB,SAAS,GAElC,OAAO;CAET,OAAO,OAAO,aAAa;AAC7B;;;;;;;AAQA,SAAgB,uBAAuB,SAAiC;CACtE,MAAM,SAAS,QAAQ;CACvB,IACE,OAAO,OAAO,sBAAsB,YACpC,OAAO,sBAAsB,IAE7B,OAAO,OAAO;CAEhB,MAAM,YAAY,OAAO;CACzB,IAAI,OAAO,cAAc,YAAY,cAAc,IACjD,OAAO;CAET,IAAI,aAAa,QAAQ,OAAO,cAAc,UAAU;EACtD,MAAM,cACJ,UACA,UAAU,EAAE,EAAE;EAChB,IAAI,OAAO,gBAAgB,YAAY,gBAAgB,IACrD,OAAO;CAEX;CACA,MAAM,UAAU,OAAO;CACvB,IAAI,MAAM,QAAQ,OAAO,GAAG;EAC1B,IAAI,OAAO;EACX,KAAK,MAAM,UAAU,SACnB,IAAI,UAAU,QAAQ,OAAO,WAAW,UAAU;GAChD,MAAM,aAAc,OAA8B;GAClD,IAAI,OAAO,eAAe,UACxB,QAAQ;EAEZ;EAEF,IAAI,SAAS,IACX,OAAO;CAEX;CACA,OAAO;AACT;AAEA,SAAgB,uBACd,OACA,eACiC;CACjC,MAAM,EAAE,YAAY;CACpB,MAAM,iBAAiB,mBAAmB;CAC1C,MAAM,sBACJ,CAAC,sBAAsB,MAAM,cAAc,KAC3C,CAAC,gBAAgB,MAAM,cAAc;CAUvC,IARE,QAAQ,MAAM,IAAI,KAClB,kBACA,OAAO,QAAQ,YAAY,YAC3B,QAAQ,YAAY,MAAM,QAC1B,uBACA,CAAC,mBAAmB,OAAO,KAC3B,CAAC,kBAAkB,OAAO,GAG1B,OAAO;EACL,MAAM,MAAM;EACZ,QAAQ;EACR,OAAO,UAAU,0BAA0B,OAAO,KAAK;CACzD;CAGF,MAAM,iBACJ,QAAQ,MAAM,IAAI,KAClB,kBACA,uBACA,CAAC,mBAAmB,OAAO,KAC3B,CAAC,kBAAkB,OAAO,IACtB,sBAAsB,SAAS,MAAM,IAAI,IACzC;CACN,IAAI,kBAAkB,MACpB,OAAO;EACL,MAAM,MAAM;EACZ,QAAQ;EACR,OAAO,UACL,8BAA8B,OAAO,gBAAgB,KAAK;CAC9D;CAGF,MAAM,YACJ,MAAM,SACL,kBAAkB,iBAAiB,OAAO,cAAc,OAAO,IAAI;CACtE,IAAI,cAAc,IAChB,OAAO;EACL,MAAM;EACN,QAAQ;EACR,QAAQ;EACR,YAAY;CACd;CAGF,OAAO;EAAE,MAAM;EAAI,QAAQ;EAAO,YAAY;CAAM;AACtD;;;;;;;AAQA,gBAAuB,uBAAuB,EAC5C,QACA,SACA,QACA,cAMsC;CACtC,MAAM,UAAU,mBAEZ;EACF,WAAW,MAAM,SAAS,QACxB,MAAM,uBAAuB,OAAO,sBAAsB;CAE9D,EAAA,CAAG;CAEH,WAAW,MAAM,eAAe,aAAa;EAAE;EAAQ;EAAS;CAAO,CAAC,GAAG;EACzE,MAAM;EACN,MAAM,YAAY,kBAChB,YAAY,QAAQ,IACpB,KAAA,GACA,KAAA,GACA,KAAA,GACA,KAAA,GACA,EAAE,OAAO,YAAY,CACvB;CACF;AACF"}