@stackfactor/agent-utils 1.4.0 → 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;AAkoBF;;;;;;;;;;;;;;;;;;;;;GAqBG;AAGH,eAAO,MAAM,WAAW,GACtB,WAAW,MAAM,EACjB,QAAQ,GAAG,EACX,SAAQ,GAAU,KACjB,GAoHF,CAAC;;0CAhxB2C,GAAG,KAAG,IAAI;wBAmyB/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;+CA/wB9C,YAAY,GAAG,IAAI,GAAG,SAAS,aAC7B,MAAM,kBACD,MAAM,UACd,GAAG,KACV,IAAI;mCA+xBU,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
  /**
@@ -670,23 +637,22 @@ const extractTextContent = (content) => {
670
637
  /**
671
638
  * Instantiates and returns the appropriate LangChain chat model based on the model
672
639
  * name prefix. `claude-` maps to `ChatAnthropic`, `gemini-` maps to
673
- * `ChatGoogleGenerativeAI`, and `gpt-` maps to `ChatOpenAI`. DeepSeek (`deepseek-`),
674
- * Kimi/Moonshot (`kimi-`, `moonshot-`), and GLM/Zhipu (`glm-`) models are routed
675
- * through `ChatOpenAI` against each provider's OpenAI-compatible endpoint. When a Zod
640
+ * `ChatGoogleGenerativeAI`, and `gpt-` maps to `ChatOpenAI`. When a Zod
676
641
  * `schema` is provided, native structured output is configured per provider: OpenAI
677
642
  * via `response_format` with `json_schema`, Anthropic via `output_config.format`, and
678
643
  * Gemini via JSON mode (`json: true`) plus `responseSchema`. In every case the model
679
644
  * emits JSON as the message text, so the caller's parse/validate pipeline is unchanged.
680
- * The schema is ignored for the OpenAI-compatible providers (DeepSeek/Kimi/GLM), which
681
- * have no native structured-output support here. Throws a `BAD_REQUEST` error for
682
- * 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.
683
651
  * @param modelName - The model identifier, e.g. `"gpt-4o"`, `"claude-3-5-sonnet"`,
684
- * `"gemini-1.5-pro"`, `"deepseek-chat"`, `"kimi-k2-0905-preview"`, `"glm-4.6"`
652
+ * `"gemini-1.5-pro"`
685
653
  * @param config - Configuration object containing API keys (`openAIAPIKey`,
686
- * `anthropicAPIKey`, `googleAPIKey`, `deepSeekAPIKey`, `kimiAPIKey`, `glmAPIKey`),
687
- * optional `maxTokens`, and optional `temperature`
654
+ * `anthropicAPIKey`, `googleAPIKey`) and optional `temperature`
688
655
  * @param schema - Optional Zod schema used to configure native structured JSON output
689
- * for GPT / Claude / Gemini models; ignored for OpenAI-compatible providers
690
656
  * @returns A configured LangChain chat model (or bound runnable) instance
691
657
  */
692
658
  // Exported (but not re-exported from index.ts / the package's public entry
@@ -697,12 +663,17 @@ const getLLMModel = (modelName, config, schema = null) => {
697
663
  const modelSettings = resolveTemperatureSetting(modelName, config);
698
664
  // Claude models (Anthropic)
699
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
+ //
700
672
  // Anthropic's SDK rejects non-streamed requests when max_tokens is large
701
- // enough that the operation could exceed 10 minutes (threshold: 21333).
702
- // Auto-enable LangChain streaming above that cutoff so `.invoke()` routes
703
- // through the streaming endpoint internally — callers see no interface
704
- // change, but the guard is bypassed.
705
- 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;
706
677
  // Native structured output: Anthropic's `output_config.format` constrains
707
678
  // decoding to the schema while still emitting JSON as the message text, so
708
679
  // the downstream streaming + parse/validate pipeline is unchanged.
@@ -723,7 +694,7 @@ const getLLMModel = (modelName, config, schema = null) => {
723
694
  apiKey: config.anthropicAPIKey,
724
695
  maxTokens,
725
696
  modelName: modelName,
726
- streaming: maxTokens > 16384,
697
+ streaming: true,
727
698
  invocationKwargs: { cache_control: { type: "ephemeral" } },
728
699
  ...outputConfig,
729
700
  ...modelSettings,
@@ -734,7 +705,9 @@ const getLLMModel = (modelName, config, schema = null) => {
734
705
  else if (modelName.startsWith("gemini-")) {
735
706
  const model = new google_genai_1.ChatGoogleGenerativeAI({
736
707
  apiKey: config.googleAPIKey,
737
- maxOutputTokens: config.maxTokens || 200000,
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.
738
711
  model: modelName,
739
712
  // `json: true` forces responseMimeType=application/json, guaranteeing the
740
713
  // model returns valid JSON text (never prose) for the parse pipeline.
@@ -756,17 +729,26 @@ const getLLMModel = (modelName, config, schema = null) => {
756
729
  else if (modelName.startsWith("gpt-")) {
757
730
  const openAISettings = {
758
731
  apiKey: config.openAIAPIKey,
759
- 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.
760
735
  modelName: modelName,
761
736
  ...modelSettings,
762
737
  };
763
- // gpt-5.6's /v1/chat/completions endpoint 400s whenever function tools are
764
- // bound, because it rejects the model's own server-side default reasoning
765
- // effort in that combination ("use /v1/responses or set reasoning_effort
766
- // to 'none'"); forcing it to "none" here avoids the collision. Scoped to
767
- // exactly gpt-5.6 — sibling gpt-5.x models are not confirmed to share the
768
- // bug.
769
- 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
+ : {};
770
752
  // Use native structured output with a JSON schema. `modelKwargs` is spread
771
753
  // verbatim into the Chat Completions request. Merge (not overwrite) with
772
754
  // the reasoning-effort override above, since both may apply at once.
@@ -789,17 +771,6 @@ const getLLMModel = (modelName, config, schema = null) => {
789
771
  }
790
772
  return new openai_1.ChatOpenAI(openAISettings);
791
773
  }
792
- // OpenAI-compatible providers: DeepSeek, Kimi (Moonshot), GLM (Zhipu)
793
- const openAICompatible = getOpenAICompatibleProvider(modelName, config);
794
- if (openAICompatible) {
795
- return new openai_1.ChatOpenAI({
796
- apiKey: openAICompatible.apiKey,
797
- maxTokens: config.maxTokens || 200000,
798
- modelName: modelName,
799
- configuration: { baseURL: openAICompatible.baseURL },
800
- ...modelSettings,
801
- });
802
- }
803
774
  throw errorHandling_js_1.default.create(const_js_1.default.HTTP_CODES.BAD_REQUEST, const_js_1.default.ERROR.UNSUPPORTED_MODEL + ": " + modelName);
804
775
  };
805
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;AAkoBF;;;;;;;;;;;;;;;;;;;;;GAqBG;AAGH,eAAO,MAAM,WAAW,GACtB,WAAW,MAAM,EACjB,QAAQ,GAAG,EACX,SAAQ,GAAU,KACjB,GAoHF,CAAC;;0CAhxB2C,GAAG,KAAG,IAAI;wBAmyB/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;+CA/wB9C,YAAY,GAAG,IAAI,GAAG,SAAS,aAC7B,MAAM,kBACD,MAAM,UACd,GAAG,KACV,IAAI;mCA+xBU,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
  /**
@@ -664,23 +631,22 @@ const extractTextContent = (content) => {
664
631
  /**
665
632
  * Instantiates and returns the appropriate LangChain chat model based on the model
666
633
  * name prefix. `claude-` maps to `ChatAnthropic`, `gemini-` maps to
667
- * `ChatGoogleGenerativeAI`, and `gpt-` maps to `ChatOpenAI`. DeepSeek (`deepseek-`),
668
- * Kimi/Moonshot (`kimi-`, `moonshot-`), and GLM/Zhipu (`glm-`) models are routed
669
- * through `ChatOpenAI` against each provider's OpenAI-compatible endpoint. When a Zod
634
+ * `ChatGoogleGenerativeAI`, and `gpt-` maps to `ChatOpenAI`. When a Zod
670
635
  * `schema` is provided, native structured output is configured per provider: OpenAI
671
636
  * via `response_format` with `json_schema`, Anthropic via `output_config.format`, and
672
637
  * Gemini via JSON mode (`json: true`) plus `responseSchema`. In every case the model
673
638
  * emits JSON as the message text, so the caller's parse/validate pipeline is unchanged.
674
- * The schema is ignored for the OpenAI-compatible providers (DeepSeek/Kimi/GLM), which
675
- * have no native structured-output support here. Throws a `BAD_REQUEST` error for
676
- * 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.
677
645
  * @param modelName - The model identifier, e.g. `"gpt-4o"`, `"claude-3-5-sonnet"`,
678
- * `"gemini-1.5-pro"`, `"deepseek-chat"`, `"kimi-k2-0905-preview"`, `"glm-4.6"`
646
+ * `"gemini-1.5-pro"`
679
647
  * @param config - Configuration object containing API keys (`openAIAPIKey`,
680
- * `anthropicAPIKey`, `googleAPIKey`, `deepSeekAPIKey`, `kimiAPIKey`, `glmAPIKey`),
681
- * optional `maxTokens`, and optional `temperature`
648
+ * `anthropicAPIKey`, `googleAPIKey`) and optional `temperature`
682
649
  * @param schema - Optional Zod schema used to configure native structured JSON output
683
- * for GPT / Claude / Gemini models; ignored for OpenAI-compatible providers
684
650
  * @returns A configured LangChain chat model (or bound runnable) instance
685
651
  */
686
652
  // Exported (but not re-exported from index.ts / the package's public entry
@@ -691,12 +657,17 @@ export const getLLMModel = (modelName, config, schema = null) => {
691
657
  const modelSettings = resolveTemperatureSetting(modelName, config);
692
658
  // Claude models (Anthropic)
693
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
+ //
694
666
  // Anthropic's SDK rejects non-streamed requests when max_tokens is large
695
- // enough that the operation could exceed 10 minutes (threshold: 21333).
696
- // Auto-enable LangChain streaming above that cutoff so `.invoke()` routes
697
- // through the streaming endpoint internally — callers see no interface
698
- // change, but the guard is bypassed.
699
- 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;
700
671
  // Native structured output: Anthropic's `output_config.format` constrains
701
672
  // decoding to the schema while still emitting JSON as the message text, so
702
673
  // the downstream streaming + parse/validate pipeline is unchanged.
@@ -717,7 +688,7 @@ export const getLLMModel = (modelName, config, schema = null) => {
717
688
  apiKey: config.anthropicAPIKey,
718
689
  maxTokens,
719
690
  modelName: modelName,
720
- streaming: maxTokens > 16384,
691
+ streaming: true,
721
692
  invocationKwargs: { cache_control: { type: "ephemeral" } },
722
693
  ...outputConfig,
723
694
  ...modelSettings,
@@ -728,7 +699,9 @@ export const getLLMModel = (modelName, config, schema = null) => {
728
699
  else if (modelName.startsWith("gemini-")) {
729
700
  const model = new ChatGoogleGenerativeAI({
730
701
  apiKey: config.googleAPIKey,
731
- maxOutputTokens: config.maxTokens || 200000,
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.
732
705
  model: modelName,
733
706
  // `json: true` forces responseMimeType=application/json, guaranteeing the
734
707
  // model returns valid JSON text (never prose) for the parse pipeline.
@@ -750,17 +723,26 @@ export const getLLMModel = (modelName, config, schema = null) => {
750
723
  else if (modelName.startsWith("gpt-")) {
751
724
  const openAISettings = {
752
725
  apiKey: config.openAIAPIKey,
753
- 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.
754
729
  modelName: modelName,
755
730
  ...modelSettings,
756
731
  };
757
- // gpt-5.6's /v1/chat/completions endpoint 400s whenever function tools are
758
- // bound, because it rejects the model's own server-side default reasoning
759
- // effort in that combination ("use /v1/responses or set reasoning_effort
760
- // to 'none'"); forcing it to "none" here avoids the collision. Scoped to
761
- // exactly gpt-5.6 — sibling gpt-5.x models are not confirmed to share the
762
- // bug.
763
- 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
+ : {};
764
746
  // Use native structured output with a JSON schema. `modelKwargs` is spread
765
747
  // verbatim into the Chat Completions request. Merge (not overwrite) with
766
748
  // the reasoning-effort override above, since both may apply at once.
@@ -783,17 +765,6 @@ export const getLLMModel = (modelName, config, schema = null) => {
783
765
  }
784
766
  return new ChatOpenAI(openAISettings);
785
767
  }
786
- // OpenAI-compatible providers: DeepSeek, Kimi (Moonshot), GLM (Zhipu)
787
- const openAICompatible = getOpenAICompatibleProvider(modelName, config);
788
- if (openAICompatible) {
789
- return new ChatOpenAI({
790
- apiKey: openAICompatible.apiKey,
791
- maxTokens: config.maxTokens || 200000,
792
- modelName: modelName,
793
- configuration: { baseURL: openAICompatible.baseURL },
794
- ...modelSettings,
795
- });
796
- }
797
768
  throw errorHandlingHelper.create(constants.HTTP_CODES.BAD_REQUEST, constants.ERROR.UNSUPPORTED_MODEL + ": " + modelName);
798
769
  };
799
770
  /**
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "1.4.0",
6
+ "version": "1.4.3",
7
7
  "description": "",
8
8
  "main": "dist/cjs/index.js",
9
9
  "module": "dist/esm/index.js",