@stackfactor/agent-utils 1.4.1 → 1.4.3

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/README.md CHANGED
@@ -25,7 +25,7 @@ import {
25
25
 
26
26
  ## `langChain`
27
27
 
28
- Unified interface for running LLM prompts, managing LangChain agents, and generating images across OpenAI, Anthropic, Google, DeepSeek, Kimi (Moonshot), and GLM (Zhipu) providers.
28
+ Unified interface for running LLM prompts, managing LangChain agents, and generating images across the OpenAI, Anthropic, and Google providers.
29
29
 
30
30
  ### `langChain.runPromptWithModel(modelName, config, prompt, onProgressReport?, minPercent?, maxPercent?, expectsJsonResponse?, schema?, agentName?, tools?, usageTracker?)`
31
31
 
@@ -39,8 +39,8 @@ When `expectsJsonResponse` is `true` (the default), JSON escape instructions are
39
39
 
40
40
  | Parameter | Type | Default | Description |
41
41
  | --------------------- | -------------------- | --------------- | -------------------------------------------------------------------------------------------------------- |
42
- | `modelName` | `string` | — | Model identifier: `"gpt-4o"`, `"claude-3-5-sonnet"`, `"gemini-1.5-pro"`, `"deepseek-chat"`, `"kimi-k2-0905-preview"`, `"glm-4.6"`, etc. |
43
- | `config` | `object` | — | API keys (`openAIAPIKey`, `anthropicAPIKey`, `googleAPIKey`, `deepSeekAPIKey`, `kimiAPIKey`, `glmAPIKey`), `temperature`, `agentic`, `recursionLimit`, `tavily`, `tavilyAPIKey` |
42
+ | `modelName` | `string` | — | Model identifier: `"gpt-4o"`, `"claude-3-5-sonnet"`, `"gemini-1.5-pro"`, etc. |
43
+ | `config` | `object` | — | API keys (`openAIAPIKey`, `anthropicAPIKey`, `googleAPIKey`), `temperature`, `agentic`, `recursionLimit`, `tavily`, `tavilyAPIKey` |
44
44
  | `prompt` | `string \| object[]` | — | Plain string or array of `{ role, content }` message objects |
45
45
  | `onProgressReport` | `function \| null` | `null` | Async callback receiving `{ message, progress }` updates |
46
46
  | `minPercent` | `number` | `0` | Lower bound for progress percentage |
@@ -338,7 +338,7 @@ A model that cannot tell it received half a regulation will reason over the half
338
338
 
339
339
  **Notes**
340
340
 
341
- - Works with every supported provider, including DeepSeek, Kimi and GLM, which have no native web search of their own.
341
+ - Works with every supported provider.
342
342
  - Credits come from each response's own `usage` block rather than an estimate, and are accumulated on `usageTracker.tokens.tavilyCredits`, costed from the `tavily-credit-costs` constant (USD per credit; falls back to Tavily's list price of `0.008`).
343
343
  - URLs are collected on `usageTracker.webSearchSources`, deduplicated across the whole run — Tavily's terms require citing the original sources when its content is shown to end users.
344
344
  - Per-URL failures are surfaced in the tool result instead of being dropped, so a fetch failure is not mistaken for an absence of content.
@@ -355,11 +355,10 @@ A model that cannot tell it received half a regulation will reason over the half
355
355
  | OpenAI | `gpt-` | `gpt-4o`, `gpt-4o-mini` |
356
356
  | Anthropic | `claude-` | `claude-3-5-sonnet`, `claude-3-opus` |
357
357
  | Google | `gemini-` | `gemini-1.5-pro`, `gemini-2.0-flash` |
358
- | DeepSeek | `deepseek-` | `deepseek-chat`, `deepseek-reasoner` |
359
- | Kimi (Moonshot) | `kimi-`, `moonshot-` | `kimi-k2-0905-preview`, `moonshot-v1-8k` |
360
- | GLM (Zhipu) | `glm-` | `glm-4.6`, `glm-4.5` |
361
358
 
362
- DeepSeek, Kimi, and GLM are routed through their OpenAI-compatible endpoints. Native structured output via `response_format` with JSON schema is only applied to `gpt-*` models; all other providers (including these three) use prompt-based JSON instructions.
359
+ Native structured output is configured per provider when a Zod `schema` is passed: `response_format` with a JSON schema for `gpt-*`, `output_config.format` for `claude-*`, and JSON mode plus `responseSchema` for `gemini-*`.
360
+
361
+ Output token limits are not configurable. Anthropic requires `max_tokens` on every request, so `claude-*` is pinned to `64000` with streaming always enabled; `gpt-*` and `gemini-*` are sent no limit and use each provider's own default, which is that model's maximum.
363
362
 
364
363
  ### Image Generation
365
364
 
@@ -379,11 +378,7 @@ The `config` object accepted by LangChain methods supports the following keys:
379
378
  | `openAIAPIKey` | `string` | OpenAI API key |
380
379
  | `anthropicAPIKey` | `string` | Anthropic API key |
381
380
  | `googleAPIKey` | `string` | Google AI API key |
382
- | `deepSeekAPIKey` | `string` | DeepSeek API key |
383
- | `kimiAPIKey` | `string` | Kimi (Moonshot) API key |
384
- | `glmAPIKey` | `string` | GLM (Zhipu) API key |
385
381
  | `temperature` | `number` | Sampling temperature |
386
- | `maxTokens` | `number` | Maximum output tokens (default: `16384` for Claude, `200000` for other providers). For Claude, values above `21333` automatically enable LangChain streaming to bypass the Anthropic SDK's 10-minute non-streaming guard. |
387
382
  | `agentic` | `boolean` | Enable agentic mode in `runPromptWithModel` |
388
383
  | `recursionLimit` | `number` | Max agent steps (default: `25`) |
389
384
  | `tavilyAPIKey` | `string` | Tavily API key; required when `tavily` is set |
@@ -23,23 +23,22 @@ export type UsageTracker = {
23
23
  /**
24
24
  * Instantiates and returns the appropriate LangChain chat model based on the model
25
25
  * name prefix. `claude-` maps to `ChatAnthropic`, `gemini-` maps to
26
- * `ChatGoogleGenerativeAI`, and `gpt-` maps to `ChatOpenAI`. DeepSeek (`deepseek-`),
27
- * Kimi/Moonshot (`kimi-`, `moonshot-`), and GLM/Zhipu (`glm-`) models are routed
28
- * through `ChatOpenAI` against each provider's OpenAI-compatible endpoint. When a Zod
26
+ * `ChatGoogleGenerativeAI`, and `gpt-` maps to `ChatOpenAI`. When a Zod
29
27
  * `schema` is provided, native structured output is configured per provider: OpenAI
30
28
  * via `response_format` with `json_schema`, Anthropic via `output_config.format`, and
31
29
  * Gemini via JSON mode (`json: true`) plus `responseSchema`. In every case the model
32
30
  * emits JSON as the message text, so the caller's parse/validate pipeline is unchanged.
33
- * The schema is ignored for the OpenAI-compatible providers (DeepSeek/Kimi/GLM), which
34
- * have no native structured-output support here. Throws a `BAD_REQUEST` error for
35
- * unrecognised model names.
31
+ * Throws a `BAD_REQUEST` error for unrecognised model names.
32
+ *
33
+ * Output token limits are not caller-configurable. Anthropic requires
34
+ * `max_tokens` on every request and @langchain/anthropic defaults it to 4096,
35
+ * so the Claude branch pins it explicitly; OpenAI and Gemini are sent no limit
36
+ * and fall back to each provider's own default, which is that model's maximum.
36
37
  * @param modelName - The model identifier, e.g. `"gpt-4o"`, `"claude-3-5-sonnet"`,
37
- * `"gemini-1.5-pro"`, `"deepseek-chat"`, `"kimi-k2-0905-preview"`, `"glm-4.6"`
38
+ * `"gemini-1.5-pro"`
38
39
  * @param config - Configuration object containing API keys (`openAIAPIKey`,
39
- * `anthropicAPIKey`, `googleAPIKey`, `deepSeekAPIKey`, `kimiAPIKey`, `glmAPIKey`),
40
- * optional `maxTokens`, and optional `temperature`
40
+ * `anthropicAPIKey`, `googleAPIKey`) and optional `temperature`
41
41
  * @param schema - Optional Zod schema used to configure native structured JSON output
42
- * for GPT / Claude / Gemini models; ignored for OpenAI-compatible providers
43
42
  * @returns A configured LangChain chat model (or bound runnable) instance
44
43
  */
45
44
  export declare const getLLMModel: (modelName: string, config: any, schema?: any) => any;
@@ -1 +1 @@
1
- {"version":3,"file":"langChain.d.ts","sourceRoot":"","sources":["../../src/langChain.ts"],"names":[],"mappings":"AA4GA;;;;;GAKG;AACH,MAAM,MAAM,YAAY,GAAG;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE;QAAE,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAA;KAAE,CAAC;IACvC;;;;;OAKG;IACH,gBAAgB,CAAC,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CACtD,CAAC;AAmrBF;;;;;;;;;;;;;;;;;;;;;GAqBG;AAGH,eAAO,MAAM,WAAW,GACtB,WAAW,MAAM,EACjB,QAAQ,GAAG,EACX,SAAQ,GAAU,KACjB,GAoHF,CAAC;;0CAj0B2C,GAAG,KAAG,IAAI;wBAo1B/C,MAAM,aACD,MAAM,gBACH,MAAM,SACb,GAAG,EAAE,kBACI,GAAG,UACX,GAAG,iBACG,YAAY,GAAG,IAAI,KAChC,GAAG;sBAoCG,GAAG,UACF,MAAM,UACN,GAAG,eACC,QAAQ,GAAG,IAAI,iBACb,YAAY,GAAG,IAAI,KAChC,OAAO,CAAC,GAAG,CAAC;oCAkYF,MAAM,UACT,GAAG,UACH,GAAG,oBACO,GAAG,eACT,MAAM,eACN,MAAM,wBACG,OAAO,WACpB,GAAG,cACA,MAAM,UACV,GAAG,EAAE,iBACE,YAAY,GAAG,IAAI,KAChC,OAAO,CAAC,GAAG,CAAC;sDA2tBF,MAAM,UACT,GAAG,UACH,MAAM,YACL,GAAG,iBACE,YAAY,GAAG,IAAI,KAChC,OAAO,CAAC,GAAG,CAAC;sDAmTF,MAAM,UACT,GAAG,UACH,MAAM,YACL,GAAG,iBACE,YAAY,GAAG,IAAI,KAChC,OAAO,CAAC,GAAG,CAAC;0CA7zC8B,GAAG,KAAG,MAAM;+CAh0B9C,YAAY,GAAG,IAAI,GAAG,SAAS,aAC7B,MAAM,kBACD,MAAM,UACd,GAAG,KACV,IAAI;mCAg1BU,MAAM,mBACJ,MAAM,EAAE,KACxB,MAAM;;AAg2CT,wBAUE"}
1
+ {"version":3,"file":"langChain.d.ts","sourceRoot":"","sources":["../../src/langChain.ts"],"names":[],"mappings":"AA6GA;;;;;GAKG;AACH,MAAM,MAAM,YAAY,GAAG;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE;QAAE,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAA;KAAE,CAAC;IACvC;;;;;OAKG;IACH,gBAAgB,CAAC,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CACtD,CAAC;AA0lBF;;;;;;;;;;;;;;;;;;;;GAoBG;AAGH,eAAO,MAAM,WAAW,GACtB,WAAW,MAAM,EACjB,QAAQ,GAAG,EACX,SAAQ,GAAU,KACjB,GAwHF,CAAC;;0CA3uB2C,GAAG,KAAG,IAAI;wBA8vB/C,MAAM,aACD,MAAM,gBACH,MAAM,SACb,GAAG,EAAE,kBACI,GAAG,UACX,GAAG,iBACG,YAAY,GAAG,IAAI,KAChC,GAAG;sBAoCG,GAAG,UACF,MAAM,UACN,GAAG,eACC,QAAQ,GAAG,IAAI,iBACb,YAAY,GAAG,IAAI,KAChC,OAAO,CAAC,GAAG,CAAC;oCAkYF,MAAM,UACT,GAAG,UACH,GAAG,oBACO,GAAG,eACT,MAAM,eACN,MAAM,wBACG,OAAO,WACpB,GAAG,cACA,MAAM,UACV,GAAG,EAAE,iBACE,YAAY,GAAG,IAAI,KAChC,OAAO,CAAC,GAAG,CAAC;sDA2tBF,MAAM,UACT,GAAG,UACH,MAAM,YACL,GAAG,iBACE,YAAY,GAAG,IAAI,KAChC,OAAO,CAAC,GAAG,CAAC;sDAmTF,MAAM,UACT,GAAG,UACH,MAAM,YACL,GAAG,iBACE,YAAY,GAAG,IAAI,KAChC,OAAO,CAAC,GAAG,CAAC;0CA7zC8B,GAAG,KAAG,MAAM;+CA1uB9C,YAAY,GAAG,IAAI,GAAG,SAAS,aAC7B,MAAM,kBACD,MAAM,UACd,GAAG,KACV,IAAI;mCA0vBU,MAAM,mBACJ,MAAM,EAAE,KACxB,MAAM;;AAg2CT,wBAUE"}
@@ -30,9 +30,10 @@ Do NOT include raw newlines, tabs, or unescaped quotes inside JSON string values
30
30
  * Providers for which `getLLMModel` configures native, schema-constrained JSON
31
31
  * output (OpenAI `response_format`, Anthropic `output_config.format`, Gemini
32
32
  * `responseSchema` + JSON mode). For these the model emits JSON directly, so the
33
- * prompt-injected schema and the schema-validation retry are skipped. Other
34
- * providers (DeepSeek/Kimi/GLM via the OpenAI-compatible shim) have no native
35
- * support here and continue to rely on prompt instructions + validation.
33
+ * prompt-injected schema and the schema-validation retry are skipped. Every
34
+ * supported provider is listed here; the check remains so that adding one
35
+ * without native support falls back to prompt instructions + validation
36
+ * rather than silently skipping them.
36
37
  * @param modelName - The model identifier being routed
37
38
  * @returns `true` when native structured output is wired up for the provider
38
39
  */
@@ -543,36 +544,6 @@ const extractJSONFromResponse = (text) => {
543
544
  }
544
545
  return null;
545
546
  };
546
- /**
547
- * Maps a model name prefix to its OpenAI-compatible provider configuration. DeepSeek,
548
- * Kimi (Moonshot), and GLM (Zhipu) all expose OpenAI-compatible chat completion APIs
549
- * and can be routed through `ChatOpenAI` with a custom base URL and API key.
550
- * @param modelName - The model identifier to inspect
551
- * @param config - Configuration object containing provider-specific API keys
552
- * @returns An object with `apiKey` and `baseURL` when the model matches a known
553
- * OpenAI-compatible provider, or `null` otherwise
554
- */
555
- const getOpenAICompatibleProvider = (modelName, config) => {
556
- if (modelName.startsWith("deepseek-")) {
557
- return {
558
- apiKey: config.deepSeekAPIKey,
559
- baseURL: "https://api.deepseek.com/v1",
560
- };
561
- }
562
- if (modelName.startsWith("kimi-") || modelName.startsWith("moonshot-")) {
563
- return {
564
- apiKey: config.kimiAPIKey,
565
- baseURL: "https://api.moonshot.ai/v1",
566
- };
567
- }
568
- if (modelName.startsWith("glm-")) {
569
- return {
570
- apiKey: config.glmAPIKey,
571
- baseURL: "https://open.bigmodel.cn/api/paas/v4/",
572
- };
573
- }
574
- return null;
575
- };
576
547
  /**
577
548
  * Whether a model accepts the `temperature` sampling parameter at all. Some
578
549
  * models reject it with an HTTP 400, so it must be omitted rather than clamped:
@@ -582,7 +553,7 @@ const getOpenAICompatibleProvider = (modelName, config) => {
582
553
  * accept it.
583
554
  * - OpenAI reasoning models (the GPT-5 family and the o-series) reject it;
584
555
  * gpt-4o still accepts it.
585
- * DeepSeek, Kimi/Moonshot, GLM, and Gemini all accept temperature.
556
+ * Gemini accepts temperature.
586
557
  * @param modelName - the model identifier being routed
587
558
  * @returns `true` when the model accepts a `temperature` parameter
588
559
  */
@@ -603,18 +574,14 @@ const modelSupportsTemperature = (modelName) => {
603
574
  };
604
575
  /**
605
576
  * Per-provider valid `temperature` range, used to clamp caller-supplied values.
606
- * Anthropic, Kimi (Moonshot), and GLM (Zhipu) cap at 1.0; OpenAI, Gemini, and
607
- * DeepSeek accept up to 2.0. Only consulted for models that support temperature
608
- * at all (see `modelSupportsTemperature`).
577
+ * Anthropic caps at 1.0; OpenAI and Gemini accept up to 2.0. Only consulted for
578
+ * models that support temperature at all (see `modelSupportsTemperature`).
609
579
  */
610
580
  const getTemperatureRange = (modelName) => {
611
- if (modelName.startsWith("claude-") ||
612
- modelName.startsWith("kimi-") ||
613
- modelName.startsWith("moonshot-") ||
614
- modelName.startsWith("glm-")) {
581
+ if (modelName.startsWith("claude-")) {
615
582
  return { min: 0, max: 1 };
616
583
  }
617
- // gpt-, gemini-, deepseek-
584
+ // gpt-, gemini-
618
585
  return { min: 0, max: 2 };
619
586
  };
620
587
  /**
@@ -643,46 +610,6 @@ const resolveTemperatureSetting = (modelName, config) => {
643
610
  }
644
611
  return { temperature: clamped };
645
612
  };
646
- /**
647
- * Gemini's ceiling on `maxOutputTokens`. The API validates the field and
648
- * rejects the request outright rather than clamping it:
649
- * "Unable to submit request because it has a maxOutputTokens value of N but the
650
- * supported range is from 1 (inclusive) to 65537 (exclusive)" (HTTP 400,
651
- * INVALID_ARGUMENT).
652
- *
653
- * Set to 64000 rather than that 65536 validation ceiling: the Pro models report
654
- * a generation capacity of 64000, so 64000 is the value every model across the
655
- * Gemini 3 line (3.1 Pro, 3.5/3.7/3.8 Flash) can both accept and actually
656
- * produce. One safe constant beats a per-model table that would need editing
657
- * every time Google ships a model — and the 1536 tokens of headroom given up on
658
- * Flash are far beyond what any caller asks for.
659
- *
660
- * Deliberately Gemini-only. Anthropic and OpenAI take the caller's `maxTokens`
661
- * unchanged, since neither rejects the values configured today.
662
- */
663
- const GEMINI_MAX_OUTPUT_TOKENS = 64000;
664
- /**
665
- * Resolves `maxOutputTokens` for a Gemini model. Callers share one `maxTokens`
666
- * setting across every provider, and the other providers tolerate values far
667
- * above what Gemini accepts, so anything above Gemini's ceiling is clamped here
668
- * (with a warning) instead of being sent through to a guaranteed HTTP 400. With
669
- * no caller value the ceiling itself is used, which is what the previous
670
- * 200000 default was reaching for.
671
- * @param modelName - The Gemini model identifier being routed
672
- * @param config - Configuration object; only `maxTokens` is consulted
673
- * @returns A token count within Gemini's supported range
674
- */
675
- const resolveGeminiMaxOutputTokens = (modelName, config) => {
676
- const maxTokens = config?.maxTokens;
677
- if (typeof maxTokens !== "number" || !Number.isFinite(maxTokens)) {
678
- return GEMINI_MAX_OUTPUT_TOKENS;
679
- }
680
- if (maxTokens > GEMINI_MAX_OUTPUT_TOKENS) {
681
- logger_js_1.default.log(null, logger_js_1.default.levels.warn, `maxTokens ${maxTokens} exceeds the ${GEMINI_MAX_OUTPUT_TOKENS}-token output ceiling of "${modelName}"; clamping to ${GEMINI_MAX_OUTPUT_TOKENS}.`);
682
- return GEMINI_MAX_OUTPUT_TOKENS;
683
- }
684
- return maxTokens;
685
- };
686
613
  /**
687
614
  * Flattens LangChain message content into plain text. Content is a string for
688
615
  * ordinary completions, but providers switch to an array of blocks whenever the
@@ -710,23 +637,22 @@ const extractTextContent = (content) => {
710
637
  /**
711
638
  * Instantiates and returns the appropriate LangChain chat model based on the model
712
639
  * name prefix. `claude-` maps to `ChatAnthropic`, `gemini-` maps to
713
- * `ChatGoogleGenerativeAI`, and `gpt-` maps to `ChatOpenAI`. DeepSeek (`deepseek-`),
714
- * Kimi/Moonshot (`kimi-`, `moonshot-`), and GLM/Zhipu (`glm-`) models are routed
715
- * through `ChatOpenAI` against each provider's OpenAI-compatible endpoint. When a Zod
640
+ * `ChatGoogleGenerativeAI`, and `gpt-` maps to `ChatOpenAI`. When a Zod
716
641
  * `schema` is provided, native structured output is configured per provider: OpenAI
717
642
  * via `response_format` with `json_schema`, Anthropic via `output_config.format`, and
718
643
  * Gemini via JSON mode (`json: true`) plus `responseSchema`. In every case the model
719
644
  * emits JSON as the message text, so the caller's parse/validate pipeline is unchanged.
720
- * The schema is ignored for the OpenAI-compatible providers (DeepSeek/Kimi/GLM), which
721
- * have no native structured-output support here. Throws a `BAD_REQUEST` error for
722
- * unrecognised model names.
645
+ * Throws a `BAD_REQUEST` error for unrecognised model names.
646
+ *
647
+ * Output token limits are not caller-configurable. Anthropic requires
648
+ * `max_tokens` on every request and @langchain/anthropic defaults it to 4096,
649
+ * so the Claude branch pins it explicitly; OpenAI and Gemini are sent no limit
650
+ * and fall back to each provider's own default, which is that model's maximum.
723
651
  * @param modelName - The model identifier, e.g. `"gpt-4o"`, `"claude-3-5-sonnet"`,
724
- * `"gemini-1.5-pro"`, `"deepseek-chat"`, `"kimi-k2-0905-preview"`, `"glm-4.6"`
652
+ * `"gemini-1.5-pro"`
725
653
  * @param config - Configuration object containing API keys (`openAIAPIKey`,
726
- * `anthropicAPIKey`, `googleAPIKey`, `deepSeekAPIKey`, `kimiAPIKey`, `glmAPIKey`),
727
- * optional `maxTokens`, and optional `temperature`
654
+ * `anthropicAPIKey`, `googleAPIKey`) and optional `temperature`
728
655
  * @param schema - Optional Zod schema used to configure native structured JSON output
729
- * for GPT / Claude / Gemini models; ignored for OpenAI-compatible providers
730
656
  * @returns A configured LangChain chat model (or bound runnable) instance
731
657
  */
732
658
  // Exported (but not re-exported from index.ts / the package's public entry
@@ -737,12 +663,17 @@ const getLLMModel = (modelName, config, schema = null) => {
737
663
  const modelSettings = resolveTemperatureSetting(modelName, config);
738
664
  // Claude models (Anthropic)
739
665
  if (modelName.startsWith("claude-")) {
666
+ // Anthropic requires `max_tokens` on every request and @langchain/anthropic
667
+ // defaults it to 4096 when omitted — low enough to truncate agentic turns
668
+ // mid-tool-call — so it is pinned here rather than left to the library.
669
+ // 64000 is Anthropic's recommended value for streamed requests and sits
670
+ // within the 128000-token output limit of every Claude model routed here.
671
+ //
740
672
  // Anthropic's SDK rejects non-streamed requests when max_tokens is large
741
- // enough that the operation could exceed 10 minutes (threshold: 21333).
742
- // Auto-enable LangChain streaming above that cutoff so `.invoke()` routes
743
- // through the streaming endpoint internally — callers see no interface
744
- // change, but the guard is bypassed.
745
- const maxTokens = config.maxTokens || 16384;
673
+ // enough that the operation could exceed 10 minutes (threshold: 21333), so
674
+ // streaming is always on: `.invoke()` routes through the streaming endpoint
675
+ // internally and callers see no interface change.
676
+ const maxTokens = 64000;
746
677
  // Native structured output: Anthropic's `output_config.format` constrains
747
678
  // decoding to the schema while still emitting JSON as the message text, so
748
679
  // the downstream streaming + parse/validate pipeline is unchanged.
@@ -763,7 +694,7 @@ const getLLMModel = (modelName, config, schema = null) => {
763
694
  apiKey: config.anthropicAPIKey,
764
695
  maxTokens,
765
696
  modelName: modelName,
766
- streaming: maxTokens > 16384,
697
+ streaming: true,
767
698
  invocationKwargs: { cache_control: { type: "ephemeral" } },
768
699
  ...outputConfig,
769
700
  ...modelSettings,
@@ -774,7 +705,9 @@ const getLLMModel = (modelName, config, schema = null) => {
774
705
  else if (modelName.startsWith("gemini-")) {
775
706
  const model = new google_genai_1.ChatGoogleGenerativeAI({
776
707
  apiKey: config.googleAPIKey,
777
- maxOutputTokens: resolveGeminiMaxOutputTokens(modelName, config),
708
+ // No `maxOutputTokens`: omitting it leaves Gemini's own default, which is
709
+ // the model's maximum output. Sending a value meant tracking a per-model
710
+ // ceiling that the API rejects outright rather than clamps.
778
711
  model: modelName,
779
712
  // `json: true` forces responseMimeType=application/json, guaranteeing the
780
713
  // model returns valid JSON text (never prose) for the parse pipeline.
@@ -796,17 +729,26 @@ const getLLMModel = (modelName, config, schema = null) => {
796
729
  else if (modelName.startsWith("gpt-")) {
797
730
  const openAISettings = {
798
731
  apiKey: config.openAIAPIKey,
799
- max_tokens: config.maxTokens || 200000,
732
+ // No output limit is sent: OpenAI defaults to the model's maximum. The
733
+ // snake_case `max_tokens` that used to sit here was never a recognised
734
+ // ChatOpenAI field and was silently dropped by LangChain.
800
735
  modelName: modelName,
801
736
  ...modelSettings,
802
737
  };
803
- // gpt-5.6's /v1/chat/completions endpoint 400s whenever function tools are
804
- // bound, because it rejects the model's own server-side default reasoning
805
- // effort in that combination ("use /v1/responses or set reasoning_effort
806
- // to 'none'"); forcing it to "none" here avoids the collision. Scoped to
807
- // exactly gpt-5.6 — sibling gpt-5.x models are not confirmed to share the
808
- // bug.
809
- const reasoningEffortOverride = modelName === "gpt-5.6" ? { reasoning_effort: "none" } : {};
738
+ // The gpt-5.6 family's /v1/chat/completions endpoint 400s whenever function
739
+ // tools are bound, because it rejects the model's own server-side default
740
+ // reasoning effort in that combination ("use /v1/responses or set
741
+ // reasoning_effort to 'none'"); forcing it to "none" here avoids the
742
+ // collision. The restriction belongs to the family, not to one id: the
743
+ // `gpt-5.6` alias resolves to `gpt-5.6-sol`, and `gpt-5.6-terra` /
744
+ // `gpt-5.6-luna` are separately addressable ids that hit the same 400, as
745
+ // do dated snapshots (`gpt-5.6-2026-08-22`). Matching the alias alone left
746
+ // every one of those unpatched, so match the whole family. Still excludes
747
+ // sibling releases (gpt-5.5, gpt-5.7): they are not confirmed to share the
748
+ // bug, and reasoning effort is worth keeping wherever it is accepted.
749
+ const reasoningEffortOverride = /^gpt-5\.6($|-)/.test(modelName)
750
+ ? { reasoning_effort: "none" }
751
+ : {};
810
752
  // Use native structured output with a JSON schema. `modelKwargs` is spread
811
753
  // verbatim into the Chat Completions request. Merge (not overwrite) with
812
754
  // the reasoning-effort override above, since both may apply at once.
@@ -829,17 +771,6 @@ const getLLMModel = (modelName, config, schema = null) => {
829
771
  }
830
772
  return new openai_1.ChatOpenAI(openAISettings);
831
773
  }
832
- // OpenAI-compatible providers: DeepSeek, Kimi (Moonshot), GLM (Zhipu)
833
- const openAICompatible = getOpenAICompatibleProvider(modelName, config);
834
- if (openAICompatible) {
835
- return new openai_1.ChatOpenAI({
836
- apiKey: openAICompatible.apiKey,
837
- maxTokens: config.maxTokens || 200000,
838
- modelName: modelName,
839
- configuration: { baseURL: openAICompatible.baseURL },
840
- ...modelSettings,
841
- });
842
- }
843
774
  throw errorHandling_js_1.default.create(const_js_1.default.HTTP_CODES.BAD_REQUEST, const_js_1.default.ERROR.UNSUPPORTED_MODEL + ": " + modelName);
844
775
  };
845
776
  exports.getLLMModel = getLLMModel;
@@ -23,23 +23,22 @@ export type UsageTracker = {
23
23
  /**
24
24
  * Instantiates and returns the appropriate LangChain chat model based on the model
25
25
  * name prefix. `claude-` maps to `ChatAnthropic`, `gemini-` maps to
26
- * `ChatGoogleGenerativeAI`, and `gpt-` maps to `ChatOpenAI`. DeepSeek (`deepseek-`),
27
- * Kimi/Moonshot (`kimi-`, `moonshot-`), and GLM/Zhipu (`glm-`) models are routed
28
- * through `ChatOpenAI` against each provider's OpenAI-compatible endpoint. When a Zod
26
+ * `ChatGoogleGenerativeAI`, and `gpt-` maps to `ChatOpenAI`. When a Zod
29
27
  * `schema` is provided, native structured output is configured per provider: OpenAI
30
28
  * via `response_format` with `json_schema`, Anthropic via `output_config.format`, and
31
29
  * Gemini via JSON mode (`json: true`) plus `responseSchema`. In every case the model
32
30
  * emits JSON as the message text, so the caller's parse/validate pipeline is unchanged.
33
- * The schema is ignored for the OpenAI-compatible providers (DeepSeek/Kimi/GLM), which
34
- * have no native structured-output support here. Throws a `BAD_REQUEST` error for
35
- * unrecognised model names.
31
+ * Throws a `BAD_REQUEST` error for unrecognised model names.
32
+ *
33
+ * Output token limits are not caller-configurable. Anthropic requires
34
+ * `max_tokens` on every request and @langchain/anthropic defaults it to 4096,
35
+ * so the Claude branch pins it explicitly; OpenAI and Gemini are sent no limit
36
+ * and fall back to each provider's own default, which is that model's maximum.
36
37
  * @param modelName - The model identifier, e.g. `"gpt-4o"`, `"claude-3-5-sonnet"`,
37
- * `"gemini-1.5-pro"`, `"deepseek-chat"`, `"kimi-k2-0905-preview"`, `"glm-4.6"`
38
+ * `"gemini-1.5-pro"`
38
39
  * @param config - Configuration object containing API keys (`openAIAPIKey`,
39
- * `anthropicAPIKey`, `googleAPIKey`, `deepSeekAPIKey`, `kimiAPIKey`, `glmAPIKey`),
40
- * optional `maxTokens`, and optional `temperature`
40
+ * `anthropicAPIKey`, `googleAPIKey`) and optional `temperature`
41
41
  * @param schema - Optional Zod schema used to configure native structured JSON output
42
- * for GPT / Claude / Gemini models; ignored for OpenAI-compatible providers
43
42
  * @returns A configured LangChain chat model (or bound runnable) instance
44
43
  */
45
44
  export declare const getLLMModel: (modelName: string, config: any, schema?: any) => any;
@@ -1 +1 @@
1
- {"version":3,"file":"langChain.d.ts","sourceRoot":"","sources":["../../src/langChain.ts"],"names":[],"mappings":"AA4GA;;;;;GAKG;AACH,MAAM,MAAM,YAAY,GAAG;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE;QAAE,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAA;KAAE,CAAC;IACvC;;;;;OAKG;IACH,gBAAgB,CAAC,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CACtD,CAAC;AAmrBF;;;;;;;;;;;;;;;;;;;;;GAqBG;AAGH,eAAO,MAAM,WAAW,GACtB,WAAW,MAAM,EACjB,QAAQ,GAAG,EACX,SAAQ,GAAU,KACjB,GAoHF,CAAC;;0CAj0B2C,GAAG,KAAG,IAAI;wBAo1B/C,MAAM,aACD,MAAM,gBACH,MAAM,SACb,GAAG,EAAE,kBACI,GAAG,UACX,GAAG,iBACG,YAAY,GAAG,IAAI,KAChC,GAAG;sBAoCG,GAAG,UACF,MAAM,UACN,GAAG,eACC,QAAQ,GAAG,IAAI,iBACb,YAAY,GAAG,IAAI,KAChC,OAAO,CAAC,GAAG,CAAC;oCAkYF,MAAM,UACT,GAAG,UACH,GAAG,oBACO,GAAG,eACT,MAAM,eACN,MAAM,wBACG,OAAO,WACpB,GAAG,cACA,MAAM,UACV,GAAG,EAAE,iBACE,YAAY,GAAG,IAAI,KAChC,OAAO,CAAC,GAAG,CAAC;sDA2tBF,MAAM,UACT,GAAG,UACH,MAAM,YACL,GAAG,iBACE,YAAY,GAAG,IAAI,KAChC,OAAO,CAAC,GAAG,CAAC;sDAmTF,MAAM,UACT,GAAG,UACH,MAAM,YACL,GAAG,iBACE,YAAY,GAAG,IAAI,KAChC,OAAO,CAAC,GAAG,CAAC;0CA7zC8B,GAAG,KAAG,MAAM;+CAh0B9C,YAAY,GAAG,IAAI,GAAG,SAAS,aAC7B,MAAM,kBACD,MAAM,UACd,GAAG,KACV,IAAI;mCAg1BU,MAAM,mBACJ,MAAM,EAAE,KACxB,MAAM;;AAg2CT,wBAUE"}
1
+ {"version":3,"file":"langChain.d.ts","sourceRoot":"","sources":["../../src/langChain.ts"],"names":[],"mappings":"AA6GA;;;;;GAKG;AACH,MAAM,MAAM,YAAY,GAAG;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE;QAAE,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAA;KAAE,CAAC;IACvC;;;;;OAKG;IACH,gBAAgB,CAAC,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CACtD,CAAC;AA0lBF;;;;;;;;;;;;;;;;;;;;GAoBG;AAGH,eAAO,MAAM,WAAW,GACtB,WAAW,MAAM,EACjB,QAAQ,GAAG,EACX,SAAQ,GAAU,KACjB,GAwHF,CAAC;;0CA3uB2C,GAAG,KAAG,IAAI;wBA8vB/C,MAAM,aACD,MAAM,gBACH,MAAM,SACb,GAAG,EAAE,kBACI,GAAG,UACX,GAAG,iBACG,YAAY,GAAG,IAAI,KAChC,GAAG;sBAoCG,GAAG,UACF,MAAM,UACN,GAAG,eACC,QAAQ,GAAG,IAAI,iBACb,YAAY,GAAG,IAAI,KAChC,OAAO,CAAC,GAAG,CAAC;oCAkYF,MAAM,UACT,GAAG,UACH,GAAG,oBACO,GAAG,eACT,MAAM,eACN,MAAM,wBACG,OAAO,WACpB,GAAG,cACA,MAAM,UACV,GAAG,EAAE,iBACE,YAAY,GAAG,IAAI,KAChC,OAAO,CAAC,GAAG,CAAC;sDA2tBF,MAAM,UACT,GAAG,UACH,MAAM,YACL,GAAG,iBACE,YAAY,GAAG,IAAI,KAChC,OAAO,CAAC,GAAG,CAAC;sDAmTF,MAAM,UACT,GAAG,UACH,MAAM,YACL,GAAG,iBACE,YAAY,GAAG,IAAI,KAChC,OAAO,CAAC,GAAG,CAAC;0CA7zC8B,GAAG,KAAG,MAAM;+CA1uB9C,YAAY,GAAG,IAAI,GAAG,SAAS,aAC7B,MAAM,kBACD,MAAM,UACd,GAAG,KACV,IAAI;mCA0vBU,MAAM,mBACJ,MAAM,EAAE,KACxB,MAAM;;AAg2CT,wBAUE"}
@@ -24,9 +24,10 @@ Do NOT include raw newlines, tabs, or unescaped quotes inside JSON string values
24
24
  * Providers for which `getLLMModel` configures native, schema-constrained JSON
25
25
  * output (OpenAI `response_format`, Anthropic `output_config.format`, Gemini
26
26
  * `responseSchema` + JSON mode). For these the model emits JSON directly, so the
27
- * prompt-injected schema and the schema-validation retry are skipped. Other
28
- * providers (DeepSeek/Kimi/GLM via the OpenAI-compatible shim) have no native
29
- * support here and continue to rely on prompt instructions + validation.
27
+ * prompt-injected schema and the schema-validation retry are skipped. Every
28
+ * supported provider is listed here; the check remains so that adding one
29
+ * without native support falls back to prompt instructions + validation
30
+ * rather than silently skipping them.
30
31
  * @param modelName - The model identifier being routed
31
32
  * @returns `true` when native structured output is wired up for the provider
32
33
  */
@@ -537,36 +538,6 @@ const extractJSONFromResponse = (text) => {
537
538
  }
538
539
  return null;
539
540
  };
540
- /**
541
- * Maps a model name prefix to its OpenAI-compatible provider configuration. DeepSeek,
542
- * Kimi (Moonshot), and GLM (Zhipu) all expose OpenAI-compatible chat completion APIs
543
- * and can be routed through `ChatOpenAI` with a custom base URL and API key.
544
- * @param modelName - The model identifier to inspect
545
- * @param config - Configuration object containing provider-specific API keys
546
- * @returns An object with `apiKey` and `baseURL` when the model matches a known
547
- * OpenAI-compatible provider, or `null` otherwise
548
- */
549
- const getOpenAICompatibleProvider = (modelName, config) => {
550
- if (modelName.startsWith("deepseek-")) {
551
- return {
552
- apiKey: config.deepSeekAPIKey,
553
- baseURL: "https://api.deepseek.com/v1",
554
- };
555
- }
556
- if (modelName.startsWith("kimi-") || modelName.startsWith("moonshot-")) {
557
- return {
558
- apiKey: config.kimiAPIKey,
559
- baseURL: "https://api.moonshot.ai/v1",
560
- };
561
- }
562
- if (modelName.startsWith("glm-")) {
563
- return {
564
- apiKey: config.glmAPIKey,
565
- baseURL: "https://open.bigmodel.cn/api/paas/v4/",
566
- };
567
- }
568
- return null;
569
- };
570
541
  /**
571
542
  * Whether a model accepts the `temperature` sampling parameter at all. Some
572
543
  * models reject it with an HTTP 400, so it must be omitted rather than clamped:
@@ -576,7 +547,7 @@ const getOpenAICompatibleProvider = (modelName, config) => {
576
547
  * accept it.
577
548
  * - OpenAI reasoning models (the GPT-5 family and the o-series) reject it;
578
549
  * gpt-4o still accepts it.
579
- * DeepSeek, Kimi/Moonshot, GLM, and Gemini all accept temperature.
550
+ * Gemini accepts temperature.
580
551
  * @param modelName - the model identifier being routed
581
552
  * @returns `true` when the model accepts a `temperature` parameter
582
553
  */
@@ -597,18 +568,14 @@ const modelSupportsTemperature = (modelName) => {
597
568
  };
598
569
  /**
599
570
  * Per-provider valid `temperature` range, used to clamp caller-supplied values.
600
- * Anthropic, Kimi (Moonshot), and GLM (Zhipu) cap at 1.0; OpenAI, Gemini, and
601
- * DeepSeek accept up to 2.0. Only consulted for models that support temperature
602
- * at all (see `modelSupportsTemperature`).
571
+ * Anthropic caps at 1.0; OpenAI and Gemini accept up to 2.0. Only consulted for
572
+ * models that support temperature at all (see `modelSupportsTemperature`).
603
573
  */
604
574
  const getTemperatureRange = (modelName) => {
605
- if (modelName.startsWith("claude-") ||
606
- modelName.startsWith("kimi-") ||
607
- modelName.startsWith("moonshot-") ||
608
- modelName.startsWith("glm-")) {
575
+ if (modelName.startsWith("claude-")) {
609
576
  return { min: 0, max: 1 };
610
577
  }
611
- // gpt-, gemini-, deepseek-
578
+ // gpt-, gemini-
612
579
  return { min: 0, max: 2 };
613
580
  };
614
581
  /**
@@ -637,46 +604,6 @@ const resolveTemperatureSetting = (modelName, config) => {
637
604
  }
638
605
  return { temperature: clamped };
639
606
  };
640
- /**
641
- * Gemini's ceiling on `maxOutputTokens`. The API validates the field and
642
- * rejects the request outright rather than clamping it:
643
- * "Unable to submit request because it has a maxOutputTokens value of N but the
644
- * supported range is from 1 (inclusive) to 65537 (exclusive)" (HTTP 400,
645
- * INVALID_ARGUMENT).
646
- *
647
- * Set to 64000 rather than that 65536 validation ceiling: the Pro models report
648
- * a generation capacity of 64000, so 64000 is the value every model across the
649
- * Gemini 3 line (3.1 Pro, 3.5/3.7/3.8 Flash) can both accept and actually
650
- * produce. One safe constant beats a per-model table that would need editing
651
- * every time Google ships a model — and the 1536 tokens of headroom given up on
652
- * Flash are far beyond what any caller asks for.
653
- *
654
- * Deliberately Gemini-only. Anthropic and OpenAI take the caller's `maxTokens`
655
- * unchanged, since neither rejects the values configured today.
656
- */
657
- const GEMINI_MAX_OUTPUT_TOKENS = 64000;
658
- /**
659
- * Resolves `maxOutputTokens` for a Gemini model. Callers share one `maxTokens`
660
- * setting across every provider, and the other providers tolerate values far
661
- * above what Gemini accepts, so anything above Gemini's ceiling is clamped here
662
- * (with a warning) instead of being sent through to a guaranteed HTTP 400. With
663
- * no caller value the ceiling itself is used, which is what the previous
664
- * 200000 default was reaching for.
665
- * @param modelName - The Gemini model identifier being routed
666
- * @param config - Configuration object; only `maxTokens` is consulted
667
- * @returns A token count within Gemini's supported range
668
- */
669
- const resolveGeminiMaxOutputTokens = (modelName, config) => {
670
- const maxTokens = config?.maxTokens;
671
- if (typeof maxTokens !== "number" || !Number.isFinite(maxTokens)) {
672
- return GEMINI_MAX_OUTPUT_TOKENS;
673
- }
674
- if (maxTokens > GEMINI_MAX_OUTPUT_TOKENS) {
675
- logger.log(null, logger.levels.warn, `maxTokens ${maxTokens} exceeds the ${GEMINI_MAX_OUTPUT_TOKENS}-token output ceiling of "${modelName}"; clamping to ${GEMINI_MAX_OUTPUT_TOKENS}.`);
676
- return GEMINI_MAX_OUTPUT_TOKENS;
677
- }
678
- return maxTokens;
679
- };
680
607
  /**
681
608
  * Flattens LangChain message content into plain text. Content is a string for
682
609
  * ordinary completions, but providers switch to an array of blocks whenever the
@@ -704,23 +631,22 @@ const extractTextContent = (content) => {
704
631
  /**
705
632
  * Instantiates and returns the appropriate LangChain chat model based on the model
706
633
  * name prefix. `claude-` maps to `ChatAnthropic`, `gemini-` maps to
707
- * `ChatGoogleGenerativeAI`, and `gpt-` maps to `ChatOpenAI`. DeepSeek (`deepseek-`),
708
- * Kimi/Moonshot (`kimi-`, `moonshot-`), and GLM/Zhipu (`glm-`) models are routed
709
- * through `ChatOpenAI` against each provider's OpenAI-compatible endpoint. When a Zod
634
+ * `ChatGoogleGenerativeAI`, and `gpt-` maps to `ChatOpenAI`. When a Zod
710
635
  * `schema` is provided, native structured output is configured per provider: OpenAI
711
636
  * via `response_format` with `json_schema`, Anthropic via `output_config.format`, and
712
637
  * Gemini via JSON mode (`json: true`) plus `responseSchema`. In every case the model
713
638
  * emits JSON as the message text, so the caller's parse/validate pipeline is unchanged.
714
- * The schema is ignored for the OpenAI-compatible providers (DeepSeek/Kimi/GLM), which
715
- * have no native structured-output support here. Throws a `BAD_REQUEST` error for
716
- * unrecognised model names.
639
+ * Throws a `BAD_REQUEST` error for unrecognised model names.
640
+ *
641
+ * Output token limits are not caller-configurable. Anthropic requires
642
+ * `max_tokens` on every request and @langchain/anthropic defaults it to 4096,
643
+ * so the Claude branch pins it explicitly; OpenAI and Gemini are sent no limit
644
+ * and fall back to each provider's own default, which is that model's maximum.
717
645
  * @param modelName - The model identifier, e.g. `"gpt-4o"`, `"claude-3-5-sonnet"`,
718
- * `"gemini-1.5-pro"`, `"deepseek-chat"`, `"kimi-k2-0905-preview"`, `"glm-4.6"`
646
+ * `"gemini-1.5-pro"`
719
647
  * @param config - Configuration object containing API keys (`openAIAPIKey`,
720
- * `anthropicAPIKey`, `googleAPIKey`, `deepSeekAPIKey`, `kimiAPIKey`, `glmAPIKey`),
721
- * optional `maxTokens`, and optional `temperature`
648
+ * `anthropicAPIKey`, `googleAPIKey`) and optional `temperature`
722
649
  * @param schema - Optional Zod schema used to configure native structured JSON output
723
- * for GPT / Claude / Gemini models; ignored for OpenAI-compatible providers
724
650
  * @returns A configured LangChain chat model (or bound runnable) instance
725
651
  */
726
652
  // Exported (but not re-exported from index.ts / the package's public entry
@@ -731,12 +657,17 @@ export const getLLMModel = (modelName, config, schema = null) => {
731
657
  const modelSettings = resolveTemperatureSetting(modelName, config);
732
658
  // Claude models (Anthropic)
733
659
  if (modelName.startsWith("claude-")) {
660
+ // Anthropic requires `max_tokens` on every request and @langchain/anthropic
661
+ // defaults it to 4096 when omitted — low enough to truncate agentic turns
662
+ // mid-tool-call — so it is pinned here rather than left to the library.
663
+ // 64000 is Anthropic's recommended value for streamed requests and sits
664
+ // within the 128000-token output limit of every Claude model routed here.
665
+ //
734
666
  // Anthropic's SDK rejects non-streamed requests when max_tokens is large
735
- // enough that the operation could exceed 10 minutes (threshold: 21333).
736
- // Auto-enable LangChain streaming above that cutoff so `.invoke()` routes
737
- // through the streaming endpoint internally — callers see no interface
738
- // change, but the guard is bypassed.
739
- const maxTokens = config.maxTokens || 16384;
667
+ // enough that the operation could exceed 10 minutes (threshold: 21333), so
668
+ // streaming is always on: `.invoke()` routes through the streaming endpoint
669
+ // internally and callers see no interface change.
670
+ const maxTokens = 64000;
740
671
  // Native structured output: Anthropic's `output_config.format` constrains
741
672
  // decoding to the schema while still emitting JSON as the message text, so
742
673
  // the downstream streaming + parse/validate pipeline is unchanged.
@@ -757,7 +688,7 @@ export const getLLMModel = (modelName, config, schema = null) => {
757
688
  apiKey: config.anthropicAPIKey,
758
689
  maxTokens,
759
690
  modelName: modelName,
760
- streaming: maxTokens > 16384,
691
+ streaming: true,
761
692
  invocationKwargs: { cache_control: { type: "ephemeral" } },
762
693
  ...outputConfig,
763
694
  ...modelSettings,
@@ -768,7 +699,9 @@ export const getLLMModel = (modelName, config, schema = null) => {
768
699
  else if (modelName.startsWith("gemini-")) {
769
700
  const model = new ChatGoogleGenerativeAI({
770
701
  apiKey: config.googleAPIKey,
771
- maxOutputTokens: resolveGeminiMaxOutputTokens(modelName, config),
702
+ // No `maxOutputTokens`: omitting it leaves Gemini's own default, which is
703
+ // the model's maximum output. Sending a value meant tracking a per-model
704
+ // ceiling that the API rejects outright rather than clamps.
772
705
  model: modelName,
773
706
  // `json: true` forces responseMimeType=application/json, guaranteeing the
774
707
  // model returns valid JSON text (never prose) for the parse pipeline.
@@ -790,17 +723,26 @@ export const getLLMModel = (modelName, config, schema = null) => {
790
723
  else if (modelName.startsWith("gpt-")) {
791
724
  const openAISettings = {
792
725
  apiKey: config.openAIAPIKey,
793
- max_tokens: config.maxTokens || 200000,
726
+ // No output limit is sent: OpenAI defaults to the model's maximum. The
727
+ // snake_case `max_tokens` that used to sit here was never a recognised
728
+ // ChatOpenAI field and was silently dropped by LangChain.
794
729
  modelName: modelName,
795
730
  ...modelSettings,
796
731
  };
797
- // gpt-5.6's /v1/chat/completions endpoint 400s whenever function tools are
798
- // bound, because it rejects the model's own server-side default reasoning
799
- // effort in that combination ("use /v1/responses or set reasoning_effort
800
- // to 'none'"); forcing it to "none" here avoids the collision. Scoped to
801
- // exactly gpt-5.6 — sibling gpt-5.x models are not confirmed to share the
802
- // bug.
803
- const reasoningEffortOverride = modelName === "gpt-5.6" ? { reasoning_effort: "none" } : {};
732
+ // The gpt-5.6 family's /v1/chat/completions endpoint 400s whenever function
733
+ // tools are bound, because it rejects the model's own server-side default
734
+ // reasoning effort in that combination ("use /v1/responses or set
735
+ // reasoning_effort to 'none'"); forcing it to "none" here avoids the
736
+ // collision. The restriction belongs to the family, not to one id: the
737
+ // `gpt-5.6` alias resolves to `gpt-5.6-sol`, and `gpt-5.6-terra` /
738
+ // `gpt-5.6-luna` are separately addressable ids that hit the same 400, as
739
+ // do dated snapshots (`gpt-5.6-2026-08-22`). Matching the alias alone left
740
+ // every one of those unpatched, so match the whole family. Still excludes
741
+ // sibling releases (gpt-5.5, gpt-5.7): they are not confirmed to share the
742
+ // bug, and reasoning effort is worth keeping wherever it is accepted.
743
+ const reasoningEffortOverride = /^gpt-5\.6($|-)/.test(modelName)
744
+ ? { reasoning_effort: "none" }
745
+ : {};
804
746
  // Use native structured output with a JSON schema. `modelKwargs` is spread
805
747
  // verbatim into the Chat Completions request. Merge (not overwrite) with
806
748
  // the reasoning-effort override above, since both may apply at once.
@@ -823,17 +765,6 @@ export const getLLMModel = (modelName, config, schema = null) => {
823
765
  }
824
766
  return new ChatOpenAI(openAISettings);
825
767
  }
826
- // OpenAI-compatible providers: DeepSeek, Kimi (Moonshot), GLM (Zhipu)
827
- const openAICompatible = getOpenAICompatibleProvider(modelName, config);
828
- if (openAICompatible) {
829
- return new ChatOpenAI({
830
- apiKey: openAICompatible.apiKey,
831
- maxTokens: config.maxTokens || 200000,
832
- modelName: modelName,
833
- configuration: { baseURL: openAICompatible.baseURL },
834
- ...modelSettings,
835
- });
836
- }
837
768
  throw errorHandlingHelper.create(constants.HTTP_CODES.BAD_REQUEST, constants.ERROR.UNSUPPORTED_MODEL + ": " + modelName);
838
769
  };
839
770
  /**
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "1.4.1",
6
+ "version": "1.4.3",
7
7
  "description": "",
8
8
  "main": "dist/cjs/index.js",
9
9
  "module": "dist/esm/index.js",