@stackfactor/agent-utils 1.3.1 → 1.3.2

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.
@@ -20,6 +20,29 @@ export type UsageTracker = {
20
20
  title?: string;
21
21
  }[];
22
22
  };
23
+ /**
24
+ * Instantiates and returns the appropriate LangChain chat model based on the model
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
29
+ * `schema` is provided, native structured output is configured per provider: OpenAI
30
+ * via `response_format` with `json_schema`, Anthropic via `output_config.format`, and
31
+ * Gemini via JSON mode (`json: true`) plus `responseSchema`. In every case the model
32
+ * 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.
36
+ * @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
+ * @param config - Configuration object containing API keys (`openAIAPIKey`,
39
+ * `anthropicAPIKey`, `googleAPIKey`, `deepSeekAPIKey`, `kimiAPIKey`, `glmAPIKey`),
40
+ * optional `maxTokens`, and optional `temperature`
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
+ * @returns A configured LangChain chat model (or bound runnable) instance
44
+ */
45
+ export declare const getLLMModel: (modelName: string, config: any, schema?: any) => any;
23
46
  declare const _default: {
24
47
  checkIfAIProviderConfigured: (config: any) => void;
25
48
  createAgent: (name: string, modelName: string, systemPrompt: string, tools: any[], responseFormat: any, config: any, usageTracker?: UsageTracker | null) => 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;;0CAE2C,GAAG,KAAG,IAAI;wBA6uB/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;sDA0tBF,MAAM,UACT,GAAG,UACH,MAAM,YACL,GAAG,iBACE,YAAY,GAAG,IAAI,KAChC,OAAO,CAAC,GAAG,CAAC;0CApgC8B,GAAG,KAAG,MAAM;+CA1tB9C,YAAY,GAAG,IAAI,GAAG,SAAS,aAC7B,MAAM,kBACD,MAAM,UACd,GAAG,KACV,IAAI;mCA0uBU,MAAM,mBACJ,MAAM,EAAE,KACxB,MAAM;;AA2iCT,wBASE"}
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;AA2lBF;;;;;;;;;;;;;;;;;;;;;GAqBG;AAGH,eAAO,MAAM,WAAW,GACtB,WAAW,MAAM,EACjB,QAAQ,GAAG,EACX,SAAQ,GAAU,KACjB,GAoHF,CAAC;;0CAzuB2C,GAAG,KAAG,IAAI;wBA4vB/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;sDA0tBF,MAAM,UACT,GAAG,UACH,MAAM,YACL,GAAG,iBACE,YAAY,GAAG,IAAI,KAChC,OAAO,CAAC,GAAG,CAAC;0CApgC8B,GAAG,KAAG,MAAM;+CAzuB9C,YAAY,GAAG,IAAI,GAAG,SAAS,aAC7B,MAAM,kBACD,MAAM,UACd,GAAG,KACV,IAAI;mCAyvBU,MAAM,mBACJ,MAAM,EAAE,KACxB,MAAM;;AA2iCT,wBASE"}
@@ -3,6 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.getLLMModel = void 0;
6
7
  const openai_1 = require("@langchain/openai");
7
8
  const anthropic_1 = require("@langchain/anthropic");
8
9
  const google_genai_1 = require("@langchain/google-genai");
@@ -655,6 +656,8 @@ const extractTextContent = (content) => {
655
656
  * for GPT / Claude / Gemini models; ignored for OpenAI-compatible providers
656
657
  * @returns A configured LangChain chat model (or bound runnable) instance
657
658
  */
659
+ // Exported (but not re-exported from index.ts / the package's public entry
660
+ // point) solely so unit tests can import it directly from source.
658
661
  const getLLMModel = (modelName, config, schema = null) => {
659
662
  // Resolve `temperature` with presence/support/range handling (see
660
663
  // resolveTemperatureSetting). Applied uniformly to every provider below.
@@ -724,11 +727,20 @@ const getLLMModel = (modelName, config, schema = null) => {
724
727
  modelName: modelName,
725
728
  ...modelSettings,
726
729
  };
730
+ // gpt-5.6's /v1/chat/completions endpoint 400s whenever function tools are
731
+ // bound, because it rejects the model's own server-side default reasoning
732
+ // effort in that combination ("use /v1/responses or set reasoning_effort
733
+ // to 'none'"); forcing it to "none" here avoids the collision. Scoped to
734
+ // exactly gpt-5.6 — sibling gpt-5.x models are not confirmed to share the
735
+ // bug.
736
+ const reasoningEffortOverride = modelName === "gpt-5.6" ? { reasoning_effort: "none" } : {};
727
737
  // Use native structured output with a JSON schema. `modelKwargs` is spread
728
- // verbatim into the Chat Completions request.
738
+ // verbatim into the Chat Completions request. Merge (not overwrite) with
739
+ // the reasoning-effort override above, since both may apply at once.
729
740
  if (schema) {
730
741
  const jsonSchema = strictifyJsonSchema(buildJsonSchema(schema));
731
742
  openAISettings.modelKwargs = {
743
+ ...reasoningEffortOverride,
732
744
  response_format: {
733
745
  type: "json_schema",
734
746
  json_schema: {
@@ -739,6 +751,9 @@ const getLLMModel = (modelName, config, schema = null) => {
739
751
  },
740
752
  };
741
753
  }
754
+ else if (Object.keys(reasoningEffortOverride).length > 0) {
755
+ openAISettings.modelKwargs = { ...reasoningEffortOverride };
756
+ }
742
757
  return new openai_1.ChatOpenAI(openAISettings);
743
758
  }
744
759
  // OpenAI-compatible providers: DeepSeek, Kimi (Moonshot), GLM (Zhipu)
@@ -754,6 +769,7 @@ const getLLMModel = (modelName, config, schema = null) => {
754
769
  }
755
770
  throw errorHandling_js_1.default.create(const_js_1.default.HTTP_CODES.BAD_REQUEST, const_js_1.default.ERROR.UNSUPPORTED_MODEL + ": " + modelName);
756
771
  };
772
+ exports.getLLMModel = getLLMModel;
757
773
  /**
758
774
  * Constructs a LangChain agent configured with a specified model, system prompt, and
759
775
  * set of tools.
@@ -783,7 +799,7 @@ const createAgent = (name, modelName, systemPrompt, tools = [], responseFormat,
783
799
  }
784
800
  const agent = (0, langchain_1.createAgent)({
785
801
  name: name,
786
- model: getLLMModel(modelName, config),
802
+ model: (0, exports.getLLMModel)(modelName, config),
787
803
  systemPrompt: systemPrompt.trim(),
788
804
  tools: tavilyTools.length > 0 ? [...tools, ...tavilyTools] : tools,
789
805
  ...(responseFormat ? { responseFormat: responseFormat } : {}),
@@ -1246,7 +1262,7 @@ const runPromptWithModel = async (modelName, config, prompt, onProgressReport, m
1246
1262
  if (onProgressReport) {
1247
1263
  // Streaming mode: use server-side chunk-based progress for all models
1248
1264
  const useNativeSchema = expectsJsonResponse && !!schema && supportsNativeSchema(modelName);
1249
- const llm = getLLMModel(modelName, config, useNativeSchema ? schema : null);
1265
+ const llm = (0, exports.getLLMModel)(modelName, config, useNativeSchema ? schema : null);
1250
1266
  // Build messages with JSON instructions if needed
1251
1267
  let messagesToSend;
1252
1268
  if (expectsJsonResponse) {
@@ -1395,7 +1411,7 @@ const runPromptWithModel = async (modelName, config, prompt, onProgressReport, m
1395
1411
  // supports it (OpenAI/Anthropic/Gemini); otherwise fall back to prompt
1396
1412
  // instructions + validation retry.
1397
1413
  const useNativeSchema = expectsJsonResponse && !!schema && supportsNativeSchema(modelName);
1398
- const llm = getLLMModel(modelName, config, useNativeSchema ? schema : null);
1414
+ const llm = (0, exports.getLLMModel)(modelName, config, useNativeSchema ? schema : null);
1399
1415
  // Add escape instruction to help LLM produce valid JSON (only if expecting JSON).
1400
1416
  // When native structured output is in use the model is already constrained,
1401
1417
  // so a light instruction suffices and the schema is not re-injected.
@@ -20,6 +20,29 @@ export type UsageTracker = {
20
20
  title?: string;
21
21
  }[];
22
22
  };
23
+ /**
24
+ * Instantiates and returns the appropriate LangChain chat model based on the model
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
29
+ * `schema` is provided, native structured output is configured per provider: OpenAI
30
+ * via `response_format` with `json_schema`, Anthropic via `output_config.format`, and
31
+ * Gemini via JSON mode (`json: true`) plus `responseSchema`. In every case the model
32
+ * 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.
36
+ * @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
+ * @param config - Configuration object containing API keys (`openAIAPIKey`,
39
+ * `anthropicAPIKey`, `googleAPIKey`, `deepSeekAPIKey`, `kimiAPIKey`, `glmAPIKey`),
40
+ * optional `maxTokens`, and optional `temperature`
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
+ * @returns A configured LangChain chat model (or bound runnable) instance
44
+ */
45
+ export declare const getLLMModel: (modelName: string, config: any, schema?: any) => any;
23
46
  declare const _default: {
24
47
  checkIfAIProviderConfigured: (config: any) => void;
25
48
  createAgent: (name: string, modelName: string, systemPrompt: string, tools: any[], responseFormat: any, config: any, usageTracker?: UsageTracker | null) => 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;;0CAE2C,GAAG,KAAG,IAAI;wBA6uB/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;sDA0tBF,MAAM,UACT,GAAG,UACH,MAAM,YACL,GAAG,iBACE,YAAY,GAAG,IAAI,KAChC,OAAO,CAAC,GAAG,CAAC;0CApgC8B,GAAG,KAAG,MAAM;+CA1tB9C,YAAY,GAAG,IAAI,GAAG,SAAS,aAC7B,MAAM,kBACD,MAAM,UACd,GAAG,KACV,IAAI;mCA0uBU,MAAM,mBACJ,MAAM,EAAE,KACxB,MAAM;;AA2iCT,wBASE"}
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;AA2lBF;;;;;;;;;;;;;;;;;;;;;GAqBG;AAGH,eAAO,MAAM,WAAW,GACtB,WAAW,MAAM,EACjB,QAAQ,GAAG,EACX,SAAQ,GAAU,KACjB,GAoHF,CAAC;;0CAzuB2C,GAAG,KAAG,IAAI;wBA4vB/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;sDA0tBF,MAAM,UACT,GAAG,UACH,MAAM,YACL,GAAG,iBACE,YAAY,GAAG,IAAI,KAChC,OAAO,CAAC,GAAG,CAAC;0CApgC8B,GAAG,KAAG,MAAM;+CAzuB9C,YAAY,GAAG,IAAI,GAAG,SAAS,aAC7B,MAAM,kBACD,MAAM,UACd,GAAG,KACV,IAAI;mCAyvBU,MAAM,mBACJ,MAAM,EAAE,KACxB,MAAM;;AA2iCT,wBASE"}
@@ -650,7 +650,9 @@ const extractTextContent = (content) => {
650
650
  * for GPT / Claude / Gemini models; ignored for OpenAI-compatible providers
651
651
  * @returns A configured LangChain chat model (or bound runnable) instance
652
652
  */
653
- const getLLMModel = (modelName, config, schema = null) => {
653
+ // Exported (but not re-exported from index.ts / the package's public entry
654
+ // point) solely so unit tests can import it directly from source.
655
+ export const getLLMModel = (modelName, config, schema = null) => {
654
656
  // Resolve `temperature` with presence/support/range handling (see
655
657
  // resolveTemperatureSetting). Applied uniformly to every provider below.
656
658
  const modelSettings = resolveTemperatureSetting(modelName, config);
@@ -719,11 +721,20 @@ const getLLMModel = (modelName, config, schema = null) => {
719
721
  modelName: modelName,
720
722
  ...modelSettings,
721
723
  };
724
+ // gpt-5.6's /v1/chat/completions endpoint 400s whenever function tools are
725
+ // bound, because it rejects the model's own server-side default reasoning
726
+ // effort in that combination ("use /v1/responses or set reasoning_effort
727
+ // to 'none'"); forcing it to "none" here avoids the collision. Scoped to
728
+ // exactly gpt-5.6 — sibling gpt-5.x models are not confirmed to share the
729
+ // bug.
730
+ const reasoningEffortOverride = modelName === "gpt-5.6" ? { reasoning_effort: "none" } : {};
722
731
  // Use native structured output with a JSON schema. `modelKwargs` is spread
723
- // verbatim into the Chat Completions request.
732
+ // verbatim into the Chat Completions request. Merge (not overwrite) with
733
+ // the reasoning-effort override above, since both may apply at once.
724
734
  if (schema) {
725
735
  const jsonSchema = strictifyJsonSchema(buildJsonSchema(schema));
726
736
  openAISettings.modelKwargs = {
737
+ ...reasoningEffortOverride,
727
738
  response_format: {
728
739
  type: "json_schema",
729
740
  json_schema: {
@@ -734,6 +745,9 @@ const getLLMModel = (modelName, config, schema = null) => {
734
745
  },
735
746
  };
736
747
  }
748
+ else if (Object.keys(reasoningEffortOverride).length > 0) {
749
+ openAISettings.modelKwargs = { ...reasoningEffortOverride };
750
+ }
737
751
  return new ChatOpenAI(openAISettings);
738
752
  }
739
753
  // OpenAI-compatible providers: DeepSeek, Kimi (Moonshot), GLM (Zhipu)
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "1.3.1",
6
+ "version": "1.3.2",
7
7
  "description": "",
8
8
  "main": "dist/cjs/index.js",
9
9
  "module": "dist/esm/index.js",
@@ -14,6 +14,7 @@
14
14
  "scripts": {
15
15
  "clean": "rm -rf dist",
16
16
  "build": "npm run clean && tsc -p tsconfig.cjs.json && tsc -p tsconfig.esm.json && echo '{\"type\":\"commonjs\"}' > dist/cjs/package.json && echo '{\"type\":\"module\"}' > dist/esm/package.json",
17
+ "test": "npm run build && node --test \"test/**/*.test.js\"",
17
18
  "work:start": "bash scripts/work-start.sh",
18
19
  "work:commit": "bash scripts/work-commit.sh",
19
20
  "work:release": "bash scripts/release.sh",
@@ -62,6 +63,7 @@
62
63
  "zod": "^4.3.6"
63
64
  },
64
65
  "devDependencies": {
66
+ "@langchain/core": "^1.2.9",
65
67
  "typescript": "^5.9.3"
66
68
  }
67
69
  }