@langchain/openrouter 0.2.5 → 0.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,44 @@
1
1
  # @langchain/openrouter
2
2
 
3
+ ## 0.4.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#10924](https://github.com/langchain-ai/langchainjs/pull/10924) [`2e28115`](https://github.com/langchain-ai/langchainjs/commit/2e2811509d75af94f57cedcc3842f178f4c020d1) Thanks [@christian-bromann](https://github.com/christian-bromann)! - feat(openrouter): add native streamEvents event conversion
8
+
9
+ ### Patch Changes
10
+
11
+ - Updated dependencies [[`2e28115`](https://github.com/langchain-ai/langchainjs/commit/2e2811509d75af94f57cedcc3842f178f4c020d1)]:
12
+ - @langchain/openai@1.5.0
13
+
14
+ ## 0.3.0
15
+
16
+ ### Minor Changes
17
+
18
+ - [#10666](https://github.com/langchain-ai/langchainjs/pull/10666) [`2bb55b0`](https://github.com/langchain-ai/langchainjs/commit/2bb55b053f49f89e81e3252a7af08e86d71ddd95) Thanks [@hnustwjj](https://github.com/hnustwjj)! - feat(openrouter): surface reasoning content as v1 standard content blocks
19
+
20
+ `convertOpenRouterResponseToBaseMessage` and
21
+ `convertOpenRouterDeltaToBaseMessageChunk` now copy OpenRouter's
22
+ `reasoning` (flat string) and `reasoning_details` (structured array) fields
23
+ onto `additional_kwargs.reasoning_content` / `additional_kwargs.reasoning_details`.
24
+ A new `ChatOpenRouterTranslator` is registered in `@langchain/core` under
25
+ the `"openrouter"` provider key so `AIMessage.contentBlocks` emits standard
26
+ `{type: "reasoning"}` blocks alongside text and tool calls.
27
+
28
+ Previously, reasoning text returned by reasoning-capable models routed
29
+ through OpenRouter (DeepSeek R1, Minimax M2, Claude extended thinking,
30
+ o-series, etc.) was silently dropped: only the `reasoning_tokens` count
31
+ was preserved via `usage_metadata`. Consumers using standard content blocks
32
+ (including the frontend agent UI patterns shown in the docs) could not
33
+ display the model's chain of thought.
34
+
35
+ ### Patch Changes
36
+
37
+ - [#10832](https://github.com/langchain-ai/langchainjs/pull/10832) [`1b24369`](https://github.com/langchain-ai/langchainjs/commit/1b24369a970ad6f56f1f428027f48601f87e62eb) Thanks [@info-arnav](https://github.com/info-arnav)! - fix(core, openrouter): make CJS default re-exports callable
38
+
39
+ - Updated dependencies [[`3999fab`](https://github.com/langchain-ai/langchainjs/commit/3999fab55870c6eea22d6d90c08aa472f9b2fac3), [`fce9ab4`](https://github.com/langchain-ai/langchainjs/commit/fce9ab418901323618fdfaaa9fc350fa1c0d50e0)]:
40
+ - @langchain/openai@1.4.7
41
+
3
42
  ## 0.2.5
4
43
 
5
44
  ### Patch Changes
@@ -1,3 +1,4 @@
1
+ const require_stream_events = require("../utils/stream_events.cjs");
1
2
  const require_messages = require("../converters/messages.cjs");
2
3
  const require_tools = require("../converters/tools.cjs");
3
4
  const require_errors = require("../utils/errors.cjs");
@@ -151,7 +152,7 @@ var ChatOpenRouter = class extends _langchain_core_language_models_chat_models.B
151
152
  model: modelOrFields
152
153
  } : modelOrFields;
153
154
  super(fields);
154
- this._addVersion("@langchain/openrouter", "0.2.5");
155
+ this._addVersion("@langchain/openrouter", "0.4.0");
155
156
  const apiKey = fields.apiKey ?? (0, _langchain_core_utils_env.getEnvironmentVariable)("OPENROUTER_API_KEY");
156
157
  if (!apiKey) throw new require_errors.OpenRouterAuthError("OpenRouter API key is required. Get one at https://openrouter.ai/keys and set it via the `apiKey` parameter or the OPENROUTER_API_KEY environment variable.");
157
158
  this.apiKey = apiKey;
@@ -291,6 +292,35 @@ var ChatOpenRouter = class extends _langchain_core_language_models_chat_models.B
291
292
  llmOutput: { tokenUsage: data.usage }
292
293
  };
293
294
  }
295
+ async *_streamChatModelEvents(messages, options, _runManager) {
296
+ const body = {
297
+ ...this.invocationParams(options),
298
+ messages: require_messages.convertMessagesToOpenRouterParams(messages, this.model),
299
+ stream: true
300
+ };
301
+ const response = await fetch(this.buildUrl(), {
302
+ method: "POST",
303
+ headers: this.buildHeaders(),
304
+ body: JSON.stringify(body),
305
+ signal: options.signal
306
+ });
307
+ if (!response.ok) throw await require_errors.OpenRouterError.fromResponse(response);
308
+ if (!response.body) return;
309
+ const reader = response.body.pipeThrough(new TextDecoderStream()).pipeThrough(new eventsource_parser_stream.EventSourceParserStream()).pipeThrough(new require_stream.OpenRouterJsonParseStream()).getReader();
310
+ async function* readChunks() {
311
+ try {
312
+ while (true) {
313
+ const { done, value } = await reader.read();
314
+ if (done) break;
315
+ if (value) yield value;
316
+ }
317
+ } finally {
318
+ reader.releaseLock();
319
+ }
320
+ }
321
+ const shouldStreamUsage = this.streamUsage ?? options.streamUsage ?? true;
322
+ yield* require_stream_events.convertOpenRouterStream(readChunks(), { streamUsage: shouldStreamUsage });
323
+ }
294
324
  /**
295
325
  * Streaming generation. Opens an SSE connection and yields one
296
326
  * `ChatGenerationChunk` per delta received from the API. The stream
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","names":["BaseChatModel","OpenRouterAuthError","PROFILES","convertToolsToOpenRouter","formatToolChoice","convertMessagesToOpenRouterParams","OpenRouterError","convertOpenRouterResponseToBaseMessage","AIMessage","convertUsageMetadata","EventSourceParserStream","OpenRouterJsonParseStream","convertOpenRouterDeltaToBaseMessageChunk","AIMessageChunk","ChatGenerationChunk","resolveOpenRouterStructuredOutputMethod"],"sources":["../../src/chat_models/index.ts"],"sourcesContent":["import {\n BaseChatModel,\n type BindToolsInput,\n type LangSmithParams,\n} from \"@langchain/core/language_models/chat_models\";\nimport type { ModelProfile } from \"@langchain/core/language_models/profile\";\nimport type {\n BaseLanguageModelInput,\n StructuredOutputMethodOptions,\n} from \"@langchain/core/language_models/base\";\nimport {\n AIMessage,\n AIMessageChunk,\n BaseMessage,\n} from \"@langchain/core/messages\";\nimport { ChatGenerationChunk, type ChatResult } from \"@langchain/core/outputs\";\nimport type { CallbackManagerForLLMRun } from \"@langchain/core/callbacks/manager\";\nimport { Runnable } from \"@langchain/core/runnables\";\nimport { toJsonSchema } from \"@langchain/core/utils/json_schema\";\nimport {\n type InteropZodType,\n getSchemaDescription,\n isInteropZodSchema,\n} from \"@langchain/core/utils/types\";\nimport { getEnvironmentVariable } from \"@langchain/core/utils/env\";\nimport { EventSourceParserStream } from \"eventsource-parser/stream\";\n\nimport type {\n ChatOpenRouterParams,\n ChatOpenRouterCallOptions,\n} from \"./types.js\";\nimport type { OpenAI as OpenAIClient } from \"openai\";\nimport type { OpenRouter } from \"../api-types.js\";\n\n/**\n * Full request body sent to the OpenRouter `/chat/completions` endpoint.\n *\n * Extends the base generation params with OpenRouter-specific sampling\n * knobs (`top_k`, `min_p`, etc.) and features (`prediction`, `transforms`)\n * that aren't part of the standard OpenAI spec.\n */\ntype OpenRouterRequestBody = Omit<\n OpenRouter.ChatGenerationParams,\n \"messages\"\n> & {\n messages: OpenAIClient.Chat.Completions.ChatCompletionMessageParam[];\n top_k?: number | null;\n repetition_penalty?: number | null;\n min_p?: number | null;\n top_a?: number | null;\n prediction?: { type: \"content\"; content: string };\n transforms?: string[];\n};\nimport {\n convertMessagesToOpenRouterParams,\n convertOpenRouterResponseToBaseMessage,\n convertOpenRouterDeltaToBaseMessageChunk,\n convertUsageMetadata,\n} from \"../converters/messages.js\";\nimport {\n convertToolsToOpenRouter,\n formatToolChoice,\n} from \"../converters/tools.js\";\nimport { OpenRouterError, OpenRouterAuthError } from \"../utils/errors.js\";\nimport { resolveOpenRouterStructuredOutputMethod } from \"../utils/structured_output.js\";\nimport { OpenRouterJsonParseStream } from \"../utils/stream.js\";\nimport PROFILES from \"../profiles.js\";\nimport {\n isSerializableSchema,\n SerializableSchema,\n} from \"@langchain/core/utils/standard_schema\";\nimport {\n assembleStructuredOutputPipeline,\n createContentParser,\n createFunctionCallingParser,\n} from \"@langchain/core/language_models/structured_output\";\n\nconst DEFAULT_BASE_URL = \"https://openrouter.ai/api/v1\";\n\n/**\n * OpenRouter chat model integration.\n *\n * Talks directly to the OpenRouter REST API via `fetch` (no SDK dependency)\n * and supports tool calling, structured output, and streaming. Any model\n * available on OpenRouter can be used by passing its identifier (e.g.\n * `\"anthropic/claude-4-sonnet\"`) as the `model` param.\n */\nexport class ChatOpenRouter extends BaseChatModel<\n ChatOpenRouterCallOptions,\n AIMessageChunk\n> {\n static lc_name() {\n return \"ChatOpenRouter\";\n }\n\n lc_serializable = true;\n\n /** Maps secret fields to the environment variable they can be loaded from. */\n get lc_secrets(): { [key: string]: string } | undefined {\n return {\n apiKey: \"OPENROUTER_API_KEY\",\n };\n }\n\n /** Allows serialized JSON to use `modelName` as an alias for `model`. */\n get lc_aliases(): Record<string, string> {\n return {\n modelName: \"model\",\n };\n }\n\n /** Fields that may be overridden per-call via `.bind()` / `.withConfig()`. */\n get callKeys(): string[] {\n return [\n ...super.callKeys,\n \"tools\",\n \"tool_choice\",\n \"response_format\",\n \"seed\",\n \"strict\",\n \"temperature\",\n \"maxTokens\",\n \"topP\",\n \"topK\",\n \"frequencyPenalty\",\n \"presencePenalty\",\n \"repetitionPenalty\",\n \"minP\",\n \"topA\",\n \"stop\",\n \"logitBias\",\n \"topLogprobs\",\n \"user\",\n \"transforms\",\n \"models\",\n \"route\",\n \"provider\",\n \"plugins\",\n \"sessionId\",\n \"trace\",\n \"prediction\",\n ];\n }\n\n /** Model identifier, e.g. `\"anthropic/claude-4-sonnet\"`. */\n model: string;\n\n /** OpenRouter API key. Falls back to the `OPENROUTER_API_KEY` env var. */\n apiKey: string;\n\n /** Base URL for the API. Defaults to `\"https://openrouter.ai/api/v1\"`. */\n baseURL: string;\n\n /** Sampling temperature (0–2). */\n temperature?: number;\n\n /** Maximum number of tokens to generate. */\n maxTokens?: number;\n\n /** Nucleus sampling cutoff probability. */\n topP?: number;\n\n /** Top-K sampling: only consider the K most likely tokens. */\n topK?: number;\n\n /** Additive penalty based on how often a token has appeared so far (−2 to 2). */\n frequencyPenalty?: number;\n\n /** Additive penalty based on whether a token has appeared at all (−2 to 2). */\n presencePenalty?: number;\n\n /** Multiplicative penalty applied to repeated token logits (0 to 2). */\n repetitionPenalty?: number;\n\n /** Minimum probability threshold for token sampling. */\n minP?: number;\n\n /** Top-A sampling threshold. */\n topA?: number;\n\n /** Random seed for deterministic generation. */\n seed?: number;\n\n /** Stop sequences that halt generation. */\n stop?: string[];\n\n /** Token-level biases to apply during sampling. */\n logitBias?: Record<string, number>;\n\n /** Number of most-likely log-probabilities to return per token. */\n topLogprobs?: number;\n\n /** Stable identifier for end-users, used for abuse detection. */\n user?: string;\n\n /** OpenRouter-specific transformations to apply to the request. */\n transforms?: string[];\n\n /** OpenRouter-specific list of models for routing. */\n models?: string[];\n\n /** OpenRouter-specific routing strategy. */\n route?: \"fallback\";\n\n /** OpenRouter-specific provider preferences and ordering. */\n provider?: OpenRouter.ProviderPreferences;\n\n /** OpenRouter plugins to enable (e.g. web search). */\n plugins?: ChatOpenRouterParams[\"plugins\"];\n\n /** Identifier used by OpenRouter to group related requests together. */\n sessionId?: string;\n\n /** Trace metadata for OpenRouter broadcast destinations. */\n trace?: OpenRouter.TraceConfig;\n\n /**\n * Application URL for OpenRouter attribution. Maps to `HTTP-Referer` header.\n *\n * Defaults to LangChain docs URL. Set this to your app's URL to get\n * attribution for API usage in the OpenRouter dashboard.\n *\n * See https://openrouter.ai/docs/app-attribution for details.\n */\n siteUrl: string;\n\n /**\n * Application title for OpenRouter attribution. Maps to `X-Title` header.\n *\n * Defaults to `'LangChain'`. Set this to your app's name to get attribution\n * for API usage in the OpenRouter dashboard.\n *\n * See https://openrouter.ai/docs/app-attribution for details.\n */\n siteName: string;\n\n /**\n * Marketplace categories for OpenRouter attribution.\n * Maps to `X-OpenRouter-Categories` header.\n *\n * See https://openrouter.ai/docs/app-attribution for recognized categories.\n */\n appCategories?: string[];\n\n /** Extra params passed through to the API body. */\n modelKwargs?: Record<string, unknown>;\n\n /** Whether to include token usage in streaming chunks. Defaults to `true`. */\n streamUsage: boolean;\n\n constructor(model: string, fields?: Omit<ChatOpenRouterParams, \"model\">);\n constructor(fields: ChatOpenRouterParams);\n constructor(\n modelOrFields: string | ChatOpenRouterParams,\n fieldsArg?: Omit<ChatOpenRouterParams, \"model\">\n ) {\n const fields =\n typeof modelOrFields === \"string\"\n ? { ...(fieldsArg ?? {}), model: modelOrFields }\n : modelOrFields;\n super(fields);\n this._addVersion(\"@langchain/openrouter\", __PKG_VERSION__);\n const apiKey =\n fields.apiKey ?? getEnvironmentVariable(\"OPENROUTER_API_KEY\");\n if (!apiKey) {\n throw new OpenRouterAuthError(\n \"OpenRouter API key is required. Get one at https://openrouter.ai/keys and set it via the `apiKey` parameter or the OPENROUTER_API_KEY environment variable.\"\n );\n }\n this.apiKey = apiKey;\n if (!fields.model) {\n throw new Error(\n 'ChatOpenRouter requires a `model` parameter, e.g. \"openai/gpt-4o-mini\".'\n );\n }\n this.model = fields.model;\n this.baseURL = fields.baseURL ?? DEFAULT_BASE_URL;\n this.temperature = fields.temperature;\n this.maxTokens = fields.maxTokens;\n this.topP = fields.topP;\n this.topK = fields.topK;\n this.frequencyPenalty = fields.frequencyPenalty;\n this.presencePenalty = fields.presencePenalty;\n this.repetitionPenalty = fields.repetitionPenalty;\n this.minP = fields.minP;\n this.topA = fields.topA;\n this.seed = fields.seed;\n this.stop = fields.stop;\n this.logitBias = fields.logitBias;\n this.topLogprobs = fields.topLogprobs;\n this.user = fields.user;\n this.transforms = fields.transforms;\n this.models = fields.models;\n this.route = fields.route;\n this.provider = fields.provider;\n this.plugins = fields.plugins;\n this.sessionId =\n fields.sessionId ?? getEnvironmentVariable(\"OPENROUTER_SESSION_ID\");\n this.trace = fields.trace;\n this.siteUrl = fields.siteUrl ?? \"https://docs.langchain.com\";\n this.siteName = fields.siteName ?? \"LangChain\";\n this.appCategories = fields.appCategories;\n this.modelKwargs = fields.modelKwargs;\n this.streamUsage = fields.streamUsage ?? true;\n }\n\n _llmType(): string {\n return \"openrouter\";\n }\n\n /** Static capability profile (context size, tool support, etc.) for the current model. */\n get profile(): ModelProfile {\n return PROFILES[this.model] ?? {};\n }\n\n /** Builds auth + content-type headers, plus optional attribution headers. */\n private buildHeaders(): Record<string, string> {\n const headers: Record<string, string> = {\n Authorization: `Bearer ${this.apiKey}`,\n \"Content-Type\": \"application/json\",\n };\n if (this.siteUrl) {\n headers[\"HTTP-Referer\"] = this.siteUrl;\n }\n if (this.siteName) {\n headers[\"X-Title\"] = this.siteName;\n }\n if (this.appCategories && this.appCategories.length > 0) {\n headers[\"X-OpenRouter-Categories\"] = this.appCategories.join(\",\");\n }\n return headers;\n }\n\n /** Returns the full chat-completions endpoint URL. */\n private buildUrl(): string {\n return `${this.baseURL}/chat/completions`;\n }\n\n /**\n * Merges constructor-level defaults with per-call overrides into the\n * API request body (everything except `messages`, which is added later).\n */\n override invocationParams(\n options: this[\"ParsedCallOptions\"]\n ): Omit<OpenRouterRequestBody, \"messages\"> {\n const tools = options.tools\n ? convertToolsToOpenRouter(options.tools, { strict: options.strict })\n : undefined;\n const toolChoice = formatToolChoice(options.tool_choice);\n const sessionId = options.sessionId ?? this.sessionId;\n const trace = options.trace ?? this.trace;\n\n return {\n model: this.model,\n temperature: options.temperature ?? this.temperature,\n max_tokens: options.maxTokens ?? this.maxTokens,\n top_p: options.topP ?? this.topP,\n top_k: options.topK ?? this.topK,\n frequency_penalty: options.frequencyPenalty ?? this.frequencyPenalty,\n presence_penalty: options.presencePenalty ?? this.presencePenalty,\n repetition_penalty: options.repetitionPenalty ?? this.repetitionPenalty,\n min_p: options.minP ?? this.minP,\n top_a: options.topA ?? this.topA,\n seed: options.seed ?? this.seed,\n stop: options.stop ?? this.stop,\n logit_bias: options.logitBias ?? this.logitBias,\n top_logprobs: options.topLogprobs ?? this.topLogprobs,\n user: options.user ?? this.user,\n tools,\n tool_choice: toolChoice,\n response_format: options.response_format,\n ...(options.prediction ? { prediction: options.prediction } : {}),\n transforms: options.transforms ?? this.transforms,\n models: options.models ?? this.models,\n route: options.route ?? this.route,\n provider: options.provider ?? this.provider,\n plugins: options.plugins ?? this.plugins,\n ...(sessionId ? { session_id: sessionId } : {}),\n ...(trace !== undefined ? { trace } : {}),\n ...this.modelKwargs,\n };\n }\n\n /** Returns metadata for LangSmith tracing (provider, model name, temperature, etc.). */\n override getLsParams(options: this[\"ParsedCallOptions\"]): LangSmithParams {\n const params = this.invocationParams(options);\n return {\n ls_provider: \"openrouter\",\n ls_model_name: this.model,\n ls_model_type: \"chat\",\n ls_temperature: params.temperature ?? undefined,\n ls_max_tokens: params.max_tokens ?? undefined,\n ls_stop: options.stop,\n };\n }\n\n /**\n * Non-streaming generation. Sends a single request and returns the\n * complete response with the generated message and token usage.\n */\n async _generate(\n messages: BaseMessage[],\n options: this[\"ParsedCallOptions\"],\n runManager?: CallbackManagerForLLMRun\n ): Promise<ChatResult> {\n const body: OpenRouterRequestBody = {\n ...this.invocationParams(options),\n messages: convertMessagesToOpenRouterParams(messages, this.model),\n stream: false,\n };\n\n const response = await fetch(this.buildUrl(), {\n method: \"POST\",\n headers: this.buildHeaders(),\n body: JSON.stringify(body),\n signal: options.signal,\n });\n\n if (!response.ok) {\n throw await OpenRouterError.fromResponse(response);\n }\n\n const data: OpenRouter.ChatResponse = await response.json();\n const choice = data.choices[0];\n\n if (!choice) {\n throw new OpenRouterError(\"No choices returned in response.\");\n }\n\n const message = convertOpenRouterResponseToBaseMessage(choice, data);\n if (AIMessage.isInstance(message)) {\n message.usage_metadata = convertUsageMetadata(data.usage);\n }\n\n const text = typeof message.content === \"string\" ? message.content : \"\";\n\n await runManager?.handleLLMNewToken(text);\n\n return {\n generations: [\n {\n text,\n message,\n generationInfo: {\n finish_reason: choice.finish_reason,\n },\n },\n ],\n llmOutput: { tokenUsage: data.usage },\n };\n }\n\n /**\n * Streaming generation. Opens an SSE connection and yields one\n * `ChatGenerationChunk` per delta received from the API. The stream\n * pipeline is: raw bytes -> text -> SSE events -> JSON-parsed deltas.\n */\n async *_streamResponseChunks(\n messages: BaseMessage[],\n options: this[\"ParsedCallOptions\"],\n runManager?: CallbackManagerForLLMRun\n ): AsyncGenerator<ChatGenerationChunk> {\n const body: OpenRouterRequestBody = {\n ...this.invocationParams(options),\n messages: convertMessagesToOpenRouterParams(messages, this.model),\n stream: true,\n };\n\n const response = await fetch(this.buildUrl(), {\n method: \"POST\",\n headers: this.buildHeaders(),\n body: JSON.stringify(body),\n signal: options.signal,\n });\n\n if (!response.ok) {\n throw await OpenRouterError.fromResponse(response);\n }\n\n if (!response.body) {\n return;\n }\n\n const stream = response.body\n .pipeThrough(new TextDecoderStream())\n .pipeThrough(new EventSourceParserStream())\n .pipeThrough(new OpenRouterJsonParseStream());\n\n const reader = stream.getReader();\n let defaultRole: string | undefined;\n\n try {\n while (true) {\n const { done, value: data } = await reader.read();\n if (done) break;\n if (!data) continue;\n\n const choice = data.choices?.[0];\n if (!choice?.delta) continue;\n\n const chunk = convertOpenRouterDeltaToBaseMessageChunk(\n choice.delta,\n data,\n defaultRole\n );\n defaultRole = choice.delta.role ?? defaultRole;\n\n if (\n data.usage &&\n this.streamUsage &&\n AIMessageChunk.isInstance(chunk)\n ) {\n chunk.usage_metadata = convertUsageMetadata(data.usage);\n }\n\n const text = typeof chunk.content === \"string\" ? chunk.content : \"\";\n\n const generationChunk = new ChatGenerationChunk({\n message: chunk,\n text,\n generationInfo: {\n ...(choice.finish_reason\n ? { finish_reason: choice.finish_reason }\n : {}),\n },\n });\n\n yield generationChunk;\n await runManager?.handleLLMNewToken(\n text,\n undefined,\n undefined,\n undefined,\n undefined,\n { chunk: generationChunk }\n );\n }\n } finally {\n reader.releaseLock();\n }\n }\n\n /**\n * Returns a new Runnable with the given tools bound into every call.\n * Equivalent to `.withConfig({ tools, ...kwargs })`.\n */\n override bindTools(\n tools: BindToolsInput[],\n kwargs?: Partial<ChatOpenRouterCallOptions>\n ): Runnable<\n BaseLanguageModelInput,\n AIMessageChunk,\n ChatOpenRouterCallOptions\n > {\n return this.withConfig({\n ...kwargs,\n tools,\n } as Partial<ChatOpenRouterCallOptions>);\n }\n\n /**\n * Returns a Runnable that forces the model to produce output conforming\n * to `outputSchema` (a Zod schema or plain JSON Schema object).\n *\n * The extraction strategy (JSON Schema response format, function calling,\n * or JSON mode) is chosen automatically based on model capabilities —\n * see {@link resolveOpenRouterStructuredOutputMethod}. You can override\n * this via `config.method`.\n *\n * When `config.includeRaw` is `true` the returned object contains both\n * the raw `BaseMessage` and the parsed output, with a fallback that\n * sets `parsed: null` if the parser throws.\n */\n withStructuredOutput<\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n RunOutput extends Record<string, any> = Record<string, any>,\n >(\n outputSchema:\n | InteropZodType<RunOutput>\n | SerializableSchema<RunOutput>\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n | Record<string, any>,\n config?: StructuredOutputMethodOptions<false>\n ): Runnable<BaseLanguageModelInput, RunOutput>;\n\n withStructuredOutput<\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n RunOutput extends Record<string, any> = Record<string, any>,\n >(\n outputSchema:\n | InteropZodType<RunOutput>\n | SerializableSchema<RunOutput>\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n | Record<string, any>,\n config?: StructuredOutputMethodOptions<true>\n ): Runnable<BaseLanguageModelInput, { raw: BaseMessage; parsed: RunOutput }>;\n\n withStructuredOutput<\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n RunOutput extends Record<string, any> = Record<string, any>,\n >(\n outputSchema:\n | InteropZodType<RunOutput>\n | SerializableSchema<RunOutput>\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n | Record<string, any>,\n config?: StructuredOutputMethodOptions<boolean>\n ):\n | Runnable<BaseLanguageModelInput, RunOutput>\n | Runnable<BaseLanguageModelInput, { raw: BaseMessage; parsed: RunOutput }>;\n\n withStructuredOutput<\n RunOutput extends Record<string, unknown> = Record<string, unknown>,\n >(\n outputSchema:\n | InteropZodType<RunOutput>\n | SerializableSchema<RunOutput>\n | Record<string, unknown>,\n config?: StructuredOutputMethodOptions<boolean>\n ) {\n let llm: Runnable<BaseLanguageModelInput>;\n let outputParser: Runnable<AIMessageChunk, RunOutput>;\n\n const { schema, name, includeRaw } = {\n ...config,\n schema: outputSchema,\n };\n\n const method = resolveOpenRouterStructuredOutputMethod({\n model: this.model,\n method: config?.method,\n profile: this.profile,\n models: this.models,\n route: this.route,\n });\n\n const asJsonSchema = toJsonSchema(schema);\n\n if (method === \"jsonSchema\") {\n const schemaName = name ?? \"extract\";\n llm = this.withConfig({\n response_format: {\n type: \"json_schema\",\n json_schema: {\n name: schemaName,\n description: getSchemaDescription(schema),\n schema: asJsonSchema,\n strict: config?.strict,\n },\n },\n ls_structured_output_format: {\n kwargs: { method: \"json_schema\" },\n schema: {\n title: schemaName,\n description: getSchemaDescription(schema),\n ...asJsonSchema,\n },\n },\n } as Partial<ChatOpenRouterCallOptions>);\n\n outputParser = createContentParser(schema);\n } else if (method === \"jsonMode\") {\n llm = this.withConfig({\n response_format: { type: \"json_object\" },\n ls_structured_output_format: {\n kwargs: { method: \"json_mode\" },\n schema: { title: name ?? \"extract\", ...asJsonSchema },\n },\n } as Partial<ChatOpenRouterCallOptions>);\n outputParser = createContentParser(schema);\n } else {\n let functionName = name ?? \"extract\";\n if (\n !isInteropZodSchema(schema) &&\n !isSerializableSchema(schema) &&\n \"name\" in (schema as Record<string, unknown>)\n ) {\n functionName = (schema as Record<string, unknown>).name as string;\n }\n\n llm = this.withConfig({\n tools: [\n {\n type: \"function\" as const,\n function: {\n name: functionName,\n description: getSchemaDescription(schema) ?? \"\",\n parameters: asJsonSchema,\n },\n },\n ],\n tool_choice: {\n type: \"function\" as const,\n function: { name: functionName },\n },\n ls_structured_output_format: {\n kwargs: { method: \"function_calling\" },\n schema: { title: functionName, ...asJsonSchema },\n },\n ...(config?.strict !== undefined ? { strict: config.strict } : {}),\n } as Partial<ChatOpenRouterCallOptions>);\n\n outputParser = createFunctionCallingParser(schema, functionName);\n }\n\n return assembleStructuredOutputPipeline(\n llm,\n outputParser,\n includeRaw,\n \"ChatOpenRouterStructuredOutput\"\n );\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs","names":["BaseChatModel","OpenRouterAuthError","PROFILES","convertToolsToOpenRouter","formatToolChoice","convertMessagesToOpenRouterParams","OpenRouterError","convertOpenRouterResponseToBaseMessage","AIMessage","convertUsageMetadata","EventSourceParserStream","OpenRouterJsonParseStream","convertOpenRouterStream","convertOpenRouterDeltaToBaseMessageChunk","AIMessageChunk","ChatGenerationChunk","resolveOpenRouterStructuredOutputMethod"],"sources":["../../src/chat_models/index.ts"],"sourcesContent":["import {\n BaseChatModel,\n type BindToolsInput,\n type LangSmithParams,\n} from \"@langchain/core/language_models/chat_models\";\nimport type { ModelProfile } from \"@langchain/core/language_models/profile\";\nimport type {\n BaseLanguageModelInput,\n StructuredOutputMethodOptions,\n} from \"@langchain/core/language_models/base\";\nimport {\n AIMessage,\n AIMessageChunk,\n BaseMessage,\n} from \"@langchain/core/messages\";\nimport { ChatGenerationChunk, type ChatResult } from \"@langchain/core/outputs\";\nimport type { CallbackManagerForLLMRun } from \"@langchain/core/callbacks/manager\";\nimport type { ChatModelStreamEvent } from \"@langchain/core/language_models/event\";\nimport { convertOpenRouterStream } from \"../utils/stream_events.js\";\nimport { Runnable } from \"@langchain/core/runnables\";\nimport { toJsonSchema } from \"@langchain/core/utils/json_schema\";\nimport {\n type InteropZodType,\n getSchemaDescription,\n isInteropZodSchema,\n} from \"@langchain/core/utils/types\";\nimport { getEnvironmentVariable } from \"@langchain/core/utils/env\";\nimport { EventSourceParserStream } from \"eventsource-parser/stream\";\n\nimport type {\n ChatOpenRouterParams,\n ChatOpenRouterCallOptions,\n} from \"./types.js\";\nimport type { OpenAI as OpenAIClient } from \"openai\";\nimport type { OpenRouter } from \"../api-types.js\";\n\n/**\n * Full request body sent to the OpenRouter `/chat/completions` endpoint.\n *\n * Extends the base generation params with OpenRouter-specific sampling\n * knobs (`top_k`, `min_p`, etc.) and features (`prediction`, `transforms`)\n * that aren't part of the standard OpenAI spec.\n */\ntype OpenRouterRequestBody = Omit<\n OpenRouter.ChatGenerationParams,\n \"messages\"\n> & {\n messages: OpenAIClient.Chat.Completions.ChatCompletionMessageParam[];\n top_k?: number | null;\n repetition_penalty?: number | null;\n min_p?: number | null;\n top_a?: number | null;\n prediction?: { type: \"content\"; content: string };\n transforms?: string[];\n};\nimport {\n convertMessagesToOpenRouterParams,\n convertOpenRouterResponseToBaseMessage,\n convertOpenRouterDeltaToBaseMessageChunk,\n convertUsageMetadata,\n} from \"../converters/messages.js\";\nimport {\n convertToolsToOpenRouter,\n formatToolChoice,\n} from \"../converters/tools.js\";\nimport { OpenRouterError, OpenRouterAuthError } from \"../utils/errors.js\";\nimport { resolveOpenRouterStructuredOutputMethod } from \"../utils/structured_output.js\";\nimport { OpenRouterJsonParseStream } from \"../utils/stream.js\";\nimport PROFILES from \"../profiles.js\";\nimport {\n isSerializableSchema,\n SerializableSchema,\n} from \"@langchain/core/utils/standard_schema\";\nimport {\n assembleStructuredOutputPipeline,\n createContentParser,\n createFunctionCallingParser,\n} from \"@langchain/core/language_models/structured_output\";\n\nconst DEFAULT_BASE_URL = \"https://openrouter.ai/api/v1\";\n\n/**\n * OpenRouter chat model integration.\n *\n * Talks directly to the OpenRouter REST API via `fetch` (no SDK dependency)\n * and supports tool calling, structured output, and streaming. Any model\n * available on OpenRouter can be used by passing its identifier (e.g.\n * `\"anthropic/claude-4-sonnet\"`) as the `model` param.\n */\nexport class ChatOpenRouter extends BaseChatModel<\n ChatOpenRouterCallOptions,\n AIMessageChunk\n> {\n static lc_name() {\n return \"ChatOpenRouter\";\n }\n\n lc_serializable = true;\n\n /** Maps secret fields to the environment variable they can be loaded from. */\n get lc_secrets(): { [key: string]: string } | undefined {\n return {\n apiKey: \"OPENROUTER_API_KEY\",\n };\n }\n\n /** Allows serialized JSON to use `modelName` as an alias for `model`. */\n get lc_aliases(): Record<string, string> {\n return {\n modelName: \"model\",\n };\n }\n\n /** Fields that may be overridden per-call via `.bind()` / `.withConfig()`. */\n get callKeys(): string[] {\n return [\n ...super.callKeys,\n \"tools\",\n \"tool_choice\",\n \"response_format\",\n \"seed\",\n \"strict\",\n \"temperature\",\n \"maxTokens\",\n \"topP\",\n \"topK\",\n \"frequencyPenalty\",\n \"presencePenalty\",\n \"repetitionPenalty\",\n \"minP\",\n \"topA\",\n \"stop\",\n \"logitBias\",\n \"topLogprobs\",\n \"user\",\n \"transforms\",\n \"models\",\n \"route\",\n \"provider\",\n \"plugins\",\n \"sessionId\",\n \"trace\",\n \"prediction\",\n ];\n }\n\n /** Model identifier, e.g. `\"anthropic/claude-4-sonnet\"`. */\n model: string;\n\n /** OpenRouter API key. Falls back to the `OPENROUTER_API_KEY` env var. */\n apiKey: string;\n\n /** Base URL for the API. Defaults to `\"https://openrouter.ai/api/v1\"`. */\n baseURL: string;\n\n /** Sampling temperature (0–2). */\n temperature?: number;\n\n /** Maximum number of tokens to generate. */\n maxTokens?: number;\n\n /** Nucleus sampling cutoff probability. */\n topP?: number;\n\n /** Top-K sampling: only consider the K most likely tokens. */\n topK?: number;\n\n /** Additive penalty based on how often a token has appeared so far (−2 to 2). */\n frequencyPenalty?: number;\n\n /** Additive penalty based on whether a token has appeared at all (−2 to 2). */\n presencePenalty?: number;\n\n /** Multiplicative penalty applied to repeated token logits (0 to 2). */\n repetitionPenalty?: number;\n\n /** Minimum probability threshold for token sampling. */\n minP?: number;\n\n /** Top-A sampling threshold. */\n topA?: number;\n\n /** Random seed for deterministic generation. */\n seed?: number;\n\n /** Stop sequences that halt generation. */\n stop?: string[];\n\n /** Token-level biases to apply during sampling. */\n logitBias?: Record<string, number>;\n\n /** Number of most-likely log-probabilities to return per token. */\n topLogprobs?: number;\n\n /** Stable identifier for end-users, used for abuse detection. */\n user?: string;\n\n /** OpenRouter-specific transformations to apply to the request. */\n transforms?: string[];\n\n /** OpenRouter-specific list of models for routing. */\n models?: string[];\n\n /** OpenRouter-specific routing strategy. */\n route?: \"fallback\";\n\n /** OpenRouter-specific provider preferences and ordering. */\n provider?: OpenRouter.ProviderPreferences;\n\n /** OpenRouter plugins to enable (e.g. web search). */\n plugins?: ChatOpenRouterParams[\"plugins\"];\n\n /** Identifier used by OpenRouter to group related requests together. */\n sessionId?: string;\n\n /** Trace metadata for OpenRouter broadcast destinations. */\n trace?: OpenRouter.TraceConfig;\n\n /**\n * Application URL for OpenRouter attribution. Maps to `HTTP-Referer` header.\n *\n * Defaults to LangChain docs URL. Set this to your app's URL to get\n * attribution for API usage in the OpenRouter dashboard.\n *\n * See https://openrouter.ai/docs/app-attribution for details.\n */\n siteUrl: string;\n\n /**\n * Application title for OpenRouter attribution. Maps to `X-Title` header.\n *\n * Defaults to `'LangChain'`. Set this to your app's name to get attribution\n * for API usage in the OpenRouter dashboard.\n *\n * See https://openrouter.ai/docs/app-attribution for details.\n */\n siteName: string;\n\n /**\n * Marketplace categories for OpenRouter attribution.\n * Maps to `X-OpenRouter-Categories` header.\n *\n * See https://openrouter.ai/docs/app-attribution for recognized categories.\n */\n appCategories?: string[];\n\n /** Extra params passed through to the API body. */\n modelKwargs?: Record<string, unknown>;\n\n /** Whether to include token usage in streaming chunks. Defaults to `true`. */\n streamUsage: boolean;\n\n constructor(model: string, fields?: Omit<ChatOpenRouterParams, \"model\">);\n constructor(fields: ChatOpenRouterParams);\n constructor(\n modelOrFields: string | ChatOpenRouterParams,\n fieldsArg?: Omit<ChatOpenRouterParams, \"model\">\n ) {\n const fields =\n typeof modelOrFields === \"string\"\n ? { ...(fieldsArg ?? {}), model: modelOrFields }\n : modelOrFields;\n super(fields);\n this._addVersion(\"@langchain/openrouter\", __PKG_VERSION__);\n const apiKey =\n fields.apiKey ?? getEnvironmentVariable(\"OPENROUTER_API_KEY\");\n if (!apiKey) {\n throw new OpenRouterAuthError(\n \"OpenRouter API key is required. Get one at https://openrouter.ai/keys and set it via the `apiKey` parameter or the OPENROUTER_API_KEY environment variable.\"\n );\n }\n this.apiKey = apiKey;\n if (!fields.model) {\n throw new Error(\n 'ChatOpenRouter requires a `model` parameter, e.g. \"openai/gpt-4o-mini\".'\n );\n }\n this.model = fields.model;\n this.baseURL = fields.baseURL ?? DEFAULT_BASE_URL;\n this.temperature = fields.temperature;\n this.maxTokens = fields.maxTokens;\n this.topP = fields.topP;\n this.topK = fields.topK;\n this.frequencyPenalty = fields.frequencyPenalty;\n this.presencePenalty = fields.presencePenalty;\n this.repetitionPenalty = fields.repetitionPenalty;\n this.minP = fields.minP;\n this.topA = fields.topA;\n this.seed = fields.seed;\n this.stop = fields.stop;\n this.logitBias = fields.logitBias;\n this.topLogprobs = fields.topLogprobs;\n this.user = fields.user;\n this.transforms = fields.transforms;\n this.models = fields.models;\n this.route = fields.route;\n this.provider = fields.provider;\n this.plugins = fields.plugins;\n this.sessionId =\n fields.sessionId ?? getEnvironmentVariable(\"OPENROUTER_SESSION_ID\");\n this.trace = fields.trace;\n this.siteUrl = fields.siteUrl ?? \"https://docs.langchain.com\";\n this.siteName = fields.siteName ?? \"LangChain\";\n this.appCategories = fields.appCategories;\n this.modelKwargs = fields.modelKwargs;\n this.streamUsage = fields.streamUsage ?? true;\n }\n\n _llmType(): string {\n return \"openrouter\";\n }\n\n /** Static capability profile (context size, tool support, etc.) for the current model. */\n get profile(): ModelProfile {\n return PROFILES[this.model] ?? {};\n }\n\n /** Builds auth + content-type headers, plus optional attribution headers. */\n private buildHeaders(): Record<string, string> {\n const headers: Record<string, string> = {\n Authorization: `Bearer ${this.apiKey}`,\n \"Content-Type\": \"application/json\",\n };\n if (this.siteUrl) {\n headers[\"HTTP-Referer\"] = this.siteUrl;\n }\n if (this.siteName) {\n headers[\"X-Title\"] = this.siteName;\n }\n if (this.appCategories && this.appCategories.length > 0) {\n headers[\"X-OpenRouter-Categories\"] = this.appCategories.join(\",\");\n }\n return headers;\n }\n\n /** Returns the full chat-completions endpoint URL. */\n private buildUrl(): string {\n return `${this.baseURL}/chat/completions`;\n }\n\n /**\n * Merges constructor-level defaults with per-call overrides into the\n * API request body (everything except `messages`, which is added later).\n */\n override invocationParams(\n options: this[\"ParsedCallOptions\"]\n ): Omit<OpenRouterRequestBody, \"messages\"> {\n const tools = options.tools\n ? convertToolsToOpenRouter(options.tools, { strict: options.strict })\n : undefined;\n const toolChoice = formatToolChoice(options.tool_choice);\n const sessionId = options.sessionId ?? this.sessionId;\n const trace = options.trace ?? this.trace;\n\n return {\n model: this.model,\n temperature: options.temperature ?? this.temperature,\n max_tokens: options.maxTokens ?? this.maxTokens,\n top_p: options.topP ?? this.topP,\n top_k: options.topK ?? this.topK,\n frequency_penalty: options.frequencyPenalty ?? this.frequencyPenalty,\n presence_penalty: options.presencePenalty ?? this.presencePenalty,\n repetition_penalty: options.repetitionPenalty ?? this.repetitionPenalty,\n min_p: options.minP ?? this.minP,\n top_a: options.topA ?? this.topA,\n seed: options.seed ?? this.seed,\n stop: options.stop ?? this.stop,\n logit_bias: options.logitBias ?? this.logitBias,\n top_logprobs: options.topLogprobs ?? this.topLogprobs,\n user: options.user ?? this.user,\n tools,\n tool_choice: toolChoice,\n response_format: options.response_format,\n ...(options.prediction ? { prediction: options.prediction } : {}),\n transforms: options.transforms ?? this.transforms,\n models: options.models ?? this.models,\n route: options.route ?? this.route,\n provider: options.provider ?? this.provider,\n plugins: options.plugins ?? this.plugins,\n ...(sessionId ? { session_id: sessionId } : {}),\n ...(trace !== undefined ? { trace } : {}),\n ...this.modelKwargs,\n };\n }\n\n /** Returns metadata for LangSmith tracing (provider, model name, temperature, etc.). */\n override getLsParams(options: this[\"ParsedCallOptions\"]): LangSmithParams {\n const params = this.invocationParams(options);\n return {\n ls_provider: \"openrouter\",\n ls_model_name: this.model,\n ls_model_type: \"chat\",\n ls_temperature: params.temperature ?? undefined,\n ls_max_tokens: params.max_tokens ?? undefined,\n ls_stop: options.stop,\n };\n }\n\n /**\n * Non-streaming generation. Sends a single request and returns the\n * complete response with the generated message and token usage.\n */\n async _generate(\n messages: BaseMessage[],\n options: this[\"ParsedCallOptions\"],\n runManager?: CallbackManagerForLLMRun\n ): Promise<ChatResult> {\n const body: OpenRouterRequestBody = {\n ...this.invocationParams(options),\n messages: convertMessagesToOpenRouterParams(messages, this.model),\n stream: false,\n };\n\n const response = await fetch(this.buildUrl(), {\n method: \"POST\",\n headers: this.buildHeaders(),\n body: JSON.stringify(body),\n signal: options.signal,\n });\n\n if (!response.ok) {\n throw await OpenRouterError.fromResponse(response);\n }\n\n const data: OpenRouter.ChatResponse = await response.json();\n const choice = data.choices[0];\n\n if (!choice) {\n throw new OpenRouterError(\"No choices returned in response.\");\n }\n\n const message = convertOpenRouterResponseToBaseMessage(choice, data);\n if (AIMessage.isInstance(message)) {\n message.usage_metadata = convertUsageMetadata(data.usage);\n }\n\n const text = typeof message.content === \"string\" ? message.content : \"\";\n\n await runManager?.handleLLMNewToken(text);\n\n return {\n generations: [\n {\n text,\n message,\n generationInfo: {\n finish_reason: choice.finish_reason,\n },\n },\n ],\n llmOutput: { tokenUsage: data.usage },\n };\n }\n\n async *_streamChatModelEvents(\n messages: BaseMessage[],\n options: this[\"ParsedCallOptions\"],\n _runManager?: CallbackManagerForLLMRun\n ): AsyncGenerator<ChatModelStreamEvent> {\n const body: OpenRouterRequestBody = {\n ...this.invocationParams(options),\n messages: convertMessagesToOpenRouterParams(messages, this.model),\n stream: true,\n };\n\n const response = await fetch(this.buildUrl(), {\n method: \"POST\",\n headers: this.buildHeaders(),\n body: JSON.stringify(body),\n signal: options.signal,\n });\n\n if (!response.ok) {\n throw await OpenRouterError.fromResponse(response);\n }\n\n if (!response.body) {\n return;\n }\n\n const stream = response.body\n .pipeThrough(new TextDecoderStream())\n .pipeThrough(new EventSourceParserStream())\n .pipeThrough(new OpenRouterJsonParseStream());\n\n const reader = stream.getReader();\n async function* readChunks() {\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n if (value) yield value;\n }\n } finally {\n reader.releaseLock();\n }\n }\n\n const shouldStreamUsage = this.streamUsage ?? options.streamUsage ?? true;\n yield* convertOpenRouterStream(readChunks(), {\n streamUsage: shouldStreamUsage,\n });\n }\n\n /**\n * Streaming generation. Opens an SSE connection and yields one\n * `ChatGenerationChunk` per delta received from the API. The stream\n * pipeline is: raw bytes -> text -> SSE events -> JSON-parsed deltas.\n */\n async *_streamResponseChunks(\n messages: BaseMessage[],\n options: this[\"ParsedCallOptions\"],\n runManager?: CallbackManagerForLLMRun\n ): AsyncGenerator<ChatGenerationChunk> {\n const body: OpenRouterRequestBody = {\n ...this.invocationParams(options),\n messages: convertMessagesToOpenRouterParams(messages, this.model),\n stream: true,\n };\n\n const response = await fetch(this.buildUrl(), {\n method: \"POST\",\n headers: this.buildHeaders(),\n body: JSON.stringify(body),\n signal: options.signal,\n });\n\n if (!response.ok) {\n throw await OpenRouterError.fromResponse(response);\n }\n\n if (!response.body) {\n return;\n }\n\n const stream = response.body\n .pipeThrough(new TextDecoderStream())\n .pipeThrough(new EventSourceParserStream())\n .pipeThrough(new OpenRouterJsonParseStream());\n\n const reader = stream.getReader();\n let defaultRole: string | undefined;\n\n try {\n while (true) {\n const { done, value: data } = await reader.read();\n if (done) break;\n if (!data) continue;\n\n const choice = data.choices?.[0];\n if (!choice?.delta) continue;\n\n const chunk = convertOpenRouterDeltaToBaseMessageChunk(\n choice.delta,\n data,\n defaultRole\n );\n defaultRole = choice.delta.role ?? defaultRole;\n\n if (\n data.usage &&\n this.streamUsage &&\n AIMessageChunk.isInstance(chunk)\n ) {\n chunk.usage_metadata = convertUsageMetadata(data.usage);\n }\n\n const text = typeof chunk.content === \"string\" ? chunk.content : \"\";\n\n const generationChunk = new ChatGenerationChunk({\n message: chunk,\n text,\n generationInfo: {\n ...(choice.finish_reason\n ? { finish_reason: choice.finish_reason }\n : {}),\n },\n });\n\n yield generationChunk;\n await runManager?.handleLLMNewToken(\n text,\n undefined,\n undefined,\n undefined,\n undefined,\n { chunk: generationChunk }\n );\n }\n } finally {\n reader.releaseLock();\n }\n }\n\n /**\n * Returns a new Runnable with the given tools bound into every call.\n * Equivalent to `.withConfig({ tools, ...kwargs })`.\n */\n override bindTools(\n tools: BindToolsInput[],\n kwargs?: Partial<ChatOpenRouterCallOptions>\n ): Runnable<\n BaseLanguageModelInput,\n AIMessageChunk,\n ChatOpenRouterCallOptions\n > {\n return this.withConfig({\n ...kwargs,\n tools,\n } as Partial<ChatOpenRouterCallOptions>);\n }\n\n /**\n * Returns a Runnable that forces the model to produce output conforming\n * to `outputSchema` (a Zod schema or plain JSON Schema object).\n *\n * The extraction strategy (JSON Schema response format, function calling,\n * or JSON mode) is chosen automatically based on model capabilities —\n * see {@link resolveOpenRouterStructuredOutputMethod}. You can override\n * this via `config.method`.\n *\n * When `config.includeRaw` is `true` the returned object contains both\n * the raw `BaseMessage` and the parsed output, with a fallback that\n * sets `parsed: null` if the parser throws.\n */\n withStructuredOutput<\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n RunOutput extends Record<string, any> = Record<string, any>,\n >(\n outputSchema:\n | InteropZodType<RunOutput>\n | SerializableSchema<RunOutput>\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n | Record<string, any>,\n config?: StructuredOutputMethodOptions<false>\n ): Runnable<BaseLanguageModelInput, RunOutput>;\n\n withStructuredOutput<\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n RunOutput extends Record<string, any> = Record<string, any>,\n >(\n outputSchema:\n | InteropZodType<RunOutput>\n | SerializableSchema<RunOutput>\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n | Record<string, any>,\n config?: StructuredOutputMethodOptions<true>\n ): Runnable<BaseLanguageModelInput, { raw: BaseMessage; parsed: RunOutput }>;\n\n withStructuredOutput<\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n RunOutput extends Record<string, any> = Record<string, any>,\n >(\n outputSchema:\n | InteropZodType<RunOutput>\n | SerializableSchema<RunOutput>\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n | Record<string, any>,\n config?: StructuredOutputMethodOptions<boolean>\n ):\n | Runnable<BaseLanguageModelInput, RunOutput>\n | Runnable<BaseLanguageModelInput, { raw: BaseMessage; parsed: RunOutput }>;\n\n withStructuredOutput<\n RunOutput extends Record<string, unknown> = Record<string, unknown>,\n >(\n outputSchema:\n | InteropZodType<RunOutput>\n | SerializableSchema<RunOutput>\n | Record<string, unknown>,\n config?: StructuredOutputMethodOptions<boolean>\n ) {\n let llm: Runnable<BaseLanguageModelInput>;\n let outputParser: Runnable<AIMessageChunk, RunOutput>;\n\n const { schema, name, includeRaw } = {\n ...config,\n schema: outputSchema,\n };\n\n const method = resolveOpenRouterStructuredOutputMethod({\n model: this.model,\n method: config?.method,\n profile: this.profile,\n models: this.models,\n route: this.route,\n });\n\n const asJsonSchema = toJsonSchema(schema);\n\n if (method === \"jsonSchema\") {\n const schemaName = name ?? \"extract\";\n llm = this.withConfig({\n response_format: {\n type: \"json_schema\",\n json_schema: {\n name: schemaName,\n description: getSchemaDescription(schema),\n schema: asJsonSchema,\n strict: config?.strict,\n },\n },\n ls_structured_output_format: {\n kwargs: { method: \"json_schema\" },\n schema: {\n title: schemaName,\n description: getSchemaDescription(schema),\n ...asJsonSchema,\n },\n },\n } as Partial<ChatOpenRouterCallOptions>);\n\n outputParser = createContentParser(schema);\n } else if (method === \"jsonMode\") {\n llm = this.withConfig({\n response_format: { type: \"json_object\" },\n ls_structured_output_format: {\n kwargs: { method: \"json_mode\" },\n schema: { title: name ?? \"extract\", ...asJsonSchema },\n },\n } as Partial<ChatOpenRouterCallOptions>);\n outputParser = createContentParser(schema);\n } else {\n let functionName = name ?? \"extract\";\n if (\n !isInteropZodSchema(schema) &&\n !isSerializableSchema(schema) &&\n \"name\" in (schema as Record<string, unknown>)\n ) {\n functionName = (schema as Record<string, unknown>).name as string;\n }\n\n llm = this.withConfig({\n tools: [\n {\n type: \"function\" as const,\n function: {\n name: functionName,\n description: getSchemaDescription(schema) ?? \"\",\n parameters: asJsonSchema,\n },\n },\n ],\n tool_choice: {\n type: \"function\" as const,\n function: { name: functionName },\n },\n ls_structured_output_format: {\n kwargs: { method: \"function_calling\" },\n schema: { title: functionName, ...asJsonSchema },\n },\n ...(config?.strict !== undefined ? { strict: config.strict } : {}),\n } as Partial<ChatOpenRouterCallOptions>);\n\n outputParser = createFunctionCallingParser(schema, functionName);\n }\n\n return assembleStructuredOutputPipeline(\n llm,\n outputParser,\n includeRaw,\n \"ChatOpenRouterStructuredOutput\"\n );\n }\n}\n"],"mappings":"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"}
@@ -1,11 +1,12 @@
1
1
  import { OpenRouter } from "../api-types.cjs";
2
2
  import { ChatOpenRouterCallOptions, ChatOpenRouterParams } from "./types.cjs";
3
- import { BaseChatModel, BindToolsInput, LangSmithParams } from "@langchain/core/language_models/chat_models";
4
3
  import { ModelProfile } from "@langchain/core/language_models/profile";
4
+ import { BaseChatModel, BindToolsInput, LangSmithParams } from "@langchain/core/language_models/chat_models";
5
5
  import { BaseLanguageModelInput, StructuredOutputMethodOptions } from "@langchain/core/language_models/base";
6
6
  import { AIMessageChunk, BaseMessage } from "@langchain/core/messages";
7
7
  import { ChatGenerationChunk, ChatResult } from "@langchain/core/outputs";
8
8
  import { CallbackManagerForLLMRun } from "@langchain/core/callbacks/manager";
9
+ import { ChatModelStreamEvent } from "@langchain/core/language_models/event";
9
10
  import { Runnable } from "@langchain/core/runnables";
10
11
  import { InteropZodType } from "@langchain/core/utils/types";
11
12
  import { OpenAI } from "openai";
@@ -148,6 +149,7 @@ declare class ChatOpenRouter extends BaseChatModel<ChatOpenRouterCallOptions, AI
148
149
  * complete response with the generated message and token usage.
149
150
  */
150
151
  _generate(messages: BaseMessage[], options: this["ParsedCallOptions"], runManager?: CallbackManagerForLLMRun): Promise<ChatResult>;
152
+ _streamChatModelEvents(messages: BaseMessage[], options: this["ParsedCallOptions"], _runManager?: CallbackManagerForLLMRun): AsyncGenerator<ChatModelStreamEvent>;
151
153
  /**
152
154
  * Streaming generation. Opens an SSE connection and yields one
153
155
  * `ChatGenerationChunk` per delta received from the API. The stream
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.cts","names":[],"sources":["../../src/chat_models/index.ts"],"mappings":"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"}
1
+ {"version":3,"file":"index.d.cts","names":[],"sources":["../../src/chat_models/index.ts"],"mappings":"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"}
@@ -8,6 +8,7 @@ import { SerializableSchema } from "@langchain/core/utils/standard_schema";
8
8
  import { ModelProfile } from "@langchain/core/language_models/profile";
9
9
  import { BaseLanguageModelInput, StructuredOutputMethodOptions } from "@langchain/core/language_models/base";
10
10
  import { CallbackManagerForLLMRun } from "@langchain/core/callbacks/manager";
11
+ import { ChatModelStreamEvent } from "@langchain/core/language_models/event";
11
12
  import { Runnable } from "@langchain/core/runnables";
12
13
  import { OpenAI } from "openai";
13
14
 
@@ -148,6 +149,7 @@ declare class ChatOpenRouter extends BaseChatModel<ChatOpenRouterCallOptions, AI
148
149
  * complete response with the generated message and token usage.
149
150
  */
150
151
  _generate(messages: BaseMessage[], options: this["ParsedCallOptions"], runManager?: CallbackManagerForLLMRun): Promise<ChatResult>;
152
+ _streamChatModelEvents(messages: BaseMessage[], options: this["ParsedCallOptions"], _runManager?: CallbackManagerForLLMRun): AsyncGenerator<ChatModelStreamEvent>;
151
153
  /**
152
154
  * Streaming generation. Opens an SSE connection and yields one
153
155
  * `ChatGenerationChunk` per delta received from the API. The stream
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../../src/chat_models/index.ts"],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../../src/chat_models/index.ts"],"mappings":"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"}
@@ -1,3 +1,4 @@
1
+ import { convertOpenRouterStream } from "../utils/stream_events.js";
1
2
  import { convertMessagesToOpenRouterParams, convertOpenRouterDeltaToBaseMessageChunk, convertOpenRouterResponseToBaseMessage, convertUsageMetadata } from "../converters/messages.js";
2
3
  import { convertToolsToOpenRouter, formatToolChoice } from "../converters/tools.js";
3
4
  import { OpenRouterAuthError, OpenRouterError } from "../utils/errors.js";
@@ -151,7 +152,7 @@ var ChatOpenRouter = class extends BaseChatModel {
151
152
  model: modelOrFields
152
153
  } : modelOrFields;
153
154
  super(fields);
154
- this._addVersion("@langchain/openrouter", "0.2.5");
155
+ this._addVersion("@langchain/openrouter", "0.4.0");
155
156
  const apiKey = fields.apiKey ?? getEnvironmentVariable("OPENROUTER_API_KEY");
156
157
  if (!apiKey) throw new OpenRouterAuthError("OpenRouter API key is required. Get one at https://openrouter.ai/keys and set it via the `apiKey` parameter or the OPENROUTER_API_KEY environment variable.");
157
158
  this.apiKey = apiKey;
@@ -291,6 +292,35 @@ var ChatOpenRouter = class extends BaseChatModel {
291
292
  llmOutput: { tokenUsage: data.usage }
292
293
  };
293
294
  }
295
+ async *_streamChatModelEvents(messages, options, _runManager) {
296
+ const body = {
297
+ ...this.invocationParams(options),
298
+ messages: convertMessagesToOpenRouterParams(messages, this.model),
299
+ stream: true
300
+ };
301
+ const response = await fetch(this.buildUrl(), {
302
+ method: "POST",
303
+ headers: this.buildHeaders(),
304
+ body: JSON.stringify(body),
305
+ signal: options.signal
306
+ });
307
+ if (!response.ok) throw await OpenRouterError.fromResponse(response);
308
+ if (!response.body) return;
309
+ const reader = response.body.pipeThrough(new TextDecoderStream()).pipeThrough(new EventSourceParserStream()).pipeThrough(new OpenRouterJsonParseStream()).getReader();
310
+ async function* readChunks() {
311
+ try {
312
+ while (true) {
313
+ const { done, value } = await reader.read();
314
+ if (done) break;
315
+ if (value) yield value;
316
+ }
317
+ } finally {
318
+ reader.releaseLock();
319
+ }
320
+ }
321
+ const shouldStreamUsage = this.streamUsage ?? options.streamUsage ?? true;
322
+ yield* convertOpenRouterStream(readChunks(), { streamUsage: shouldStreamUsage });
323
+ }
294
324
  /**
295
325
  * Streaming generation. Opens an SSE connection and yields one
296
326
  * `ChatGenerationChunk` per delta received from the API. The stream
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../src/chat_models/index.ts"],"sourcesContent":["import {\n BaseChatModel,\n type BindToolsInput,\n type LangSmithParams,\n} from \"@langchain/core/language_models/chat_models\";\nimport type { ModelProfile } from \"@langchain/core/language_models/profile\";\nimport type {\n BaseLanguageModelInput,\n StructuredOutputMethodOptions,\n} from \"@langchain/core/language_models/base\";\nimport {\n AIMessage,\n AIMessageChunk,\n BaseMessage,\n} from \"@langchain/core/messages\";\nimport { ChatGenerationChunk, type ChatResult } from \"@langchain/core/outputs\";\nimport type { CallbackManagerForLLMRun } from \"@langchain/core/callbacks/manager\";\nimport { Runnable } from \"@langchain/core/runnables\";\nimport { toJsonSchema } from \"@langchain/core/utils/json_schema\";\nimport {\n type InteropZodType,\n getSchemaDescription,\n isInteropZodSchema,\n} from \"@langchain/core/utils/types\";\nimport { getEnvironmentVariable } from \"@langchain/core/utils/env\";\nimport { EventSourceParserStream } from \"eventsource-parser/stream\";\n\nimport type {\n ChatOpenRouterParams,\n ChatOpenRouterCallOptions,\n} from \"./types.js\";\nimport type { OpenAI as OpenAIClient } from \"openai\";\nimport type { OpenRouter } from \"../api-types.js\";\n\n/**\n * Full request body sent to the OpenRouter `/chat/completions` endpoint.\n *\n * Extends the base generation params with OpenRouter-specific sampling\n * knobs (`top_k`, `min_p`, etc.) and features (`prediction`, `transforms`)\n * that aren't part of the standard OpenAI spec.\n */\ntype OpenRouterRequestBody = Omit<\n OpenRouter.ChatGenerationParams,\n \"messages\"\n> & {\n messages: OpenAIClient.Chat.Completions.ChatCompletionMessageParam[];\n top_k?: number | null;\n repetition_penalty?: number | null;\n min_p?: number | null;\n top_a?: number | null;\n prediction?: { type: \"content\"; content: string };\n transforms?: string[];\n};\nimport {\n convertMessagesToOpenRouterParams,\n convertOpenRouterResponseToBaseMessage,\n convertOpenRouterDeltaToBaseMessageChunk,\n convertUsageMetadata,\n} from \"../converters/messages.js\";\nimport {\n convertToolsToOpenRouter,\n formatToolChoice,\n} from \"../converters/tools.js\";\nimport { OpenRouterError, OpenRouterAuthError } from \"../utils/errors.js\";\nimport { resolveOpenRouterStructuredOutputMethod } from \"../utils/structured_output.js\";\nimport { OpenRouterJsonParseStream } from \"../utils/stream.js\";\nimport PROFILES from \"../profiles.js\";\nimport {\n isSerializableSchema,\n SerializableSchema,\n} from \"@langchain/core/utils/standard_schema\";\nimport {\n assembleStructuredOutputPipeline,\n createContentParser,\n createFunctionCallingParser,\n} from \"@langchain/core/language_models/structured_output\";\n\nconst DEFAULT_BASE_URL = \"https://openrouter.ai/api/v1\";\n\n/**\n * OpenRouter chat model integration.\n *\n * Talks directly to the OpenRouter REST API via `fetch` (no SDK dependency)\n * and supports tool calling, structured output, and streaming. Any model\n * available on OpenRouter can be used by passing its identifier (e.g.\n * `\"anthropic/claude-4-sonnet\"`) as the `model` param.\n */\nexport class ChatOpenRouter extends BaseChatModel<\n ChatOpenRouterCallOptions,\n AIMessageChunk\n> {\n static lc_name() {\n return \"ChatOpenRouter\";\n }\n\n lc_serializable = true;\n\n /** Maps secret fields to the environment variable they can be loaded from. */\n get lc_secrets(): { [key: string]: string } | undefined {\n return {\n apiKey: \"OPENROUTER_API_KEY\",\n };\n }\n\n /** Allows serialized JSON to use `modelName` as an alias for `model`. */\n get lc_aliases(): Record<string, string> {\n return {\n modelName: \"model\",\n };\n }\n\n /** Fields that may be overridden per-call via `.bind()` / `.withConfig()`. */\n get callKeys(): string[] {\n return [\n ...super.callKeys,\n \"tools\",\n \"tool_choice\",\n \"response_format\",\n \"seed\",\n \"strict\",\n \"temperature\",\n \"maxTokens\",\n \"topP\",\n \"topK\",\n \"frequencyPenalty\",\n \"presencePenalty\",\n \"repetitionPenalty\",\n \"minP\",\n \"topA\",\n \"stop\",\n \"logitBias\",\n \"topLogprobs\",\n \"user\",\n \"transforms\",\n \"models\",\n \"route\",\n \"provider\",\n \"plugins\",\n \"sessionId\",\n \"trace\",\n \"prediction\",\n ];\n }\n\n /** Model identifier, e.g. `\"anthropic/claude-4-sonnet\"`. */\n model: string;\n\n /** OpenRouter API key. Falls back to the `OPENROUTER_API_KEY` env var. */\n apiKey: string;\n\n /** Base URL for the API. Defaults to `\"https://openrouter.ai/api/v1\"`. */\n baseURL: string;\n\n /** Sampling temperature (0–2). */\n temperature?: number;\n\n /** Maximum number of tokens to generate. */\n maxTokens?: number;\n\n /** Nucleus sampling cutoff probability. */\n topP?: number;\n\n /** Top-K sampling: only consider the K most likely tokens. */\n topK?: number;\n\n /** Additive penalty based on how often a token has appeared so far (−2 to 2). */\n frequencyPenalty?: number;\n\n /** Additive penalty based on whether a token has appeared at all (−2 to 2). */\n presencePenalty?: number;\n\n /** Multiplicative penalty applied to repeated token logits (0 to 2). */\n repetitionPenalty?: number;\n\n /** Minimum probability threshold for token sampling. */\n minP?: number;\n\n /** Top-A sampling threshold. */\n topA?: number;\n\n /** Random seed for deterministic generation. */\n seed?: number;\n\n /** Stop sequences that halt generation. */\n stop?: string[];\n\n /** Token-level biases to apply during sampling. */\n logitBias?: Record<string, number>;\n\n /** Number of most-likely log-probabilities to return per token. */\n topLogprobs?: number;\n\n /** Stable identifier for end-users, used for abuse detection. */\n user?: string;\n\n /** OpenRouter-specific transformations to apply to the request. */\n transforms?: string[];\n\n /** OpenRouter-specific list of models for routing. */\n models?: string[];\n\n /** OpenRouter-specific routing strategy. */\n route?: \"fallback\";\n\n /** OpenRouter-specific provider preferences and ordering. */\n provider?: OpenRouter.ProviderPreferences;\n\n /** OpenRouter plugins to enable (e.g. web search). */\n plugins?: ChatOpenRouterParams[\"plugins\"];\n\n /** Identifier used by OpenRouter to group related requests together. */\n sessionId?: string;\n\n /** Trace metadata for OpenRouter broadcast destinations. */\n trace?: OpenRouter.TraceConfig;\n\n /**\n * Application URL for OpenRouter attribution. Maps to `HTTP-Referer` header.\n *\n * Defaults to LangChain docs URL. Set this to your app's URL to get\n * attribution for API usage in the OpenRouter dashboard.\n *\n * See https://openrouter.ai/docs/app-attribution for details.\n */\n siteUrl: string;\n\n /**\n * Application title for OpenRouter attribution. Maps to `X-Title` header.\n *\n * Defaults to `'LangChain'`. Set this to your app's name to get attribution\n * for API usage in the OpenRouter dashboard.\n *\n * See https://openrouter.ai/docs/app-attribution for details.\n */\n siteName: string;\n\n /**\n * Marketplace categories for OpenRouter attribution.\n * Maps to `X-OpenRouter-Categories` header.\n *\n * See https://openrouter.ai/docs/app-attribution for recognized categories.\n */\n appCategories?: string[];\n\n /** Extra params passed through to the API body. */\n modelKwargs?: Record<string, unknown>;\n\n /** Whether to include token usage in streaming chunks. Defaults to `true`. */\n streamUsage: boolean;\n\n constructor(model: string, fields?: Omit<ChatOpenRouterParams, \"model\">);\n constructor(fields: ChatOpenRouterParams);\n constructor(\n modelOrFields: string | ChatOpenRouterParams,\n fieldsArg?: Omit<ChatOpenRouterParams, \"model\">\n ) {\n const fields =\n typeof modelOrFields === \"string\"\n ? { ...(fieldsArg ?? {}), model: modelOrFields }\n : modelOrFields;\n super(fields);\n this._addVersion(\"@langchain/openrouter\", __PKG_VERSION__);\n const apiKey =\n fields.apiKey ?? getEnvironmentVariable(\"OPENROUTER_API_KEY\");\n if (!apiKey) {\n throw new OpenRouterAuthError(\n \"OpenRouter API key is required. Get one at https://openrouter.ai/keys and set it via the `apiKey` parameter or the OPENROUTER_API_KEY environment variable.\"\n );\n }\n this.apiKey = apiKey;\n if (!fields.model) {\n throw new Error(\n 'ChatOpenRouter requires a `model` parameter, e.g. \"openai/gpt-4o-mini\".'\n );\n }\n this.model = fields.model;\n this.baseURL = fields.baseURL ?? DEFAULT_BASE_URL;\n this.temperature = fields.temperature;\n this.maxTokens = fields.maxTokens;\n this.topP = fields.topP;\n this.topK = fields.topK;\n this.frequencyPenalty = fields.frequencyPenalty;\n this.presencePenalty = fields.presencePenalty;\n this.repetitionPenalty = fields.repetitionPenalty;\n this.minP = fields.minP;\n this.topA = fields.topA;\n this.seed = fields.seed;\n this.stop = fields.stop;\n this.logitBias = fields.logitBias;\n this.topLogprobs = fields.topLogprobs;\n this.user = fields.user;\n this.transforms = fields.transforms;\n this.models = fields.models;\n this.route = fields.route;\n this.provider = fields.provider;\n this.plugins = fields.plugins;\n this.sessionId =\n fields.sessionId ?? getEnvironmentVariable(\"OPENROUTER_SESSION_ID\");\n this.trace = fields.trace;\n this.siteUrl = fields.siteUrl ?? \"https://docs.langchain.com\";\n this.siteName = fields.siteName ?? \"LangChain\";\n this.appCategories = fields.appCategories;\n this.modelKwargs = fields.modelKwargs;\n this.streamUsage = fields.streamUsage ?? true;\n }\n\n _llmType(): string {\n return \"openrouter\";\n }\n\n /** Static capability profile (context size, tool support, etc.) for the current model. */\n get profile(): ModelProfile {\n return PROFILES[this.model] ?? {};\n }\n\n /** Builds auth + content-type headers, plus optional attribution headers. */\n private buildHeaders(): Record<string, string> {\n const headers: Record<string, string> = {\n Authorization: `Bearer ${this.apiKey}`,\n \"Content-Type\": \"application/json\",\n };\n if (this.siteUrl) {\n headers[\"HTTP-Referer\"] = this.siteUrl;\n }\n if (this.siteName) {\n headers[\"X-Title\"] = this.siteName;\n }\n if (this.appCategories && this.appCategories.length > 0) {\n headers[\"X-OpenRouter-Categories\"] = this.appCategories.join(\",\");\n }\n return headers;\n }\n\n /** Returns the full chat-completions endpoint URL. */\n private buildUrl(): string {\n return `${this.baseURL}/chat/completions`;\n }\n\n /**\n * Merges constructor-level defaults with per-call overrides into the\n * API request body (everything except `messages`, which is added later).\n */\n override invocationParams(\n options: this[\"ParsedCallOptions\"]\n ): Omit<OpenRouterRequestBody, \"messages\"> {\n const tools = options.tools\n ? convertToolsToOpenRouter(options.tools, { strict: options.strict })\n : undefined;\n const toolChoice = formatToolChoice(options.tool_choice);\n const sessionId = options.sessionId ?? this.sessionId;\n const trace = options.trace ?? this.trace;\n\n return {\n model: this.model,\n temperature: options.temperature ?? this.temperature,\n max_tokens: options.maxTokens ?? this.maxTokens,\n top_p: options.topP ?? this.topP,\n top_k: options.topK ?? this.topK,\n frequency_penalty: options.frequencyPenalty ?? this.frequencyPenalty,\n presence_penalty: options.presencePenalty ?? this.presencePenalty,\n repetition_penalty: options.repetitionPenalty ?? this.repetitionPenalty,\n min_p: options.minP ?? this.minP,\n top_a: options.topA ?? this.topA,\n seed: options.seed ?? this.seed,\n stop: options.stop ?? this.stop,\n logit_bias: options.logitBias ?? this.logitBias,\n top_logprobs: options.topLogprobs ?? this.topLogprobs,\n user: options.user ?? this.user,\n tools,\n tool_choice: toolChoice,\n response_format: options.response_format,\n ...(options.prediction ? { prediction: options.prediction } : {}),\n transforms: options.transforms ?? this.transforms,\n models: options.models ?? this.models,\n route: options.route ?? this.route,\n provider: options.provider ?? this.provider,\n plugins: options.plugins ?? this.plugins,\n ...(sessionId ? { session_id: sessionId } : {}),\n ...(trace !== undefined ? { trace } : {}),\n ...this.modelKwargs,\n };\n }\n\n /** Returns metadata for LangSmith tracing (provider, model name, temperature, etc.). */\n override getLsParams(options: this[\"ParsedCallOptions\"]): LangSmithParams {\n const params = this.invocationParams(options);\n return {\n ls_provider: \"openrouter\",\n ls_model_name: this.model,\n ls_model_type: \"chat\",\n ls_temperature: params.temperature ?? undefined,\n ls_max_tokens: params.max_tokens ?? undefined,\n ls_stop: options.stop,\n };\n }\n\n /**\n * Non-streaming generation. Sends a single request and returns the\n * complete response with the generated message and token usage.\n */\n async _generate(\n messages: BaseMessage[],\n options: this[\"ParsedCallOptions\"],\n runManager?: CallbackManagerForLLMRun\n ): Promise<ChatResult> {\n const body: OpenRouterRequestBody = {\n ...this.invocationParams(options),\n messages: convertMessagesToOpenRouterParams(messages, this.model),\n stream: false,\n };\n\n const response = await fetch(this.buildUrl(), {\n method: \"POST\",\n headers: this.buildHeaders(),\n body: JSON.stringify(body),\n signal: options.signal,\n });\n\n if (!response.ok) {\n throw await OpenRouterError.fromResponse(response);\n }\n\n const data: OpenRouter.ChatResponse = await response.json();\n const choice = data.choices[0];\n\n if (!choice) {\n throw new OpenRouterError(\"No choices returned in response.\");\n }\n\n const message = convertOpenRouterResponseToBaseMessage(choice, data);\n if (AIMessage.isInstance(message)) {\n message.usage_metadata = convertUsageMetadata(data.usage);\n }\n\n const text = typeof message.content === \"string\" ? message.content : \"\";\n\n await runManager?.handleLLMNewToken(text);\n\n return {\n generations: [\n {\n text,\n message,\n generationInfo: {\n finish_reason: choice.finish_reason,\n },\n },\n ],\n llmOutput: { tokenUsage: data.usage },\n };\n }\n\n /**\n * Streaming generation. Opens an SSE connection and yields one\n * `ChatGenerationChunk` per delta received from the API. The stream\n * pipeline is: raw bytes -> text -> SSE events -> JSON-parsed deltas.\n */\n async *_streamResponseChunks(\n messages: BaseMessage[],\n options: this[\"ParsedCallOptions\"],\n runManager?: CallbackManagerForLLMRun\n ): AsyncGenerator<ChatGenerationChunk> {\n const body: OpenRouterRequestBody = {\n ...this.invocationParams(options),\n messages: convertMessagesToOpenRouterParams(messages, this.model),\n stream: true,\n };\n\n const response = await fetch(this.buildUrl(), {\n method: \"POST\",\n headers: this.buildHeaders(),\n body: JSON.stringify(body),\n signal: options.signal,\n });\n\n if (!response.ok) {\n throw await OpenRouterError.fromResponse(response);\n }\n\n if (!response.body) {\n return;\n }\n\n const stream = response.body\n .pipeThrough(new TextDecoderStream())\n .pipeThrough(new EventSourceParserStream())\n .pipeThrough(new OpenRouterJsonParseStream());\n\n const reader = stream.getReader();\n let defaultRole: string | undefined;\n\n try {\n while (true) {\n const { done, value: data } = await reader.read();\n if (done) break;\n if (!data) continue;\n\n const choice = data.choices?.[0];\n if (!choice?.delta) continue;\n\n const chunk = convertOpenRouterDeltaToBaseMessageChunk(\n choice.delta,\n data,\n defaultRole\n );\n defaultRole = choice.delta.role ?? defaultRole;\n\n if (\n data.usage &&\n this.streamUsage &&\n AIMessageChunk.isInstance(chunk)\n ) {\n chunk.usage_metadata = convertUsageMetadata(data.usage);\n }\n\n const text = typeof chunk.content === \"string\" ? chunk.content : \"\";\n\n const generationChunk = new ChatGenerationChunk({\n message: chunk,\n text,\n generationInfo: {\n ...(choice.finish_reason\n ? { finish_reason: choice.finish_reason }\n : {}),\n },\n });\n\n yield generationChunk;\n await runManager?.handleLLMNewToken(\n text,\n undefined,\n undefined,\n undefined,\n undefined,\n { chunk: generationChunk }\n );\n }\n } finally {\n reader.releaseLock();\n }\n }\n\n /**\n * Returns a new Runnable with the given tools bound into every call.\n * Equivalent to `.withConfig({ tools, ...kwargs })`.\n */\n override bindTools(\n tools: BindToolsInput[],\n kwargs?: Partial<ChatOpenRouterCallOptions>\n ): Runnable<\n BaseLanguageModelInput,\n AIMessageChunk,\n ChatOpenRouterCallOptions\n > {\n return this.withConfig({\n ...kwargs,\n tools,\n } as Partial<ChatOpenRouterCallOptions>);\n }\n\n /**\n * Returns a Runnable that forces the model to produce output conforming\n * to `outputSchema` (a Zod schema or plain JSON Schema object).\n *\n * The extraction strategy (JSON Schema response format, function calling,\n * or JSON mode) is chosen automatically based on model capabilities —\n * see {@link resolveOpenRouterStructuredOutputMethod}. You can override\n * this via `config.method`.\n *\n * When `config.includeRaw` is `true` the returned object contains both\n * the raw `BaseMessage` and the parsed output, with a fallback that\n * sets `parsed: null` if the parser throws.\n */\n withStructuredOutput<\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n RunOutput extends Record<string, any> = Record<string, any>,\n >(\n outputSchema:\n | InteropZodType<RunOutput>\n | SerializableSchema<RunOutput>\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n | Record<string, any>,\n config?: StructuredOutputMethodOptions<false>\n ): Runnable<BaseLanguageModelInput, RunOutput>;\n\n withStructuredOutput<\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n RunOutput extends Record<string, any> = Record<string, any>,\n >(\n outputSchema:\n | InteropZodType<RunOutput>\n | SerializableSchema<RunOutput>\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n | Record<string, any>,\n config?: StructuredOutputMethodOptions<true>\n ): Runnable<BaseLanguageModelInput, { raw: BaseMessage; parsed: RunOutput }>;\n\n withStructuredOutput<\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n RunOutput extends Record<string, any> = Record<string, any>,\n >(\n outputSchema:\n | InteropZodType<RunOutput>\n | SerializableSchema<RunOutput>\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n | Record<string, any>,\n config?: StructuredOutputMethodOptions<boolean>\n ):\n | Runnable<BaseLanguageModelInput, RunOutput>\n | Runnable<BaseLanguageModelInput, { raw: BaseMessage; parsed: RunOutput }>;\n\n withStructuredOutput<\n RunOutput extends Record<string, unknown> = Record<string, unknown>,\n >(\n outputSchema:\n | InteropZodType<RunOutput>\n | SerializableSchema<RunOutput>\n | Record<string, unknown>,\n config?: StructuredOutputMethodOptions<boolean>\n ) {\n let llm: Runnable<BaseLanguageModelInput>;\n let outputParser: Runnable<AIMessageChunk, RunOutput>;\n\n const { schema, name, includeRaw } = {\n ...config,\n schema: outputSchema,\n };\n\n const method = resolveOpenRouterStructuredOutputMethod({\n model: this.model,\n method: config?.method,\n profile: this.profile,\n models: this.models,\n route: this.route,\n });\n\n const asJsonSchema = toJsonSchema(schema);\n\n if (method === \"jsonSchema\") {\n const schemaName = name ?? \"extract\";\n llm = this.withConfig({\n response_format: {\n type: \"json_schema\",\n json_schema: {\n name: schemaName,\n description: getSchemaDescription(schema),\n schema: asJsonSchema,\n strict: config?.strict,\n },\n },\n ls_structured_output_format: {\n kwargs: { method: \"json_schema\" },\n schema: {\n title: schemaName,\n description: getSchemaDescription(schema),\n ...asJsonSchema,\n },\n },\n } as Partial<ChatOpenRouterCallOptions>);\n\n outputParser = createContentParser(schema);\n } else if (method === \"jsonMode\") {\n llm = this.withConfig({\n response_format: { type: \"json_object\" },\n ls_structured_output_format: {\n kwargs: { method: \"json_mode\" },\n schema: { title: name ?? \"extract\", ...asJsonSchema },\n },\n } as Partial<ChatOpenRouterCallOptions>);\n outputParser = createContentParser(schema);\n } else {\n let functionName = name ?? \"extract\";\n if (\n !isInteropZodSchema(schema) &&\n !isSerializableSchema(schema) &&\n \"name\" in (schema as Record<string, unknown>)\n ) {\n functionName = (schema as Record<string, unknown>).name as string;\n }\n\n llm = this.withConfig({\n tools: [\n {\n type: \"function\" as const,\n function: {\n name: functionName,\n description: getSchemaDescription(schema) ?? \"\",\n parameters: asJsonSchema,\n },\n },\n ],\n tool_choice: {\n type: \"function\" as const,\n function: { name: functionName },\n },\n ls_structured_output_format: {\n kwargs: { method: \"function_calling\" },\n schema: { title: functionName, ...asJsonSchema },\n },\n ...(config?.strict !== undefined ? { strict: config.strict } : {}),\n } as Partial<ChatOpenRouterCallOptions>);\n\n outputParser = createFunctionCallingParser(schema, functionName);\n }\n\n return assembleStructuredOutputPipeline(\n llm,\n outputParser,\n includeRaw,\n \"ChatOpenRouterStructuredOutput\"\n );\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../src/chat_models/index.ts"],"sourcesContent":["import {\n BaseChatModel,\n type BindToolsInput,\n type LangSmithParams,\n} from \"@langchain/core/language_models/chat_models\";\nimport type { ModelProfile } from \"@langchain/core/language_models/profile\";\nimport type {\n BaseLanguageModelInput,\n StructuredOutputMethodOptions,\n} from \"@langchain/core/language_models/base\";\nimport {\n AIMessage,\n AIMessageChunk,\n BaseMessage,\n} from \"@langchain/core/messages\";\nimport { ChatGenerationChunk, type ChatResult } from \"@langchain/core/outputs\";\nimport type { CallbackManagerForLLMRun } from \"@langchain/core/callbacks/manager\";\nimport type { ChatModelStreamEvent } from \"@langchain/core/language_models/event\";\nimport { convertOpenRouterStream } from \"../utils/stream_events.js\";\nimport { Runnable } from \"@langchain/core/runnables\";\nimport { toJsonSchema } from \"@langchain/core/utils/json_schema\";\nimport {\n type InteropZodType,\n getSchemaDescription,\n isInteropZodSchema,\n} from \"@langchain/core/utils/types\";\nimport { getEnvironmentVariable } from \"@langchain/core/utils/env\";\nimport { EventSourceParserStream } from \"eventsource-parser/stream\";\n\nimport type {\n ChatOpenRouterParams,\n ChatOpenRouterCallOptions,\n} from \"./types.js\";\nimport type { OpenAI as OpenAIClient } from \"openai\";\nimport type { OpenRouter } from \"../api-types.js\";\n\n/**\n * Full request body sent to the OpenRouter `/chat/completions` endpoint.\n *\n * Extends the base generation params with OpenRouter-specific sampling\n * knobs (`top_k`, `min_p`, etc.) and features (`prediction`, `transforms`)\n * that aren't part of the standard OpenAI spec.\n */\ntype OpenRouterRequestBody = Omit<\n OpenRouter.ChatGenerationParams,\n \"messages\"\n> & {\n messages: OpenAIClient.Chat.Completions.ChatCompletionMessageParam[];\n top_k?: number | null;\n repetition_penalty?: number | null;\n min_p?: number | null;\n top_a?: number | null;\n prediction?: { type: \"content\"; content: string };\n transforms?: string[];\n};\nimport {\n convertMessagesToOpenRouterParams,\n convertOpenRouterResponseToBaseMessage,\n convertOpenRouterDeltaToBaseMessageChunk,\n convertUsageMetadata,\n} from \"../converters/messages.js\";\nimport {\n convertToolsToOpenRouter,\n formatToolChoice,\n} from \"../converters/tools.js\";\nimport { OpenRouterError, OpenRouterAuthError } from \"../utils/errors.js\";\nimport { resolveOpenRouterStructuredOutputMethod } from \"../utils/structured_output.js\";\nimport { OpenRouterJsonParseStream } from \"../utils/stream.js\";\nimport PROFILES from \"../profiles.js\";\nimport {\n isSerializableSchema,\n SerializableSchema,\n} from \"@langchain/core/utils/standard_schema\";\nimport {\n assembleStructuredOutputPipeline,\n createContentParser,\n createFunctionCallingParser,\n} from \"@langchain/core/language_models/structured_output\";\n\nconst DEFAULT_BASE_URL = \"https://openrouter.ai/api/v1\";\n\n/**\n * OpenRouter chat model integration.\n *\n * Talks directly to the OpenRouter REST API via `fetch` (no SDK dependency)\n * and supports tool calling, structured output, and streaming. Any model\n * available on OpenRouter can be used by passing its identifier (e.g.\n * `\"anthropic/claude-4-sonnet\"`) as the `model` param.\n */\nexport class ChatOpenRouter extends BaseChatModel<\n ChatOpenRouterCallOptions,\n AIMessageChunk\n> {\n static lc_name() {\n return \"ChatOpenRouter\";\n }\n\n lc_serializable = true;\n\n /** Maps secret fields to the environment variable they can be loaded from. */\n get lc_secrets(): { [key: string]: string } | undefined {\n return {\n apiKey: \"OPENROUTER_API_KEY\",\n };\n }\n\n /** Allows serialized JSON to use `modelName` as an alias for `model`. */\n get lc_aliases(): Record<string, string> {\n return {\n modelName: \"model\",\n };\n }\n\n /** Fields that may be overridden per-call via `.bind()` / `.withConfig()`. */\n get callKeys(): string[] {\n return [\n ...super.callKeys,\n \"tools\",\n \"tool_choice\",\n \"response_format\",\n \"seed\",\n \"strict\",\n \"temperature\",\n \"maxTokens\",\n \"topP\",\n \"topK\",\n \"frequencyPenalty\",\n \"presencePenalty\",\n \"repetitionPenalty\",\n \"minP\",\n \"topA\",\n \"stop\",\n \"logitBias\",\n \"topLogprobs\",\n \"user\",\n \"transforms\",\n \"models\",\n \"route\",\n \"provider\",\n \"plugins\",\n \"sessionId\",\n \"trace\",\n \"prediction\",\n ];\n }\n\n /** Model identifier, e.g. `\"anthropic/claude-4-sonnet\"`. */\n model: string;\n\n /** OpenRouter API key. Falls back to the `OPENROUTER_API_KEY` env var. */\n apiKey: string;\n\n /** Base URL for the API. Defaults to `\"https://openrouter.ai/api/v1\"`. */\n baseURL: string;\n\n /** Sampling temperature (0–2). */\n temperature?: number;\n\n /** Maximum number of tokens to generate. */\n maxTokens?: number;\n\n /** Nucleus sampling cutoff probability. */\n topP?: number;\n\n /** Top-K sampling: only consider the K most likely tokens. */\n topK?: number;\n\n /** Additive penalty based on how often a token has appeared so far (−2 to 2). */\n frequencyPenalty?: number;\n\n /** Additive penalty based on whether a token has appeared at all (−2 to 2). */\n presencePenalty?: number;\n\n /** Multiplicative penalty applied to repeated token logits (0 to 2). */\n repetitionPenalty?: number;\n\n /** Minimum probability threshold for token sampling. */\n minP?: number;\n\n /** Top-A sampling threshold. */\n topA?: number;\n\n /** Random seed for deterministic generation. */\n seed?: number;\n\n /** Stop sequences that halt generation. */\n stop?: string[];\n\n /** Token-level biases to apply during sampling. */\n logitBias?: Record<string, number>;\n\n /** Number of most-likely log-probabilities to return per token. */\n topLogprobs?: number;\n\n /** Stable identifier for end-users, used for abuse detection. */\n user?: string;\n\n /** OpenRouter-specific transformations to apply to the request. */\n transforms?: string[];\n\n /** OpenRouter-specific list of models for routing. */\n models?: string[];\n\n /** OpenRouter-specific routing strategy. */\n route?: \"fallback\";\n\n /** OpenRouter-specific provider preferences and ordering. */\n provider?: OpenRouter.ProviderPreferences;\n\n /** OpenRouter plugins to enable (e.g. web search). */\n plugins?: ChatOpenRouterParams[\"plugins\"];\n\n /** Identifier used by OpenRouter to group related requests together. */\n sessionId?: string;\n\n /** Trace metadata for OpenRouter broadcast destinations. */\n trace?: OpenRouter.TraceConfig;\n\n /**\n * Application URL for OpenRouter attribution. Maps to `HTTP-Referer` header.\n *\n * Defaults to LangChain docs URL. Set this to your app's URL to get\n * attribution for API usage in the OpenRouter dashboard.\n *\n * See https://openrouter.ai/docs/app-attribution for details.\n */\n siteUrl: string;\n\n /**\n * Application title for OpenRouter attribution. Maps to `X-Title` header.\n *\n * Defaults to `'LangChain'`. Set this to your app's name to get attribution\n * for API usage in the OpenRouter dashboard.\n *\n * See https://openrouter.ai/docs/app-attribution for details.\n */\n siteName: string;\n\n /**\n * Marketplace categories for OpenRouter attribution.\n * Maps to `X-OpenRouter-Categories` header.\n *\n * See https://openrouter.ai/docs/app-attribution for recognized categories.\n */\n appCategories?: string[];\n\n /** Extra params passed through to the API body. */\n modelKwargs?: Record<string, unknown>;\n\n /** Whether to include token usage in streaming chunks. Defaults to `true`. */\n streamUsage: boolean;\n\n constructor(model: string, fields?: Omit<ChatOpenRouterParams, \"model\">);\n constructor(fields: ChatOpenRouterParams);\n constructor(\n modelOrFields: string | ChatOpenRouterParams,\n fieldsArg?: Omit<ChatOpenRouterParams, \"model\">\n ) {\n const fields =\n typeof modelOrFields === \"string\"\n ? { ...(fieldsArg ?? {}), model: modelOrFields }\n : modelOrFields;\n super(fields);\n this._addVersion(\"@langchain/openrouter\", __PKG_VERSION__);\n const apiKey =\n fields.apiKey ?? getEnvironmentVariable(\"OPENROUTER_API_KEY\");\n if (!apiKey) {\n throw new OpenRouterAuthError(\n \"OpenRouter API key is required. Get one at https://openrouter.ai/keys and set it via the `apiKey` parameter or the OPENROUTER_API_KEY environment variable.\"\n );\n }\n this.apiKey = apiKey;\n if (!fields.model) {\n throw new Error(\n 'ChatOpenRouter requires a `model` parameter, e.g. \"openai/gpt-4o-mini\".'\n );\n }\n this.model = fields.model;\n this.baseURL = fields.baseURL ?? DEFAULT_BASE_URL;\n this.temperature = fields.temperature;\n this.maxTokens = fields.maxTokens;\n this.topP = fields.topP;\n this.topK = fields.topK;\n this.frequencyPenalty = fields.frequencyPenalty;\n this.presencePenalty = fields.presencePenalty;\n this.repetitionPenalty = fields.repetitionPenalty;\n this.minP = fields.minP;\n this.topA = fields.topA;\n this.seed = fields.seed;\n this.stop = fields.stop;\n this.logitBias = fields.logitBias;\n this.topLogprobs = fields.topLogprobs;\n this.user = fields.user;\n this.transforms = fields.transforms;\n this.models = fields.models;\n this.route = fields.route;\n this.provider = fields.provider;\n this.plugins = fields.plugins;\n this.sessionId =\n fields.sessionId ?? getEnvironmentVariable(\"OPENROUTER_SESSION_ID\");\n this.trace = fields.trace;\n this.siteUrl = fields.siteUrl ?? \"https://docs.langchain.com\";\n this.siteName = fields.siteName ?? \"LangChain\";\n this.appCategories = fields.appCategories;\n this.modelKwargs = fields.modelKwargs;\n this.streamUsage = fields.streamUsage ?? true;\n }\n\n _llmType(): string {\n return \"openrouter\";\n }\n\n /** Static capability profile (context size, tool support, etc.) for the current model. */\n get profile(): ModelProfile {\n return PROFILES[this.model] ?? {};\n }\n\n /** Builds auth + content-type headers, plus optional attribution headers. */\n private buildHeaders(): Record<string, string> {\n const headers: Record<string, string> = {\n Authorization: `Bearer ${this.apiKey}`,\n \"Content-Type\": \"application/json\",\n };\n if (this.siteUrl) {\n headers[\"HTTP-Referer\"] = this.siteUrl;\n }\n if (this.siteName) {\n headers[\"X-Title\"] = this.siteName;\n }\n if (this.appCategories && this.appCategories.length > 0) {\n headers[\"X-OpenRouter-Categories\"] = this.appCategories.join(\",\");\n }\n return headers;\n }\n\n /** Returns the full chat-completions endpoint URL. */\n private buildUrl(): string {\n return `${this.baseURL}/chat/completions`;\n }\n\n /**\n * Merges constructor-level defaults with per-call overrides into the\n * API request body (everything except `messages`, which is added later).\n */\n override invocationParams(\n options: this[\"ParsedCallOptions\"]\n ): Omit<OpenRouterRequestBody, \"messages\"> {\n const tools = options.tools\n ? convertToolsToOpenRouter(options.tools, { strict: options.strict })\n : undefined;\n const toolChoice = formatToolChoice(options.tool_choice);\n const sessionId = options.sessionId ?? this.sessionId;\n const trace = options.trace ?? this.trace;\n\n return {\n model: this.model,\n temperature: options.temperature ?? this.temperature,\n max_tokens: options.maxTokens ?? this.maxTokens,\n top_p: options.topP ?? this.topP,\n top_k: options.topK ?? this.topK,\n frequency_penalty: options.frequencyPenalty ?? this.frequencyPenalty,\n presence_penalty: options.presencePenalty ?? this.presencePenalty,\n repetition_penalty: options.repetitionPenalty ?? this.repetitionPenalty,\n min_p: options.minP ?? this.minP,\n top_a: options.topA ?? this.topA,\n seed: options.seed ?? this.seed,\n stop: options.stop ?? this.stop,\n logit_bias: options.logitBias ?? this.logitBias,\n top_logprobs: options.topLogprobs ?? this.topLogprobs,\n user: options.user ?? this.user,\n tools,\n tool_choice: toolChoice,\n response_format: options.response_format,\n ...(options.prediction ? { prediction: options.prediction } : {}),\n transforms: options.transforms ?? this.transforms,\n models: options.models ?? this.models,\n route: options.route ?? this.route,\n provider: options.provider ?? this.provider,\n plugins: options.plugins ?? this.plugins,\n ...(sessionId ? { session_id: sessionId } : {}),\n ...(trace !== undefined ? { trace } : {}),\n ...this.modelKwargs,\n };\n }\n\n /** Returns metadata for LangSmith tracing (provider, model name, temperature, etc.). */\n override getLsParams(options: this[\"ParsedCallOptions\"]): LangSmithParams {\n const params = this.invocationParams(options);\n return {\n ls_provider: \"openrouter\",\n ls_model_name: this.model,\n ls_model_type: \"chat\",\n ls_temperature: params.temperature ?? undefined,\n ls_max_tokens: params.max_tokens ?? undefined,\n ls_stop: options.stop,\n };\n }\n\n /**\n * Non-streaming generation. Sends a single request and returns the\n * complete response with the generated message and token usage.\n */\n async _generate(\n messages: BaseMessage[],\n options: this[\"ParsedCallOptions\"],\n runManager?: CallbackManagerForLLMRun\n ): Promise<ChatResult> {\n const body: OpenRouterRequestBody = {\n ...this.invocationParams(options),\n messages: convertMessagesToOpenRouterParams(messages, this.model),\n stream: false,\n };\n\n const response = await fetch(this.buildUrl(), {\n method: \"POST\",\n headers: this.buildHeaders(),\n body: JSON.stringify(body),\n signal: options.signal,\n });\n\n if (!response.ok) {\n throw await OpenRouterError.fromResponse(response);\n }\n\n const data: OpenRouter.ChatResponse = await response.json();\n const choice = data.choices[0];\n\n if (!choice) {\n throw new OpenRouterError(\"No choices returned in response.\");\n }\n\n const message = convertOpenRouterResponseToBaseMessage(choice, data);\n if (AIMessage.isInstance(message)) {\n message.usage_metadata = convertUsageMetadata(data.usage);\n }\n\n const text = typeof message.content === \"string\" ? message.content : \"\";\n\n await runManager?.handleLLMNewToken(text);\n\n return {\n generations: [\n {\n text,\n message,\n generationInfo: {\n finish_reason: choice.finish_reason,\n },\n },\n ],\n llmOutput: { tokenUsage: data.usage },\n };\n }\n\n async *_streamChatModelEvents(\n messages: BaseMessage[],\n options: this[\"ParsedCallOptions\"],\n _runManager?: CallbackManagerForLLMRun\n ): AsyncGenerator<ChatModelStreamEvent> {\n const body: OpenRouterRequestBody = {\n ...this.invocationParams(options),\n messages: convertMessagesToOpenRouterParams(messages, this.model),\n stream: true,\n };\n\n const response = await fetch(this.buildUrl(), {\n method: \"POST\",\n headers: this.buildHeaders(),\n body: JSON.stringify(body),\n signal: options.signal,\n });\n\n if (!response.ok) {\n throw await OpenRouterError.fromResponse(response);\n }\n\n if (!response.body) {\n return;\n }\n\n const stream = response.body\n .pipeThrough(new TextDecoderStream())\n .pipeThrough(new EventSourceParserStream())\n .pipeThrough(new OpenRouterJsonParseStream());\n\n const reader = stream.getReader();\n async function* readChunks() {\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n if (value) yield value;\n }\n } finally {\n reader.releaseLock();\n }\n }\n\n const shouldStreamUsage = this.streamUsage ?? options.streamUsage ?? true;\n yield* convertOpenRouterStream(readChunks(), {\n streamUsage: shouldStreamUsage,\n });\n }\n\n /**\n * Streaming generation. Opens an SSE connection and yields one\n * `ChatGenerationChunk` per delta received from the API. The stream\n * pipeline is: raw bytes -> text -> SSE events -> JSON-parsed deltas.\n */\n async *_streamResponseChunks(\n messages: BaseMessage[],\n options: this[\"ParsedCallOptions\"],\n runManager?: CallbackManagerForLLMRun\n ): AsyncGenerator<ChatGenerationChunk> {\n const body: OpenRouterRequestBody = {\n ...this.invocationParams(options),\n messages: convertMessagesToOpenRouterParams(messages, this.model),\n stream: true,\n };\n\n const response = await fetch(this.buildUrl(), {\n method: \"POST\",\n headers: this.buildHeaders(),\n body: JSON.stringify(body),\n signal: options.signal,\n });\n\n if (!response.ok) {\n throw await OpenRouterError.fromResponse(response);\n }\n\n if (!response.body) {\n return;\n }\n\n const stream = response.body\n .pipeThrough(new TextDecoderStream())\n .pipeThrough(new EventSourceParserStream())\n .pipeThrough(new OpenRouterJsonParseStream());\n\n const reader = stream.getReader();\n let defaultRole: string | undefined;\n\n try {\n while (true) {\n const { done, value: data } = await reader.read();\n if (done) break;\n if (!data) continue;\n\n const choice = data.choices?.[0];\n if (!choice?.delta) continue;\n\n const chunk = convertOpenRouterDeltaToBaseMessageChunk(\n choice.delta,\n data,\n defaultRole\n );\n defaultRole = choice.delta.role ?? defaultRole;\n\n if (\n data.usage &&\n this.streamUsage &&\n AIMessageChunk.isInstance(chunk)\n ) {\n chunk.usage_metadata = convertUsageMetadata(data.usage);\n }\n\n const text = typeof chunk.content === \"string\" ? chunk.content : \"\";\n\n const generationChunk = new ChatGenerationChunk({\n message: chunk,\n text,\n generationInfo: {\n ...(choice.finish_reason\n ? { finish_reason: choice.finish_reason }\n : {}),\n },\n });\n\n yield generationChunk;\n await runManager?.handleLLMNewToken(\n text,\n undefined,\n undefined,\n undefined,\n undefined,\n { chunk: generationChunk }\n );\n }\n } finally {\n reader.releaseLock();\n }\n }\n\n /**\n * Returns a new Runnable with the given tools bound into every call.\n * Equivalent to `.withConfig({ tools, ...kwargs })`.\n */\n override bindTools(\n tools: BindToolsInput[],\n kwargs?: Partial<ChatOpenRouterCallOptions>\n ): Runnable<\n BaseLanguageModelInput,\n AIMessageChunk,\n ChatOpenRouterCallOptions\n > {\n return this.withConfig({\n ...kwargs,\n tools,\n } as Partial<ChatOpenRouterCallOptions>);\n }\n\n /**\n * Returns a Runnable that forces the model to produce output conforming\n * to `outputSchema` (a Zod schema or plain JSON Schema object).\n *\n * The extraction strategy (JSON Schema response format, function calling,\n * or JSON mode) is chosen automatically based on model capabilities —\n * see {@link resolveOpenRouterStructuredOutputMethod}. You can override\n * this via `config.method`.\n *\n * When `config.includeRaw` is `true` the returned object contains both\n * the raw `BaseMessage` and the parsed output, with a fallback that\n * sets `parsed: null` if the parser throws.\n */\n withStructuredOutput<\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n RunOutput extends Record<string, any> = Record<string, any>,\n >(\n outputSchema:\n | InteropZodType<RunOutput>\n | SerializableSchema<RunOutput>\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n | Record<string, any>,\n config?: StructuredOutputMethodOptions<false>\n ): Runnable<BaseLanguageModelInput, RunOutput>;\n\n withStructuredOutput<\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n RunOutput extends Record<string, any> = Record<string, any>,\n >(\n outputSchema:\n | InteropZodType<RunOutput>\n | SerializableSchema<RunOutput>\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n | Record<string, any>,\n config?: StructuredOutputMethodOptions<true>\n ): Runnable<BaseLanguageModelInput, { raw: BaseMessage; parsed: RunOutput }>;\n\n withStructuredOutput<\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n RunOutput extends Record<string, any> = Record<string, any>,\n >(\n outputSchema:\n | InteropZodType<RunOutput>\n | SerializableSchema<RunOutput>\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n | Record<string, any>,\n config?: StructuredOutputMethodOptions<boolean>\n ):\n | Runnable<BaseLanguageModelInput, RunOutput>\n | Runnable<BaseLanguageModelInput, { raw: BaseMessage; parsed: RunOutput }>;\n\n withStructuredOutput<\n RunOutput extends Record<string, unknown> = Record<string, unknown>,\n >(\n outputSchema:\n | InteropZodType<RunOutput>\n | SerializableSchema<RunOutput>\n | Record<string, unknown>,\n config?: StructuredOutputMethodOptions<boolean>\n ) {\n let llm: Runnable<BaseLanguageModelInput>;\n let outputParser: Runnable<AIMessageChunk, RunOutput>;\n\n const { schema, name, includeRaw } = {\n ...config,\n schema: outputSchema,\n };\n\n const method = resolveOpenRouterStructuredOutputMethod({\n model: this.model,\n method: config?.method,\n profile: this.profile,\n models: this.models,\n route: this.route,\n });\n\n const asJsonSchema = toJsonSchema(schema);\n\n if (method === \"jsonSchema\") {\n const schemaName = name ?? \"extract\";\n llm = this.withConfig({\n response_format: {\n type: \"json_schema\",\n json_schema: {\n name: schemaName,\n description: getSchemaDescription(schema),\n schema: asJsonSchema,\n strict: config?.strict,\n },\n },\n ls_structured_output_format: {\n kwargs: { method: \"json_schema\" },\n schema: {\n title: schemaName,\n description: getSchemaDescription(schema),\n ...asJsonSchema,\n },\n },\n } as Partial<ChatOpenRouterCallOptions>);\n\n outputParser = createContentParser(schema);\n } else if (method === \"jsonMode\") {\n llm = this.withConfig({\n response_format: { type: \"json_object\" },\n ls_structured_output_format: {\n kwargs: { method: \"json_mode\" },\n schema: { title: name ?? \"extract\", ...asJsonSchema },\n },\n } as Partial<ChatOpenRouterCallOptions>);\n outputParser = createContentParser(schema);\n } else {\n let functionName = name ?? \"extract\";\n if (\n !isInteropZodSchema(schema) &&\n !isSerializableSchema(schema) &&\n \"name\" in (schema as Record<string, unknown>)\n ) {\n functionName = (schema as Record<string, unknown>).name as string;\n }\n\n llm = this.withConfig({\n tools: [\n {\n type: \"function\" as const,\n function: {\n name: functionName,\n description: getSchemaDescription(schema) ?? \"\",\n parameters: asJsonSchema,\n },\n },\n ],\n tool_choice: {\n type: \"function\" as const,\n function: { name: functionName },\n },\n ls_structured_output_format: {\n kwargs: { method: \"function_calling\" },\n schema: { title: functionName, ...asJsonSchema },\n },\n ...(config?.strict !== undefined ? { strict: config.strict } : {}),\n } as Partial<ChatOpenRouterCallOptions>);\n\n outputParser = createFunctionCallingParser(schema, functionName);\n }\n\n return assembleStructuredOutputPipeline(\n llm,\n outputParser,\n includeRaw,\n \"ChatOpenRouterStructuredOutput\"\n );\n }\n}\n"],"mappings":"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"}
@@ -20,12 +20,23 @@ function convertMessagesToOpenRouterParams(messages, model) {
20
20
  * Delegates to the OpenAI completions converter for tool call parsing,
21
21
  * multi-modal output handling, and audio support, then patches
22
22
  * response_metadata to reflect the OpenRouter provider.
23
+ *
24
+ * Reasoning (chain-of-thought) output is preserved on
25
+ * `additional_kwargs.reasoning_content` (flat string, DeepSeek convention)
26
+ * and `additional_kwargs.reasoning_details` (structured array, OpenRouter
27
+ * native shape). Together with the `model_provider: "openrouter"` stamp,
28
+ * this causes `AIMessage.contentBlocks` to emit standard
29
+ * `{type: "reasoning"}` blocks via `ChatOpenRouterTranslator` from
30
+ * `@langchain/core`.
23
31
  */
24
32
  function convertOpenRouterResponseToBaseMessage(choice, rawResponse) {
25
33
  const message = (0, _langchain_openai.convertCompletionsMessageToBaseMessage)({
26
34
  message: choice.message,
27
35
  rawResponse
28
36
  });
37
+ const assistantMessage = choice.message;
38
+ if (typeof assistantMessage.reasoning === "string" && assistantMessage.reasoning.length > 0) message.additional_kwargs.reasoning_content = assistantMessage.reasoning;
39
+ if (Array.isArray(assistantMessage.reasoning_details) && assistantMessage.reasoning_details.length > 0) message.additional_kwargs.reasoning_details = assistantMessage.reasoning_details;
29
40
  message.response_metadata = {
30
41
  ...message.response_metadata,
31
42
  model: rawResponse.model,
@@ -41,6 +52,13 @@ function convertOpenRouterResponseToBaseMessage(choice, rawResponse) {
41
52
  * Delegates to the OpenAI completions converter for tool call chunk
42
53
  * parsing, audio handling, and role-specific message types, then
43
54
  * patches response_metadata to reflect the OpenRouter provider.
55
+ *
56
+ * Reasoning delta text (`delta.reasoning`) and structured reasoning details
57
+ * (`delta.reasoning_details`) are copied onto `additional_kwargs` so they
58
+ * concatenate across chunks under the standard merge rules — string fields
59
+ * are concatenated by `_mergeDicts` and arrays are merged by index-aware
60
+ * `_mergeLists`. See `ChatOpenRouterTranslator` in `@langchain/core` for how
61
+ * the accumulated fields become v1 `{type:"reasoning"}` content blocks.
44
62
  */
45
63
  function convertOpenRouterDeltaToBaseMessageChunk(delta, rawChunk, defaultRole) {
46
64
  const chunk = (0, _langchain_openai.convertCompletionsDeltaToBaseMessageChunk)({
@@ -48,6 +66,8 @@ function convertOpenRouterDeltaToBaseMessageChunk(delta, rawChunk, defaultRole)
48
66
  rawResponse: rawChunk,
49
67
  defaultRole: defaultRole ?? "assistant"
50
68
  });
69
+ if (typeof delta.reasoning === "string" && delta.reasoning.length > 0) chunk.additional_kwargs.reasoning_content = delta.reasoning;
70
+ if (Array.isArray(delta.reasoning_details) && delta.reasoning_details.length > 0) chunk.additional_kwargs.reasoning_details = delta.reasoning_details;
51
71
  chunk.response_metadata = {
52
72
  ...chunk.response_metadata,
53
73
  model_provider: "openrouter"
@@ -1 +1 @@
1
- {"version":3,"file":"messages.cjs","names":[],"sources":["../../src/converters/messages.ts"],"sourcesContent":["import {\n BaseMessage,\n BaseMessageChunk,\n UsageMetadata,\n} from \"@langchain/core/messages\";\nimport {\n convertMessagesToCompletionsMessageParams,\n convertCompletionsMessageToBaseMessage,\n convertCompletionsDeltaToBaseMessageChunk,\n} from \"@langchain/openai\";\nimport type { OpenAI as OpenAIClient } from \"openai\";\nimport type { OpenRouter } from \"../api-types.js\";\n\n/**\n * The inner data shape of a streaming SSE chunk. Each parsed SSE event\n * contains this object directly (without the `data` wrapper that the\n * OpenAPI spec describes on `ChatStreamingResponseChunk`).\n */\nexport type StreamingChunkData = OpenRouter.ChatStreamingResponseChunk[\"data\"];\n\n// LangChain → OpenRouter\n/**\n * Convert an array of LangChain messages to the OpenRouter request format.\n *\n * Delegates to the OpenAI completions converter since OpenRouter's chat\n * API is wire-compatible with OpenAI's. This gives us full support for\n * standard content blocks, reasoning-model developer role mapping,\n * multi-modal inputs, and all edge cases handled upstream.\n */\nexport function convertMessagesToOpenRouterParams(\n messages: BaseMessage[],\n model?: string\n): OpenAIClient.Chat.Completions.ChatCompletionMessageParam[] {\n return convertMessagesToCompletionsMessageParams({\n messages,\n model,\n });\n}\n\n// OpenRouter → LangChain (non-streaming)\n/**\n * Convert a non-streaming OpenRouter response choice into a BaseMessage.\n *\n * Delegates to the OpenAI completions converter for tool call parsing,\n * multi-modal output handling, and audio support, then patches\n * response_metadata to reflect the OpenRouter provider.\n */\nexport function convertOpenRouterResponseToBaseMessage(\n choice: OpenRouter.ChatResponseChoice,\n rawResponse: OpenRouter.ChatResponse\n): BaseMessage {\n const message = convertCompletionsMessageToBaseMessage({\n message:\n choice.message as unknown as OpenAIClient.Chat.Completions.ChatCompletionMessage,\n rawResponse:\n rawResponse as unknown as OpenAIClient.Chat.Completions.ChatCompletion,\n });\n\n message.response_metadata = {\n ...message.response_metadata,\n model: rawResponse.model,\n model_provider: \"openrouter\",\n model_name: rawResponse.model,\n finish_reason: choice.finish_reason,\n };\n\n return message;\n}\n\n// OpenRouter → LangChain (streaming)\n/**\n * Convert a streaming delta into a BaseMessageChunk.\n *\n * Delegates to the OpenAI completions converter for tool call chunk\n * parsing, audio handling, and role-specific message types, then\n * patches response_metadata to reflect the OpenRouter provider.\n */\nexport function convertOpenRouterDeltaToBaseMessageChunk(\n delta: OpenRouter.ChatStreamingMessageChunk,\n rawChunk: StreamingChunkData,\n defaultRole?: string\n): BaseMessageChunk {\n const chunk = convertCompletionsDeltaToBaseMessageChunk({\n delta: delta as Record<string, unknown>,\n rawResponse:\n rawChunk as unknown as OpenAIClient.Chat.Completions.ChatCompletionChunk,\n defaultRole: (defaultRole ??\n \"assistant\") as OpenAIClient.Chat.ChatCompletionRole,\n });\n\n chunk.response_metadata = {\n ...chunk.response_metadata,\n model_provider: \"openrouter\",\n };\n\n return chunk;\n}\n\n// Usage metadata\n\n/**\n * Convert OpenRouter usage info to LangChain's `UsageMetadata`,\n * including prompt/completion token detail breakdowns when available.\n */\nexport function convertUsageMetadata(\n usage?: OpenRouter.ChatGenerationTokenUsage\n): UsageMetadata | undefined {\n if (!usage) return undefined;\n\n const result: UsageMetadata = {\n input_tokens: usage.prompt_tokens,\n output_tokens: usage.completion_tokens,\n total_tokens: usage.total_tokens,\n };\n\n const promptDetails = usage.prompt_tokens_details;\n if (promptDetails) {\n const input_token_details: Record<string, number> = {};\n if (promptDetails.cached_tokens != null)\n input_token_details.cache_read = promptDetails.cached_tokens;\n if (promptDetails.audio_tokens != null)\n input_token_details.audio = promptDetails.audio_tokens;\n if (Object.keys(input_token_details).length > 0)\n result.input_token_details = input_token_details;\n }\n\n const completionDetails = usage.completion_tokens_details;\n if (completionDetails) {\n const output_token_details: Record<string, number> = {};\n if (completionDetails.reasoning_tokens != null)\n output_token_details.reasoning = completionDetails.reasoning_tokens;\n if (Object.keys(output_token_details).length > 0)\n result.output_token_details = output_token_details;\n }\n\n return result;\n}\n"],"mappings":";;;;;;;;;;AA6BA,SAAgB,kCACd,UACA,OAC4D;AAC5D,SAAA,GAAA,kBAAA,2CAAiD;EAC/C;EACA;EACD,CAAC;;;;;;;;;AAWJ,SAAgB,uCACd,QACA,aACa;CACb,MAAM,WAAA,GAAA,kBAAA,wCAAiD;EACrD,SACE,OAAO;EAEP;EACH,CAAC;AAEF,SAAQ,oBAAoB;EAC1B,GAAG,QAAQ;EACX,OAAO,YAAY;EACnB,gBAAgB;EAChB,YAAY,YAAY;EACxB,eAAe,OAAO;EACvB;AAED,QAAO;;;;;;;;;AAWT,SAAgB,yCACd,OACA,UACA,aACkB;CAClB,MAAM,SAAA,GAAA,kBAAA,2CAAkD;EAC/C;EACP,aACE;EACF,aAAc,eACZ;EACH,CAAC;AAEF,OAAM,oBAAoB;EACxB,GAAG,MAAM;EACT,gBAAgB;EACjB;AAED,QAAO;;;;;;AAST,SAAgB,qBACd,OAC2B;AAC3B,KAAI,CAAC,MAAO,QAAO,KAAA;CAEnB,MAAM,SAAwB;EAC5B,cAAc,MAAM;EACpB,eAAe,MAAM;EACrB,cAAc,MAAM;EACrB;CAED,MAAM,gBAAgB,MAAM;AAC5B,KAAI,eAAe;EACjB,MAAM,sBAA8C,EAAE;AACtD,MAAI,cAAc,iBAAiB,KACjC,qBAAoB,aAAa,cAAc;AACjD,MAAI,cAAc,gBAAgB,KAChC,qBAAoB,QAAQ,cAAc;AAC5C,MAAI,OAAO,KAAK,oBAAoB,CAAC,SAAS,EAC5C,QAAO,sBAAsB;;CAGjC,MAAM,oBAAoB,MAAM;AAChC,KAAI,mBAAmB;EACrB,MAAM,uBAA+C,EAAE;AACvD,MAAI,kBAAkB,oBAAoB,KACxC,sBAAqB,YAAY,kBAAkB;AACrD,MAAI,OAAO,KAAK,qBAAqB,CAAC,SAAS,EAC7C,QAAO,uBAAuB;;AAGlC,QAAO"}
1
+ {"version":3,"file":"messages.cjs","names":[],"sources":["../../src/converters/messages.ts"],"sourcesContent":["import {\n BaseMessage,\n BaseMessageChunk,\n UsageMetadata,\n} from \"@langchain/core/messages\";\nimport {\n convertMessagesToCompletionsMessageParams,\n convertCompletionsMessageToBaseMessage,\n convertCompletionsDeltaToBaseMessageChunk,\n} from \"@langchain/openai\";\nimport type { OpenAI as OpenAIClient } from \"openai\";\nimport type { OpenRouter } from \"../api-types.js\";\n\n/**\n * The inner data shape of a streaming SSE chunk. Each parsed SSE event\n * contains this object directly (without the `data` wrapper that the\n * OpenAPI spec describes on `ChatStreamingResponseChunk`).\n */\nexport type StreamingChunkData = OpenRouter.ChatStreamingResponseChunk[\"data\"];\n\n// LangChain → OpenRouter\n/**\n * Convert an array of LangChain messages to the OpenRouter request format.\n *\n * Delegates to the OpenAI completions converter since OpenRouter's chat\n * API is wire-compatible with OpenAI's. This gives us full support for\n * standard content blocks, reasoning-model developer role mapping,\n * multi-modal inputs, and all edge cases handled upstream.\n */\nexport function convertMessagesToOpenRouterParams(\n messages: BaseMessage[],\n model?: string\n): OpenAIClient.Chat.Completions.ChatCompletionMessageParam[] {\n return convertMessagesToCompletionsMessageParams({\n messages,\n model,\n });\n}\n\n// OpenRouter → LangChain (non-streaming)\n/**\n * Convert a non-streaming OpenRouter response choice into a BaseMessage.\n *\n * Delegates to the OpenAI completions converter for tool call parsing,\n * multi-modal output handling, and audio support, then patches\n * response_metadata to reflect the OpenRouter provider.\n *\n * Reasoning (chain-of-thought) output is preserved on\n * `additional_kwargs.reasoning_content` (flat string, DeepSeek convention)\n * and `additional_kwargs.reasoning_details` (structured array, OpenRouter\n * native shape). Together with the `model_provider: \"openrouter\"` stamp,\n * this causes `AIMessage.contentBlocks` to emit standard\n * `{type: \"reasoning\"}` blocks via `ChatOpenRouterTranslator` from\n * `@langchain/core`.\n */\nexport function convertOpenRouterResponseToBaseMessage(\n choice: OpenRouter.ChatResponseChoice,\n rawResponse: OpenRouter.ChatResponse\n): BaseMessage {\n const message = convertCompletionsMessageToBaseMessage({\n message:\n choice.message as unknown as OpenAIClient.Chat.Completions.ChatCompletionMessage,\n rawResponse:\n rawResponse as unknown as OpenAIClient.Chat.Completions.ChatCompletion,\n });\n\n // Surface reasoning fields that the OpenAI completions converter doesn't\n // know about. Both are OpenRouter-specific extensions to the Chat\n // Completions response (see `OpenRouter.AssistantMessage` in api-types.ts).\n const assistantMessage = choice.message;\n if (\n typeof assistantMessage.reasoning === \"string\" &&\n assistantMessage.reasoning.length > 0\n ) {\n message.additional_kwargs.reasoning_content = assistantMessage.reasoning;\n }\n if (\n Array.isArray(assistantMessage.reasoning_details) &&\n assistantMessage.reasoning_details.length > 0\n ) {\n message.additional_kwargs.reasoning_details =\n assistantMessage.reasoning_details;\n }\n\n message.response_metadata = {\n ...message.response_metadata,\n model: rawResponse.model,\n model_provider: \"openrouter\",\n model_name: rawResponse.model,\n finish_reason: choice.finish_reason,\n };\n\n return message;\n}\n\n// OpenRouter → LangChain (streaming)\n/**\n * Convert a streaming delta into a BaseMessageChunk.\n *\n * Delegates to the OpenAI completions converter for tool call chunk\n * parsing, audio handling, and role-specific message types, then\n * patches response_metadata to reflect the OpenRouter provider.\n *\n * Reasoning delta text (`delta.reasoning`) and structured reasoning details\n * (`delta.reasoning_details`) are copied onto `additional_kwargs` so they\n * concatenate across chunks under the standard merge rules — string fields\n * are concatenated by `_mergeDicts` and arrays are merged by index-aware\n * `_mergeLists`. See `ChatOpenRouterTranslator` in `@langchain/core` for how\n * the accumulated fields become v1 `{type:\"reasoning\"}` content blocks.\n */\nexport function convertOpenRouterDeltaToBaseMessageChunk(\n delta: OpenRouter.ChatStreamingMessageChunk,\n rawChunk: StreamingChunkData,\n defaultRole?: string\n): BaseMessageChunk {\n const chunk = convertCompletionsDeltaToBaseMessageChunk({\n delta: delta as Record<string, unknown>,\n rawResponse:\n rawChunk as unknown as OpenAIClient.Chat.Completions.ChatCompletionChunk,\n defaultRole: (defaultRole ??\n \"assistant\") as OpenAIClient.Chat.ChatCompletionRole,\n });\n\n if (typeof delta.reasoning === \"string\" && delta.reasoning.length > 0) {\n chunk.additional_kwargs.reasoning_content = delta.reasoning;\n }\n if (\n Array.isArray(delta.reasoning_details) &&\n delta.reasoning_details.length > 0\n ) {\n chunk.additional_kwargs.reasoning_details = delta.reasoning_details;\n }\n\n chunk.response_metadata = {\n ...chunk.response_metadata,\n model_provider: \"openrouter\",\n };\n\n return chunk;\n}\n\n// Usage metadata\n\n/**\n * Convert OpenRouter usage info to LangChain's `UsageMetadata`,\n * including prompt/completion token detail breakdowns when available.\n */\nexport function convertUsageMetadata(\n usage?: OpenRouter.ChatGenerationTokenUsage\n): UsageMetadata | undefined {\n if (!usage) return undefined;\n\n const result: UsageMetadata = {\n input_tokens: usage.prompt_tokens,\n output_tokens: usage.completion_tokens,\n total_tokens: usage.total_tokens,\n };\n\n const promptDetails = usage.prompt_tokens_details;\n if (promptDetails) {\n const input_token_details: Record<string, number> = {};\n if (promptDetails.cached_tokens != null)\n input_token_details.cache_read = promptDetails.cached_tokens;\n if (promptDetails.audio_tokens != null)\n input_token_details.audio = promptDetails.audio_tokens;\n if (Object.keys(input_token_details).length > 0)\n result.input_token_details = input_token_details;\n }\n\n const completionDetails = usage.completion_tokens_details;\n if (completionDetails) {\n const output_token_details: Record<string, number> = {};\n if (completionDetails.reasoning_tokens != null)\n output_token_details.reasoning = completionDetails.reasoning_tokens;\n if (Object.keys(output_token_details).length > 0)\n result.output_token_details = output_token_details;\n }\n\n return result;\n}\n"],"mappings":";;;;;;;;;;AA6BA,SAAgB,kCACd,UACA,OAC4D;AAC5D,SAAA,GAAA,kBAAA,2CAAiD;EAC/C;EACA;EACD,CAAC;;;;;;;;;;;;;;;;;AAmBJ,SAAgB,uCACd,QACA,aACa;CACb,MAAM,WAAA,GAAA,kBAAA,wCAAiD;EACrD,SACE,OAAO;EAEP;EACH,CAAC;CAKF,MAAM,mBAAmB,OAAO;AAChC,KACE,OAAO,iBAAiB,cAAc,YACtC,iBAAiB,UAAU,SAAS,EAEpC,SAAQ,kBAAkB,oBAAoB,iBAAiB;AAEjE,KACE,MAAM,QAAQ,iBAAiB,kBAAkB,IACjD,iBAAiB,kBAAkB,SAAS,EAE5C,SAAQ,kBAAkB,oBACxB,iBAAiB;AAGrB,SAAQ,oBAAoB;EAC1B,GAAG,QAAQ;EACX,OAAO,YAAY;EACnB,gBAAgB;EAChB,YAAY,YAAY;EACxB,eAAe,OAAO;EACvB;AAED,QAAO;;;;;;;;;;;;;;;;AAkBT,SAAgB,yCACd,OACA,UACA,aACkB;CAClB,MAAM,SAAA,GAAA,kBAAA,2CAAkD;EAC/C;EACP,aACE;EACF,aAAc,eACZ;EACH,CAAC;AAEF,KAAI,OAAO,MAAM,cAAc,YAAY,MAAM,UAAU,SAAS,EAClE,OAAM,kBAAkB,oBAAoB,MAAM;AAEpD,KACE,MAAM,QAAQ,MAAM,kBAAkB,IACtC,MAAM,kBAAkB,SAAS,EAEjC,OAAM,kBAAkB,oBAAoB,MAAM;AAGpD,OAAM,oBAAoB;EACxB,GAAG,MAAM;EACT,gBAAgB;EACjB;AAED,QAAO;;;;;;AAST,SAAgB,qBACd,OAC2B;AAC3B,KAAI,CAAC,MAAO,QAAO,KAAA;CAEnB,MAAM,SAAwB;EAC5B,cAAc,MAAM;EACpB,eAAe,MAAM;EACrB,cAAc,MAAM;EACrB;CAED,MAAM,gBAAgB,MAAM;AAC5B,KAAI,eAAe;EACjB,MAAM,sBAA8C,EAAE;AACtD,MAAI,cAAc,iBAAiB,KACjC,qBAAoB,aAAa,cAAc;AACjD,MAAI,cAAc,gBAAgB,KAChC,qBAAoB,QAAQ,cAAc;AAC5C,MAAI,OAAO,KAAK,oBAAoB,CAAC,SAAS,EAC5C,QAAO,sBAAsB;;CAGjC,MAAM,oBAAoB,MAAM;AAChC,KAAI,mBAAmB;EACrB,MAAM,uBAA+C,EAAE;AACvD,MAAI,kBAAkB,oBAAoB,KACxC,sBAAqB,YAAY,kBAAkB;AACrD,MAAI,OAAO,KAAK,qBAAqB,CAAC,SAAS,EAC7C,QAAO,uBAAuB;;AAGlC,QAAO"}
@@ -20,12 +20,23 @@ function convertMessagesToOpenRouterParams(messages, model) {
20
20
  * Delegates to the OpenAI completions converter for tool call parsing,
21
21
  * multi-modal output handling, and audio support, then patches
22
22
  * response_metadata to reflect the OpenRouter provider.
23
+ *
24
+ * Reasoning (chain-of-thought) output is preserved on
25
+ * `additional_kwargs.reasoning_content` (flat string, DeepSeek convention)
26
+ * and `additional_kwargs.reasoning_details` (structured array, OpenRouter
27
+ * native shape). Together with the `model_provider: "openrouter"` stamp,
28
+ * this causes `AIMessage.contentBlocks` to emit standard
29
+ * `{type: "reasoning"}` blocks via `ChatOpenRouterTranslator` from
30
+ * `@langchain/core`.
23
31
  */
24
32
  function convertOpenRouterResponseToBaseMessage(choice, rawResponse) {
25
33
  const message = convertCompletionsMessageToBaseMessage({
26
34
  message: choice.message,
27
35
  rawResponse
28
36
  });
37
+ const assistantMessage = choice.message;
38
+ if (typeof assistantMessage.reasoning === "string" && assistantMessage.reasoning.length > 0) message.additional_kwargs.reasoning_content = assistantMessage.reasoning;
39
+ if (Array.isArray(assistantMessage.reasoning_details) && assistantMessage.reasoning_details.length > 0) message.additional_kwargs.reasoning_details = assistantMessage.reasoning_details;
29
40
  message.response_metadata = {
30
41
  ...message.response_metadata,
31
42
  model: rawResponse.model,
@@ -41,6 +52,13 @@ function convertOpenRouterResponseToBaseMessage(choice, rawResponse) {
41
52
  * Delegates to the OpenAI completions converter for tool call chunk
42
53
  * parsing, audio handling, and role-specific message types, then
43
54
  * patches response_metadata to reflect the OpenRouter provider.
55
+ *
56
+ * Reasoning delta text (`delta.reasoning`) and structured reasoning details
57
+ * (`delta.reasoning_details`) are copied onto `additional_kwargs` so they
58
+ * concatenate across chunks under the standard merge rules — string fields
59
+ * are concatenated by `_mergeDicts` and arrays are merged by index-aware
60
+ * `_mergeLists`. See `ChatOpenRouterTranslator` in `@langchain/core` for how
61
+ * the accumulated fields become v1 `{type:"reasoning"}` content blocks.
44
62
  */
45
63
  function convertOpenRouterDeltaToBaseMessageChunk(delta, rawChunk, defaultRole) {
46
64
  const chunk = convertCompletionsDeltaToBaseMessageChunk({
@@ -48,6 +66,8 @@ function convertOpenRouterDeltaToBaseMessageChunk(delta, rawChunk, defaultRole)
48
66
  rawResponse: rawChunk,
49
67
  defaultRole: defaultRole ?? "assistant"
50
68
  });
69
+ if (typeof delta.reasoning === "string" && delta.reasoning.length > 0) chunk.additional_kwargs.reasoning_content = delta.reasoning;
70
+ if (Array.isArray(delta.reasoning_details) && delta.reasoning_details.length > 0) chunk.additional_kwargs.reasoning_details = delta.reasoning_details;
51
71
  chunk.response_metadata = {
52
72
  ...chunk.response_metadata,
53
73
  model_provider: "openrouter"
@@ -1 +1 @@
1
- {"version":3,"file":"messages.js","names":[],"sources":["../../src/converters/messages.ts"],"sourcesContent":["import {\n BaseMessage,\n BaseMessageChunk,\n UsageMetadata,\n} from \"@langchain/core/messages\";\nimport {\n convertMessagesToCompletionsMessageParams,\n convertCompletionsMessageToBaseMessage,\n convertCompletionsDeltaToBaseMessageChunk,\n} from \"@langchain/openai\";\nimport type { OpenAI as OpenAIClient } from \"openai\";\nimport type { OpenRouter } from \"../api-types.js\";\n\n/**\n * The inner data shape of a streaming SSE chunk. Each parsed SSE event\n * contains this object directly (without the `data` wrapper that the\n * OpenAPI spec describes on `ChatStreamingResponseChunk`).\n */\nexport type StreamingChunkData = OpenRouter.ChatStreamingResponseChunk[\"data\"];\n\n// LangChain → OpenRouter\n/**\n * Convert an array of LangChain messages to the OpenRouter request format.\n *\n * Delegates to the OpenAI completions converter since OpenRouter's chat\n * API is wire-compatible with OpenAI's. This gives us full support for\n * standard content blocks, reasoning-model developer role mapping,\n * multi-modal inputs, and all edge cases handled upstream.\n */\nexport function convertMessagesToOpenRouterParams(\n messages: BaseMessage[],\n model?: string\n): OpenAIClient.Chat.Completions.ChatCompletionMessageParam[] {\n return convertMessagesToCompletionsMessageParams({\n messages,\n model,\n });\n}\n\n// OpenRouter → LangChain (non-streaming)\n/**\n * Convert a non-streaming OpenRouter response choice into a BaseMessage.\n *\n * Delegates to the OpenAI completions converter for tool call parsing,\n * multi-modal output handling, and audio support, then patches\n * response_metadata to reflect the OpenRouter provider.\n */\nexport function convertOpenRouterResponseToBaseMessage(\n choice: OpenRouter.ChatResponseChoice,\n rawResponse: OpenRouter.ChatResponse\n): BaseMessage {\n const message = convertCompletionsMessageToBaseMessage({\n message:\n choice.message as unknown as OpenAIClient.Chat.Completions.ChatCompletionMessage,\n rawResponse:\n rawResponse as unknown as OpenAIClient.Chat.Completions.ChatCompletion,\n });\n\n message.response_metadata = {\n ...message.response_metadata,\n model: rawResponse.model,\n model_provider: \"openrouter\",\n model_name: rawResponse.model,\n finish_reason: choice.finish_reason,\n };\n\n return message;\n}\n\n// OpenRouter → LangChain (streaming)\n/**\n * Convert a streaming delta into a BaseMessageChunk.\n *\n * Delegates to the OpenAI completions converter for tool call chunk\n * parsing, audio handling, and role-specific message types, then\n * patches response_metadata to reflect the OpenRouter provider.\n */\nexport function convertOpenRouterDeltaToBaseMessageChunk(\n delta: OpenRouter.ChatStreamingMessageChunk,\n rawChunk: StreamingChunkData,\n defaultRole?: string\n): BaseMessageChunk {\n const chunk = convertCompletionsDeltaToBaseMessageChunk({\n delta: delta as Record<string, unknown>,\n rawResponse:\n rawChunk as unknown as OpenAIClient.Chat.Completions.ChatCompletionChunk,\n defaultRole: (defaultRole ??\n \"assistant\") as OpenAIClient.Chat.ChatCompletionRole,\n });\n\n chunk.response_metadata = {\n ...chunk.response_metadata,\n model_provider: \"openrouter\",\n };\n\n return chunk;\n}\n\n// Usage metadata\n\n/**\n * Convert OpenRouter usage info to LangChain's `UsageMetadata`,\n * including prompt/completion token detail breakdowns when available.\n */\nexport function convertUsageMetadata(\n usage?: OpenRouter.ChatGenerationTokenUsage\n): UsageMetadata | undefined {\n if (!usage) return undefined;\n\n const result: UsageMetadata = {\n input_tokens: usage.prompt_tokens,\n output_tokens: usage.completion_tokens,\n total_tokens: usage.total_tokens,\n };\n\n const promptDetails = usage.prompt_tokens_details;\n if (promptDetails) {\n const input_token_details: Record<string, number> = {};\n if (promptDetails.cached_tokens != null)\n input_token_details.cache_read = promptDetails.cached_tokens;\n if (promptDetails.audio_tokens != null)\n input_token_details.audio = promptDetails.audio_tokens;\n if (Object.keys(input_token_details).length > 0)\n result.input_token_details = input_token_details;\n }\n\n const completionDetails = usage.completion_tokens_details;\n if (completionDetails) {\n const output_token_details: Record<string, number> = {};\n if (completionDetails.reasoning_tokens != null)\n output_token_details.reasoning = completionDetails.reasoning_tokens;\n if (Object.keys(output_token_details).length > 0)\n result.output_token_details = output_token_details;\n }\n\n return result;\n}\n"],"mappings":";;;;;;;;;;AA6BA,SAAgB,kCACd,UACA,OAC4D;AAC5D,QAAO,0CAA0C;EAC/C;EACA;EACD,CAAC;;;;;;;;;AAWJ,SAAgB,uCACd,QACA,aACa;CACb,MAAM,UAAU,uCAAuC;EACrD,SACE,OAAO;EAEP;EACH,CAAC;AAEF,SAAQ,oBAAoB;EAC1B,GAAG,QAAQ;EACX,OAAO,YAAY;EACnB,gBAAgB;EAChB,YAAY,YAAY;EACxB,eAAe,OAAO;EACvB;AAED,QAAO;;;;;;;;;AAWT,SAAgB,yCACd,OACA,UACA,aACkB;CAClB,MAAM,QAAQ,0CAA0C;EAC/C;EACP,aACE;EACF,aAAc,eACZ;EACH,CAAC;AAEF,OAAM,oBAAoB;EACxB,GAAG,MAAM;EACT,gBAAgB;EACjB;AAED,QAAO;;;;;;AAST,SAAgB,qBACd,OAC2B;AAC3B,KAAI,CAAC,MAAO,QAAO,KAAA;CAEnB,MAAM,SAAwB;EAC5B,cAAc,MAAM;EACpB,eAAe,MAAM;EACrB,cAAc,MAAM;EACrB;CAED,MAAM,gBAAgB,MAAM;AAC5B,KAAI,eAAe;EACjB,MAAM,sBAA8C,EAAE;AACtD,MAAI,cAAc,iBAAiB,KACjC,qBAAoB,aAAa,cAAc;AACjD,MAAI,cAAc,gBAAgB,KAChC,qBAAoB,QAAQ,cAAc;AAC5C,MAAI,OAAO,KAAK,oBAAoB,CAAC,SAAS,EAC5C,QAAO,sBAAsB;;CAGjC,MAAM,oBAAoB,MAAM;AAChC,KAAI,mBAAmB;EACrB,MAAM,uBAA+C,EAAE;AACvD,MAAI,kBAAkB,oBAAoB,KACxC,sBAAqB,YAAY,kBAAkB;AACrD,MAAI,OAAO,KAAK,qBAAqB,CAAC,SAAS,EAC7C,QAAO,uBAAuB;;AAGlC,QAAO"}
1
+ {"version":3,"file":"messages.js","names":[],"sources":["../../src/converters/messages.ts"],"sourcesContent":["import {\n BaseMessage,\n BaseMessageChunk,\n UsageMetadata,\n} from \"@langchain/core/messages\";\nimport {\n convertMessagesToCompletionsMessageParams,\n convertCompletionsMessageToBaseMessage,\n convertCompletionsDeltaToBaseMessageChunk,\n} from \"@langchain/openai\";\nimport type { OpenAI as OpenAIClient } from \"openai\";\nimport type { OpenRouter } from \"../api-types.js\";\n\n/**\n * The inner data shape of a streaming SSE chunk. Each parsed SSE event\n * contains this object directly (without the `data` wrapper that the\n * OpenAPI spec describes on `ChatStreamingResponseChunk`).\n */\nexport type StreamingChunkData = OpenRouter.ChatStreamingResponseChunk[\"data\"];\n\n// LangChain → OpenRouter\n/**\n * Convert an array of LangChain messages to the OpenRouter request format.\n *\n * Delegates to the OpenAI completions converter since OpenRouter's chat\n * API is wire-compatible with OpenAI's. This gives us full support for\n * standard content blocks, reasoning-model developer role mapping,\n * multi-modal inputs, and all edge cases handled upstream.\n */\nexport function convertMessagesToOpenRouterParams(\n messages: BaseMessage[],\n model?: string\n): OpenAIClient.Chat.Completions.ChatCompletionMessageParam[] {\n return convertMessagesToCompletionsMessageParams({\n messages,\n model,\n });\n}\n\n// OpenRouter → LangChain (non-streaming)\n/**\n * Convert a non-streaming OpenRouter response choice into a BaseMessage.\n *\n * Delegates to the OpenAI completions converter for tool call parsing,\n * multi-modal output handling, and audio support, then patches\n * response_metadata to reflect the OpenRouter provider.\n *\n * Reasoning (chain-of-thought) output is preserved on\n * `additional_kwargs.reasoning_content` (flat string, DeepSeek convention)\n * and `additional_kwargs.reasoning_details` (structured array, OpenRouter\n * native shape). Together with the `model_provider: \"openrouter\"` stamp,\n * this causes `AIMessage.contentBlocks` to emit standard\n * `{type: \"reasoning\"}` blocks via `ChatOpenRouterTranslator` from\n * `@langchain/core`.\n */\nexport function convertOpenRouterResponseToBaseMessage(\n choice: OpenRouter.ChatResponseChoice,\n rawResponse: OpenRouter.ChatResponse\n): BaseMessage {\n const message = convertCompletionsMessageToBaseMessage({\n message:\n choice.message as unknown as OpenAIClient.Chat.Completions.ChatCompletionMessage,\n rawResponse:\n rawResponse as unknown as OpenAIClient.Chat.Completions.ChatCompletion,\n });\n\n // Surface reasoning fields that the OpenAI completions converter doesn't\n // know about. Both are OpenRouter-specific extensions to the Chat\n // Completions response (see `OpenRouter.AssistantMessage` in api-types.ts).\n const assistantMessage = choice.message;\n if (\n typeof assistantMessage.reasoning === \"string\" &&\n assistantMessage.reasoning.length > 0\n ) {\n message.additional_kwargs.reasoning_content = assistantMessage.reasoning;\n }\n if (\n Array.isArray(assistantMessage.reasoning_details) &&\n assistantMessage.reasoning_details.length > 0\n ) {\n message.additional_kwargs.reasoning_details =\n assistantMessage.reasoning_details;\n }\n\n message.response_metadata = {\n ...message.response_metadata,\n model: rawResponse.model,\n model_provider: \"openrouter\",\n model_name: rawResponse.model,\n finish_reason: choice.finish_reason,\n };\n\n return message;\n}\n\n// OpenRouter → LangChain (streaming)\n/**\n * Convert a streaming delta into a BaseMessageChunk.\n *\n * Delegates to the OpenAI completions converter for tool call chunk\n * parsing, audio handling, and role-specific message types, then\n * patches response_metadata to reflect the OpenRouter provider.\n *\n * Reasoning delta text (`delta.reasoning`) and structured reasoning details\n * (`delta.reasoning_details`) are copied onto `additional_kwargs` so they\n * concatenate across chunks under the standard merge rules — string fields\n * are concatenated by `_mergeDicts` and arrays are merged by index-aware\n * `_mergeLists`. See `ChatOpenRouterTranslator` in `@langchain/core` for how\n * the accumulated fields become v1 `{type:\"reasoning\"}` content blocks.\n */\nexport function convertOpenRouterDeltaToBaseMessageChunk(\n delta: OpenRouter.ChatStreamingMessageChunk,\n rawChunk: StreamingChunkData,\n defaultRole?: string\n): BaseMessageChunk {\n const chunk = convertCompletionsDeltaToBaseMessageChunk({\n delta: delta as Record<string, unknown>,\n rawResponse:\n rawChunk as unknown as OpenAIClient.Chat.Completions.ChatCompletionChunk,\n defaultRole: (defaultRole ??\n \"assistant\") as OpenAIClient.Chat.ChatCompletionRole,\n });\n\n if (typeof delta.reasoning === \"string\" && delta.reasoning.length > 0) {\n chunk.additional_kwargs.reasoning_content = delta.reasoning;\n }\n if (\n Array.isArray(delta.reasoning_details) &&\n delta.reasoning_details.length > 0\n ) {\n chunk.additional_kwargs.reasoning_details = delta.reasoning_details;\n }\n\n chunk.response_metadata = {\n ...chunk.response_metadata,\n model_provider: \"openrouter\",\n };\n\n return chunk;\n}\n\n// Usage metadata\n\n/**\n * Convert OpenRouter usage info to LangChain's `UsageMetadata`,\n * including prompt/completion token detail breakdowns when available.\n */\nexport function convertUsageMetadata(\n usage?: OpenRouter.ChatGenerationTokenUsage\n): UsageMetadata | undefined {\n if (!usage) return undefined;\n\n const result: UsageMetadata = {\n input_tokens: usage.prompt_tokens,\n output_tokens: usage.completion_tokens,\n total_tokens: usage.total_tokens,\n };\n\n const promptDetails = usage.prompt_tokens_details;\n if (promptDetails) {\n const input_token_details: Record<string, number> = {};\n if (promptDetails.cached_tokens != null)\n input_token_details.cache_read = promptDetails.cached_tokens;\n if (promptDetails.audio_tokens != null)\n input_token_details.audio = promptDetails.audio_tokens;\n if (Object.keys(input_token_details).length > 0)\n result.input_token_details = input_token_details;\n }\n\n const completionDetails = usage.completion_tokens_details;\n if (completionDetails) {\n const output_token_details: Record<string, number> = {};\n if (completionDetails.reasoning_tokens != null)\n output_token_details.reasoning = completionDetails.reasoning_tokens;\n if (Object.keys(output_token_details).length > 0)\n result.output_token_details = output_token_details;\n }\n\n return result;\n}\n"],"mappings":";;;;;;;;;;AA6BA,SAAgB,kCACd,UACA,OAC4D;AAC5D,QAAO,0CAA0C;EAC/C;EACA;EACD,CAAC;;;;;;;;;;;;;;;;;AAmBJ,SAAgB,uCACd,QACA,aACa;CACb,MAAM,UAAU,uCAAuC;EACrD,SACE,OAAO;EAEP;EACH,CAAC;CAKF,MAAM,mBAAmB,OAAO;AAChC,KACE,OAAO,iBAAiB,cAAc,YACtC,iBAAiB,UAAU,SAAS,EAEpC,SAAQ,kBAAkB,oBAAoB,iBAAiB;AAEjE,KACE,MAAM,QAAQ,iBAAiB,kBAAkB,IACjD,iBAAiB,kBAAkB,SAAS,EAE5C,SAAQ,kBAAkB,oBACxB,iBAAiB;AAGrB,SAAQ,oBAAoB;EAC1B,GAAG,QAAQ;EACX,OAAO,YAAY;EACnB,gBAAgB;EAChB,YAAY,YAAY;EACxB,eAAe,OAAO;EACvB;AAED,QAAO;;;;;;;;;;;;;;;;AAkBT,SAAgB,yCACd,OACA,UACA,aACkB;CAClB,MAAM,QAAQ,0CAA0C;EAC/C;EACP,aACE;EACF,aAAc,eACZ;EACH,CAAC;AAEF,KAAI,OAAO,MAAM,cAAc,YAAY,MAAM,UAAU,SAAS,EAClE,OAAM,kBAAkB,oBAAoB,MAAM;AAEpD,KACE,MAAM,QAAQ,MAAM,kBAAkB,IACtC,MAAM,kBAAkB,SAAS,EAEjC,OAAM,kBAAkB,oBAAoB,MAAM;AAGpD,OAAM,oBAAoB;EACxB,GAAG,MAAM;EACT,gBAAgB;EACjB;AAED,QAAO;;;;;;AAST,SAAgB,qBACd,OAC2B;AAC3B,KAAI,CAAC,MAAO,QAAO,KAAA;CAEnB,MAAM,SAAwB;EAC5B,cAAc,MAAM;EACpB,eAAe,MAAM;EACrB,cAAc,MAAM;EACrB;CAED,MAAM,gBAAgB,MAAM;AAC5B,KAAI,eAAe;EACjB,MAAM,sBAA8C,EAAE;AACtD,MAAI,cAAc,iBAAiB,KACjC,qBAAoB,aAAa,cAAc;AACjD,MAAI,cAAc,gBAAgB,KAChC,qBAAoB,QAAQ,cAAc;AAC5C,MAAI,OAAO,KAAK,oBAAoB,CAAC,SAAS,EAC5C,QAAO,sBAAsB;;CAGjC,MAAM,oBAAoB,MAAM;AAChC,KAAI,mBAAmB;EACrB,MAAM,uBAA+C,EAAE;AACvD,MAAI,kBAAkB,oBAAoB,KACxC,sBAAqB,YAAY,kBAAkB;AACrD,MAAI,OAAO,KAAK,qBAAqB,CAAC,SAAS,EAC7C,QAAO,uBAAuB;;AAGlC,QAAO"}
@@ -1 +1 @@
1
- {"version":3,"file":"tools.cjs","names":[],"sources":["../../src/converters/tools.ts"],"sourcesContent":["import { convertToOpenAITool } from \"@langchain/core/utils/function_calling\";\nimport type { BindToolsInput } from \"@langchain/core/language_models/chat_models\";\nimport type { ToolDefinition } from \"@langchain/core/language_models/base\";\nimport type { OpenRouter } from \"../api-types.js\";\n\n/**\n * Convert LangChain tool inputs to the OpenRouter (OpenAI-compatible) format.\n */\nexport function convertToolsToOpenRouter(\n tools: BindToolsInput[],\n options?: { strict?: boolean }\n): OpenRouter.ToolDefinitionJson[] {\n return tools.map((tool) => {\n const converted: ToolDefinition = convertToOpenAITool(tool, {\n strict: options?.strict,\n });\n return converted as unknown as OpenRouter.ToolDefinitionJson;\n });\n}\n\n/**\n * Convert a LangChain `ToolChoice` value to the OpenRouter wire format.\n */\nexport function formatToolChoice(\n toolChoice?: string | Record<string, unknown>\n): OpenRouter.ToolChoiceOption | undefined {\n if (toolChoice === undefined || toolChoice === null) return undefined;\n if (toolChoice === \"auto\") return \"auto\";\n if (toolChoice === \"none\") return \"none\";\n if (toolChoice === \"any\" || toolChoice === \"required\") return \"required\";\n if (typeof toolChoice === \"string\") {\n return { type: \"function\", function: { name: toolChoice } };\n }\n return toolChoice as unknown as OpenRouter.ToolChoiceOption;\n}\n"],"mappings":";;;;;AAQA,SAAgB,yBACd,OACA,SACiC;AACjC,QAAO,MAAM,KAAK,SAAS;AAIzB,UAAA,GAAA,uCAAA,qBAHsD,MAAM,EAC1D,QAAQ,SAAS,QAClB,CAAC;GAEF;;;;;AAMJ,SAAgB,iBACd,YACyC;AACzC,KAAI,eAAe,KAAA,KAAa,eAAe,KAAM,QAAO,KAAA;AAC5D,KAAI,eAAe,OAAQ,QAAO;AAClC,KAAI,eAAe,OAAQ,QAAO;AAClC,KAAI,eAAe,SAAS,eAAe,WAAY,QAAO;AAC9D,KAAI,OAAO,eAAe,SACxB,QAAO;EAAE,MAAM;EAAY,UAAU,EAAE,MAAM,YAAY;EAAE;AAE7D,QAAO"}
1
+ {"version":3,"file":"tools.cjs","names":[],"sources":["../../src/converters/tools.ts"],"sourcesContent":["import { convertToOpenAITool } from \"@langchain/core/utils/function_calling\";\nimport type { BindToolsInput } from \"@langchain/core/language_models/chat_models\";\nimport type { ToolDefinition } from \"@langchain/core/language_models/base\";\nimport type { OpenRouter } from \"../api-types.js\";\n\n/**\n * Convert LangChain tool inputs to the OpenRouter (OpenAI-compatible) format.\n */\nexport function convertToolsToOpenRouter(\n tools: BindToolsInput[],\n options?: { strict?: boolean }\n): OpenRouter.ToolDefinitionJson[] {\n return tools.map((tool) => {\n const converted: ToolDefinition = convertToOpenAITool(tool, {\n strict: options?.strict,\n });\n return converted as unknown as OpenRouter.ToolDefinitionJson;\n });\n}\n\n/**\n * Convert a LangChain `ToolChoice` value to the OpenRouter wire format.\n */\nexport function formatToolChoice(\n toolChoice?: string | Record<string, unknown>\n): OpenRouter.ToolChoiceOption | undefined {\n if (toolChoice === undefined || toolChoice === null) return undefined;\n if (toolChoice === \"auto\") return \"auto\";\n if (toolChoice === \"none\") return \"none\";\n if (toolChoice === \"any\" || toolChoice === \"required\") return \"required\";\n if (typeof toolChoice === \"string\") {\n return { type: \"function\", function: { name: toolChoice } };\n }\n return toolChoice as unknown as OpenRouter.ToolChoiceOption;\n}\n"],"mappings":";;;;;AAQA,SAAgB,yBACd,OACA,SACiC;AACjC,QAAO,MAAM,KAAK,SAAS;AAIzB,UAAA,GAAA,uCAAA,qBAHsD,MAAM,EAC1D,QAAQ,SAAS,QAClB,CACe;GAChB;;;;;AAMJ,SAAgB,iBACd,YACyC;AACzC,KAAI,eAAe,KAAA,KAAa,eAAe,KAAM,QAAO,KAAA;AAC5D,KAAI,eAAe,OAAQ,QAAO;AAClC,KAAI,eAAe,OAAQ,QAAO;AAClC,KAAI,eAAe,SAAS,eAAe,WAAY,QAAO;AAC9D,KAAI,OAAO,eAAe,SACxB,QAAO;EAAE,MAAM;EAAY,UAAU,EAAE,MAAM,YAAY;EAAE;AAE7D,QAAO"}
@@ -1 +1 @@
1
- {"version":3,"file":"tools.js","names":[],"sources":["../../src/converters/tools.ts"],"sourcesContent":["import { convertToOpenAITool } from \"@langchain/core/utils/function_calling\";\nimport type { BindToolsInput } from \"@langchain/core/language_models/chat_models\";\nimport type { ToolDefinition } from \"@langchain/core/language_models/base\";\nimport type { OpenRouter } from \"../api-types.js\";\n\n/**\n * Convert LangChain tool inputs to the OpenRouter (OpenAI-compatible) format.\n */\nexport function convertToolsToOpenRouter(\n tools: BindToolsInput[],\n options?: { strict?: boolean }\n): OpenRouter.ToolDefinitionJson[] {\n return tools.map((tool) => {\n const converted: ToolDefinition = convertToOpenAITool(tool, {\n strict: options?.strict,\n });\n return converted as unknown as OpenRouter.ToolDefinitionJson;\n });\n}\n\n/**\n * Convert a LangChain `ToolChoice` value to the OpenRouter wire format.\n */\nexport function formatToolChoice(\n toolChoice?: string | Record<string, unknown>\n): OpenRouter.ToolChoiceOption | undefined {\n if (toolChoice === undefined || toolChoice === null) return undefined;\n if (toolChoice === \"auto\") return \"auto\";\n if (toolChoice === \"none\") return \"none\";\n if (toolChoice === \"any\" || toolChoice === \"required\") return \"required\";\n if (typeof toolChoice === \"string\") {\n return { type: \"function\", function: { name: toolChoice } };\n }\n return toolChoice as unknown as OpenRouter.ToolChoiceOption;\n}\n"],"mappings":";;;;;AAQA,SAAgB,yBACd,OACA,SACiC;AACjC,QAAO,MAAM,KAAK,SAAS;AAIzB,SAHkC,oBAAoB,MAAM,EAC1D,QAAQ,SAAS,QAClB,CAAC;GAEF;;;;;AAMJ,SAAgB,iBACd,YACyC;AACzC,KAAI,eAAe,KAAA,KAAa,eAAe,KAAM,QAAO,KAAA;AAC5D,KAAI,eAAe,OAAQ,QAAO;AAClC,KAAI,eAAe,OAAQ,QAAO;AAClC,KAAI,eAAe,SAAS,eAAe,WAAY,QAAO;AAC9D,KAAI,OAAO,eAAe,SACxB,QAAO;EAAE,MAAM;EAAY,UAAU,EAAE,MAAM,YAAY;EAAE;AAE7D,QAAO"}
1
+ {"version":3,"file":"tools.js","names":[],"sources":["../../src/converters/tools.ts"],"sourcesContent":["import { convertToOpenAITool } from \"@langchain/core/utils/function_calling\";\nimport type { BindToolsInput } from \"@langchain/core/language_models/chat_models\";\nimport type { ToolDefinition } from \"@langchain/core/language_models/base\";\nimport type { OpenRouter } from \"../api-types.js\";\n\n/**\n * Convert LangChain tool inputs to the OpenRouter (OpenAI-compatible) format.\n */\nexport function convertToolsToOpenRouter(\n tools: BindToolsInput[],\n options?: { strict?: boolean }\n): OpenRouter.ToolDefinitionJson[] {\n return tools.map((tool) => {\n const converted: ToolDefinition = convertToOpenAITool(tool, {\n strict: options?.strict,\n });\n return converted as unknown as OpenRouter.ToolDefinitionJson;\n });\n}\n\n/**\n * Convert a LangChain `ToolChoice` value to the OpenRouter wire format.\n */\nexport function formatToolChoice(\n toolChoice?: string | Record<string, unknown>\n): OpenRouter.ToolChoiceOption | undefined {\n if (toolChoice === undefined || toolChoice === null) return undefined;\n if (toolChoice === \"auto\") return \"auto\";\n if (toolChoice === \"none\") return \"none\";\n if (toolChoice === \"any\" || toolChoice === \"required\") return \"required\";\n if (typeof toolChoice === \"string\") {\n return { type: \"function\", function: { name: toolChoice } };\n }\n return toolChoice as unknown as OpenRouter.ToolChoiceOption;\n}\n"],"mappings":";;;;;AAQA,SAAgB,yBACd,OACA,SACiC;AACjC,QAAO,MAAM,KAAK,SAAS;AAIzB,SAHkC,oBAAoB,MAAM,EAC1D,QAAQ,SAAS,QAClB,CACe;GAChB;;;;;AAMJ,SAAgB,iBACd,YACyC;AACzC,KAAI,eAAe,KAAA,KAAa,eAAe,KAAM,QAAO,KAAA;AAC5D,KAAI,eAAe,OAAQ,QAAO;AAClC,KAAI,eAAe,OAAQ,QAAO;AAClC,KAAI,eAAe,SAAS,eAAe,WAAY,QAAO;AAC9D,KAAI,OAAO,eAAe,SACxB,QAAO;EAAE,MAAM;EAAY,UAAU,EAAE,MAAM,YAAY;EAAE;AAE7D,QAAO"}
package/dist/index.cjs CHANGED
@@ -2,8 +2,13 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  const require_errors = require("./utils/errors.cjs");
3
3
  const require_profiles = require("./profiles.cjs");
4
4
  const require_index = require("./chat_models/index.cjs");
5
+ //#region src/index.ts
6
+ const OPENROUTER_MODEL_PROFILES = require_profiles.default;
7
+ //#endregion
5
8
  exports.ChatOpenRouter = require_index.ChatOpenRouter;
6
- exports.OPENROUTER_MODEL_PROFILES = require_profiles;
9
+ exports.OPENROUTER_MODEL_PROFILES = OPENROUTER_MODEL_PROFILES;
7
10
  exports.OpenRouterAuthError = require_errors.OpenRouterAuthError;
8
11
  exports.OpenRouterError = require_errors.OpenRouterError;
9
12
  exports.OpenRouterRateLimitError = require_errors.OpenRouterRateLimitError;
13
+
14
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs","names":["_OPENROUTER_MODEL_PROFILES"],"sources":["../src/index.ts"],"sourcesContent":["export { ChatOpenRouter } from \"./chat_models/index.js\";\nexport type {\n ChatOpenRouterParams as ChatOpenRouterInput,\n ChatOpenRouterCallOptions,\n ChatOpenRouterFields,\n OpenRouterResponseFormat,\n OpenRouterPlugin,\n} from \"./chat_models/types.js\";\nexport type { OpenRouter } from \"./api-types.js\";\nexport {\n OpenRouterError,\n OpenRouterAuthError,\n OpenRouterRateLimitError,\n} from \"./utils/errors.js\";\nimport _OPENROUTER_MODEL_PROFILES from \"./profiles.js\";\n\nexport const OPENROUTER_MODEL_PROFILES = _OPENROUTER_MODEL_PROFILES;\n"],"mappings":";;;;;AAgBA,MAAa,4BAA4BA,iBAAAA"}
package/dist/index.d.cts CHANGED
@@ -2,5 +2,10 @@ import { OpenRouter } from "./api-types.cjs";
2
2
  import { ChatOpenRouterCallOptions, ChatOpenRouterFields, ChatOpenRouterParams, OpenRouterPlugin, OpenRouterResponseFormat } from "./chat_models/types.cjs";
3
3
  import { ChatOpenRouter } from "./chat_models/index.cjs";
4
4
  import { OpenRouterAuthError, OpenRouterError, OpenRouterRateLimitError } from "./utils/errors.cjs";
5
- import { PROFILES } from "./profiles.cjs";
6
- export { ChatOpenRouter, type ChatOpenRouterCallOptions, type ChatOpenRouterFields, type ChatOpenRouterParams as ChatOpenRouterInput, PROFILES as OPENROUTER_MODEL_PROFILES, type OpenRouter, OpenRouterAuthError, OpenRouterError, type OpenRouterPlugin, OpenRouterRateLimitError, type OpenRouterResponseFormat };
5
+ import * as _$_langchain_core_language_models_profile0 from "@langchain/core/language_models/profile";
6
+
7
+ //#region src/index.d.ts
8
+ declare const OPENROUTER_MODEL_PROFILES: Record<string, _$_langchain_core_language_models_profile0.ModelProfile>;
9
+ //#endregion
10
+ export { ChatOpenRouter, type ChatOpenRouterCallOptions, type ChatOpenRouterFields, type ChatOpenRouterParams as ChatOpenRouterInput, OPENROUTER_MODEL_PROFILES, type OpenRouter, OpenRouterAuthError, OpenRouterError, type OpenRouterPlugin, OpenRouterRateLimitError, type OpenRouterResponseFormat };
11
+ //# sourceMappingURL=index.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.cts","names":[],"sources":["../src/index.ts"],"mappings":";;;;;;;cAgBa,yBAAA,EAAyB,MAAA,SAA6B,0CAAA,CAA7B,YAAA"}
package/dist/index.d.ts CHANGED
@@ -2,5 +2,10 @@ import { OpenRouter } from "./api-types.js";
2
2
  import { ChatOpenRouterCallOptions, ChatOpenRouterFields, ChatOpenRouterParams, OpenRouterPlugin, OpenRouterResponseFormat } from "./chat_models/types.js";
3
3
  import { ChatOpenRouter } from "./chat_models/index.js";
4
4
  import { OpenRouterAuthError, OpenRouterError, OpenRouterRateLimitError } from "./utils/errors.js";
5
- import { PROFILES } from "./profiles.js";
6
- export { ChatOpenRouter, type ChatOpenRouterCallOptions, type ChatOpenRouterFields, type ChatOpenRouterParams as ChatOpenRouterInput, PROFILES as OPENROUTER_MODEL_PROFILES, type OpenRouter, OpenRouterAuthError, OpenRouterError, type OpenRouterPlugin, OpenRouterRateLimitError, type OpenRouterResponseFormat };
5
+ import * as _$_langchain_core_language_models_profile0 from "@langchain/core/language_models/profile";
6
+
7
+ //#region src/index.d.ts
8
+ declare const OPENROUTER_MODEL_PROFILES: Record<string, _$_langchain_core_language_models_profile0.ModelProfile>;
9
+ //#endregion
10
+ export { ChatOpenRouter, type ChatOpenRouterCallOptions, type ChatOpenRouterFields, type ChatOpenRouterParams as ChatOpenRouterInput, OPENROUTER_MODEL_PROFILES, type OpenRouter, OpenRouterAuthError, OpenRouterError, type OpenRouterPlugin, OpenRouterRateLimitError, type OpenRouterResponseFormat };
11
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../src/index.ts"],"mappings":";;;;;;;cAgBa,yBAAA,EAAyB,MAAA,SAA6B,0CAAA,CAA7B,YAAA"}
package/dist/index.js CHANGED
@@ -1,4 +1,9 @@
1
1
  import { OpenRouterAuthError, OpenRouterError, OpenRouterRateLimitError } from "./utils/errors.js";
2
2
  import PROFILES from "./profiles.js";
3
3
  import { ChatOpenRouter } from "./chat_models/index.js";
4
- export { ChatOpenRouter, PROFILES as OPENROUTER_MODEL_PROFILES, OpenRouterAuthError, OpenRouterError, OpenRouterRateLimitError };
4
+ //#region src/index.ts
5
+ const OPENROUTER_MODEL_PROFILES = PROFILES;
6
+ //#endregion
7
+ export { ChatOpenRouter, OPENROUTER_MODEL_PROFILES, OpenRouterAuthError, OpenRouterError, OpenRouterRateLimitError };
8
+
9
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":["_OPENROUTER_MODEL_PROFILES"],"sources":["../src/index.ts"],"sourcesContent":["export { ChatOpenRouter } from \"./chat_models/index.js\";\nexport type {\n ChatOpenRouterParams as ChatOpenRouterInput,\n ChatOpenRouterCallOptions,\n ChatOpenRouterFields,\n OpenRouterResponseFormat,\n OpenRouterPlugin,\n} from \"./chat_models/types.js\";\nexport type { OpenRouter } from \"./api-types.js\";\nexport {\n OpenRouterError,\n OpenRouterAuthError,\n OpenRouterRateLimitError,\n} from \"./utils/errors.js\";\nimport _OPENROUTER_MODEL_PROFILES from \"./profiles.js\";\n\nexport const OPENROUTER_MODEL_PROFILES = _OPENROUTER_MODEL_PROFILES;\n"],"mappings":";;;;AAgBA,MAAa,4BAA4BA"}
@@ -0,0 +1,29 @@
1
+ let _langchain_core_language_models_openai_completions_stream = require("@langchain/core/language_models/openai_completions_stream");
2
+ //#region src/utils/stream_events.ts
3
+ function mapOpenRouterChunkToOpenAI(data) {
4
+ const choice = data.choices?.[0];
5
+ if (choice?.delta && typeof choice.delta.reasoning === "string" && choice.delta.reasoning_content == null) return {
6
+ ...data,
7
+ choices: [{
8
+ ...choice,
9
+ delta: {
10
+ ...choice.delta,
11
+ reasoning_content: choice.delta.reasoning
12
+ }
13
+ }]
14
+ };
15
+ return data;
16
+ }
17
+ async function* convertOpenRouterStream(source, options = {}) {
18
+ async function* mapped() {
19
+ for await (const chunk of source) yield mapOpenRouterChunkToOpenAI(chunk);
20
+ }
21
+ yield* (0, _langchain_core_language_models_openai_completions_stream.convertOpenAICompletionsStream)(mapped(), {
22
+ ...options,
23
+ provider: "openrouter"
24
+ });
25
+ }
26
+ //#endregion
27
+ exports.convertOpenRouterStream = convertOpenRouterStream;
28
+
29
+ //# sourceMappingURL=stream_events.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stream_events.cjs","names":[],"sources":["../../src/utils/stream_events.ts"],"sourcesContent":["/**\n * Converts OpenRouter SSE stream chunks into LangChain ChatModelStreamEvents.\n *\n * @module\n */\n\nimport type { ChatModelStreamEvent } from \"@langchain/core/language_models/event\";\nimport {\n convertOpenAICompletionsStream,\n type OpenAICompletionsStreamChunk,\n} from \"@langchain/core/language_models/openai_completions_stream\";\nimport type { StreamingChunkData } from \"../converters/messages.js\";\n\nexport interface ConvertOpenRouterStreamOptions {\n streamUsage?: boolean;\n}\n\nfunction mapOpenRouterChunkToOpenAI(\n data: StreamingChunkData\n): OpenAICompletionsStreamChunk {\n const choice = data.choices?.[0];\n if (\n choice?.delta &&\n typeof choice.delta.reasoning === \"string\" &&\n choice.delta.reasoning_content == null\n ) {\n return {\n ...(data as unknown as OpenAICompletionsStreamChunk),\n choices: [\n {\n ...choice,\n delta: {\n ...choice.delta,\n reasoning_content: choice.delta.reasoning,\n },\n },\n ],\n };\n }\n return data as unknown as OpenAICompletionsStreamChunk;\n}\n\nexport async function* convertOpenRouterStream(\n source: AsyncIterable<StreamingChunkData>,\n options: ConvertOpenRouterStreamOptions = {}\n): AsyncGenerator<ChatModelStreamEvent> {\n async function* mapped() {\n for await (const chunk of source) {\n yield mapOpenRouterChunkToOpenAI(chunk);\n }\n }\n yield* convertOpenAICompletionsStream(mapped(), {\n ...options,\n provider: \"openrouter\",\n });\n}\n"],"mappings":";;AAiBA,SAAS,2BACP,MAC8B;CAC9B,MAAM,SAAS,KAAK,UAAU;AAC9B,KACE,QAAQ,SACR,OAAO,OAAO,MAAM,cAAc,YAClC,OAAO,MAAM,qBAAqB,KAElC,QAAO;EACL,GAAI;EACJ,SAAS,CACP;GACE,GAAG;GACH,OAAO;IACL,GAAG,OAAO;IACV,mBAAmB,OAAO,MAAM;IACjC;GACF,CACF;EACF;AAEH,QAAO;;AAGT,gBAAuB,wBACrB,QACA,UAA0C,EAAE,EACN;CACtC,gBAAgB,SAAS;AACvB,aAAW,MAAM,SAAS,OACxB,OAAM,2BAA2B,MAAM;;AAG3C,SAAA,GAAA,0DAAA,gCAAsC,QAAQ,EAAE;EAC9C,GAAG;EACH,UAAU;EACX,CAAC"}
@@ -0,0 +1,29 @@
1
+ import { convertOpenAICompletionsStream } from "@langchain/core/language_models/openai_completions_stream";
2
+ //#region src/utils/stream_events.ts
3
+ function mapOpenRouterChunkToOpenAI(data) {
4
+ const choice = data.choices?.[0];
5
+ if (choice?.delta && typeof choice.delta.reasoning === "string" && choice.delta.reasoning_content == null) return {
6
+ ...data,
7
+ choices: [{
8
+ ...choice,
9
+ delta: {
10
+ ...choice.delta,
11
+ reasoning_content: choice.delta.reasoning
12
+ }
13
+ }]
14
+ };
15
+ return data;
16
+ }
17
+ async function* convertOpenRouterStream(source, options = {}) {
18
+ async function* mapped() {
19
+ for await (const chunk of source) yield mapOpenRouterChunkToOpenAI(chunk);
20
+ }
21
+ yield* convertOpenAICompletionsStream(mapped(), {
22
+ ...options,
23
+ provider: "openrouter"
24
+ });
25
+ }
26
+ //#endregion
27
+ export { convertOpenRouterStream };
28
+
29
+ //# sourceMappingURL=stream_events.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stream_events.js","names":[],"sources":["../../src/utils/stream_events.ts"],"sourcesContent":["/**\n * Converts OpenRouter SSE stream chunks into LangChain ChatModelStreamEvents.\n *\n * @module\n */\n\nimport type { ChatModelStreamEvent } from \"@langchain/core/language_models/event\";\nimport {\n convertOpenAICompletionsStream,\n type OpenAICompletionsStreamChunk,\n} from \"@langchain/core/language_models/openai_completions_stream\";\nimport type { StreamingChunkData } from \"../converters/messages.js\";\n\nexport interface ConvertOpenRouterStreamOptions {\n streamUsage?: boolean;\n}\n\nfunction mapOpenRouterChunkToOpenAI(\n data: StreamingChunkData\n): OpenAICompletionsStreamChunk {\n const choice = data.choices?.[0];\n if (\n choice?.delta &&\n typeof choice.delta.reasoning === \"string\" &&\n choice.delta.reasoning_content == null\n ) {\n return {\n ...(data as unknown as OpenAICompletionsStreamChunk),\n choices: [\n {\n ...choice,\n delta: {\n ...choice.delta,\n reasoning_content: choice.delta.reasoning,\n },\n },\n ],\n };\n }\n return data as unknown as OpenAICompletionsStreamChunk;\n}\n\nexport async function* convertOpenRouterStream(\n source: AsyncIterable<StreamingChunkData>,\n options: ConvertOpenRouterStreamOptions = {}\n): AsyncGenerator<ChatModelStreamEvent> {\n async function* mapped() {\n for await (const chunk of source) {\n yield mapOpenRouterChunkToOpenAI(chunk);\n }\n }\n yield* convertOpenAICompletionsStream(mapped(), {\n ...options,\n provider: \"openrouter\",\n });\n}\n"],"mappings":";;AAiBA,SAAS,2BACP,MAC8B;CAC9B,MAAM,SAAS,KAAK,UAAU;AAC9B,KACE,QAAQ,SACR,OAAO,OAAO,MAAM,cAAc,YAClC,OAAO,MAAM,qBAAqB,KAElC,QAAO;EACL,GAAI;EACJ,SAAS,CACP;GACE,GAAG;GACH,OAAO;IACL,GAAG,OAAO;IACV,mBAAmB,OAAO,MAAM;IACjC;GACF,CACF;EACF;AAEH,QAAO;;AAGT,gBAAuB,wBACrB,QACA,UAA0C,EAAE,EACN;CACtC,gBAAgB,SAAS;AACvB,aAAW,MAAM,SAAS,OACxB,OAAM,2BAA2B,MAAM;;AAG3C,QAAO,+BAA+B,QAAQ,EAAE;EAC9C,GAAG;EACH,UAAU;EACX,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@langchain/openrouter",
3
- "version": "0.2.5",
3
+ "version": "0.4.0",
4
4
  "description": "OpenRouter integration for LangChain.js",
5
5
  "author": "LangChain",
6
6
  "license": "MIT",
@@ -14,9 +14,9 @@
14
14
  "directory": "libs/providers/langchain-openrouter"
15
15
  },
16
16
  "dependencies": {
17
- "eventsource-parser": "^3.0.6",
18
- "openai": "^6.37.0",
19
- "@langchain/openai": "1.4.6"
17
+ "eventsource-parser": "^3.1.0",
18
+ "openai": "^6.41.0",
19
+ "@langchain/openai": "1.5.0"
20
20
  },
21
21
  "peerDependencies": {
22
22
  "@langchain/core": "^1.0.0"
@@ -25,12 +25,12 @@
25
25
  "@tsconfig/recommended": "^1.0.10",
26
26
  "dpdm": "^3.14.0",
27
27
  "sparktype": "^0.1.3",
28
- "typescript": "~5.8.3",
29
- "vitest": "^4.1.2",
28
+ "typescript": "~6.0.3",
29
+ "vitest": "^4.1.8",
30
30
  "zod": "^3.25.76 || ^4",
31
31
  "zod-to-json-schema": "^3.25.2",
32
- "@langchain/openai": "^1.4.6",
33
- "@langchain/core": "^1.1.47",
32
+ "@langchain/core": "^1.2.0",
33
+ "@langchain/openai": "^1.5.0",
34
34
  "@langchain/standard-tests": "0.0.23"
35
35
  },
36
36
  "publishConfig": {
@@ -1,7 +0,0 @@
1
- import { ModelProfile } from "@langchain/core/language_models/profile";
2
-
3
- //#region src/profiles.d.ts
4
- declare const PROFILES: Record<string, ModelProfile>;
5
- //#endregion
6
- export { PROFILES };
7
- //# sourceMappingURL=profiles.d.cts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"profiles.d.cts","names":[],"sources":["../src/profiles.ts"],"mappings":";;;cAIM,QAAA,EAAU,MAAA,SAAe,YAAA"}
@@ -1,7 +0,0 @@
1
- import { ModelProfile } from "@langchain/core/language_models/profile";
2
-
3
- //#region src/profiles.d.ts
4
- declare const PROFILES: Record<string, ModelProfile>;
5
- //#endregion
6
- export { PROFILES };
7
- //# sourceMappingURL=profiles.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"profiles.d.ts","names":[],"sources":["../src/profiles.ts"],"mappings":";;;cAIM,QAAA,EAAU,MAAA,SAAe,YAAA"}