@gaunt-sloth/core 2.0.0-alpha.2 → 2.0.0-alpha.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (158) hide show
  1. package/.gsloth.code.md +10 -0
  2. package/README.md +6 -6
  3. package/dist/config/defaults.d.ts +85 -0
  4. package/dist/config/defaults.js +103 -0
  5. package/dist/config/defaults.js.map +1 -0
  6. package/dist/config/jsonc.d.ts +12 -0
  7. package/dist/config/jsonc.js +41 -0
  8. package/dist/config/jsonc.js.map +1 -0
  9. package/dist/config/loader.d.ts +169 -0
  10. package/dist/config/loader.js +872 -0
  11. package/dist/config/loader.js.map +1 -0
  12. package/dist/config/schema.d.ts +673 -0
  13. package/dist/config/schema.js +524 -0
  14. package/dist/config/schema.js.map +1 -0
  15. package/dist/config/shell-policy.d.ts +331 -0
  16. package/dist/config/shell-policy.js +238 -0
  17. package/dist/config/shell-policy.js.map +1 -0
  18. package/dist/config/types.d.ts +596 -0
  19. package/dist/config/types.js +13 -0
  20. package/dist/config/types.js.map +1 -0
  21. package/dist/config.d.ts +18 -647
  22. package/dist/config.js +15 -516
  23. package/dist/config.js.map +1 -1
  24. package/dist/constants.d.ts +42 -0
  25. package/dist/constants.js +42 -0
  26. package/dist/constants.js.map +1 -1
  27. package/dist/core/GthAbstractAgent.d.ts +85 -2
  28. package/dist/core/GthAbstractAgent.js +398 -28
  29. package/dist/core/GthAbstractAgent.js.map +1 -1
  30. package/dist/core/GthAgentRunner.d.ts +158 -1
  31. package/dist/core/GthAgentRunner.js +367 -4
  32. package/dist/core/GthAgentRunner.js.map +1 -1
  33. package/dist/core/GthLangChainAgent.js +193 -16
  34. package/dist/core/GthLangChainAgent.js.map +1 -1
  35. package/dist/core/debugCapture.d.ts +59 -0
  36. package/dist/core/debugCapture.js +101 -0
  37. package/dist/core/debugCapture.js.map +1 -0
  38. package/dist/core/gthLeanAgentFactory.d.ts +9 -0
  39. package/dist/core/gthLeanAgentFactory.js +10 -0
  40. package/dist/core/gthLeanAgentFactory.js.map +1 -0
  41. package/dist/core/plainToolIndication.d.ts +15 -0
  42. package/dist/core/plainToolIndication.js +148 -0
  43. package/dist/core/plainToolIndication.js.map +1 -0
  44. package/dist/core/runStats.d.ts +41 -0
  45. package/dist/core/runStats.js +73 -0
  46. package/dist/core/runStats.js.map +1 -0
  47. package/dist/core/shell/ShellCommandFailedError.d.ts +54 -0
  48. package/dist/core/shell/ShellCommandFailedError.js +68 -0
  49. package/dist/core/shell/ShellCommandFailedError.js.map +1 -0
  50. package/dist/core/shell/allowlist.d.ts +75 -0
  51. package/dist/core/shell/allowlist.js +187 -0
  52. package/dist/core/shell/allowlist.js.map +1 -0
  53. package/dist/core/shell/arity.d.ts +75 -0
  54. package/dist/core/shell/arity.js +313 -0
  55. package/dist/core/shell/arity.js.map +1 -0
  56. package/dist/core/shell/judge.d.ts +161 -0
  57. package/dist/core/shell/judge.js +261 -0
  58. package/dist/core/shell/judge.js.map +1 -0
  59. package/dist/core/shell/normalize.d.ts +27 -0
  60. package/dist/core/shell/normalize.js +53 -0
  61. package/dist/core/shell/normalize.js.map +1 -0
  62. package/dist/core/toolDisplay.d.ts +101 -0
  63. package/dist/core/toolDisplay.js +374 -0
  64. package/dist/core/toolDisplay.js.map +1 -0
  65. package/dist/core/toolOutputChannel.d.ts +68 -0
  66. package/dist/core/toolOutputChannel.js +112 -0
  67. package/dist/core/toolOutputChannel.js.map +1 -0
  68. package/dist/core/types.d.ts +173 -0
  69. package/dist/core/types.js.map +1 -1
  70. package/dist/history/historyFormat.d.ts +28 -0
  71. package/dist/history/historyFormat.js +127 -0
  72. package/dist/history/historyFormat.js.map +1 -0
  73. package/dist/history/historyStore.d.ts +198 -0
  74. package/dist/history/historyStore.js +482 -0
  75. package/dist/history/historyStore.js.map +1 -0
  76. package/dist/history/recordSession.d.ts +37 -0
  77. package/dist/history/recordSession.js +56 -0
  78. package/dist/history/recordSession.js.map +1 -0
  79. package/dist/index.d.ts +4 -0
  80. package/dist/index.js +4 -0
  81. package/dist/index.js.map +1 -1
  82. package/dist/providers/anthropic.d.ts +1 -1
  83. package/dist/providers/anthropic.js +5 -10
  84. package/dist/providers/anthropic.js.map +1 -1
  85. package/dist/providers/deepseek.d.ts +1 -1
  86. package/dist/providers/deepseek.js +5 -10
  87. package/dist/providers/deepseek.js.map +1 -1
  88. package/dist/providers/geminiSchemaSanitizer.d.ts +52 -0
  89. package/dist/providers/geminiSchemaSanitizer.js +201 -0
  90. package/dist/providers/geminiSchemaSanitizer.js.map +1 -0
  91. package/dist/providers/google-genai.d.ts +1 -1
  92. package/dist/providers/google-genai.js +9 -11
  93. package/dist/providers/google-genai.js.map +1 -1
  94. package/dist/providers/groq.d.ts +1 -1
  95. package/dist/providers/groq.js +5 -10
  96. package/dist/providers/groq.js.map +1 -1
  97. package/dist/providers/huggingface.d.ts +25 -0
  98. package/dist/providers/huggingface.js +69 -0
  99. package/dist/providers/huggingface.js.map +1 -0
  100. package/dist/providers/modelCatalog.d.ts +109 -0
  101. package/dist/providers/modelCatalog.js +245 -0
  102. package/dist/providers/modelCatalog.js.map +1 -0
  103. package/dist/providers/modelDiscovery.d.ts +111 -3
  104. package/dist/providers/modelDiscovery.js +183 -27
  105. package/dist/providers/modelDiscovery.js.map +1 -1
  106. package/dist/providers/ollama.d.ts +19 -5
  107. package/dist/providers/ollama.js +60 -52
  108. package/dist/providers/ollama.js.map +1 -1
  109. package/dist/providers/openai.d.ts +1 -1
  110. package/dist/providers/openai.js +5 -10
  111. package/dist/providers/openai.js.map +1 -1
  112. package/dist/providers/openrouter.d.ts +1 -1
  113. package/dist/providers/openrouter.js +12 -12
  114. package/dist/providers/openrouter.js.map +1 -1
  115. package/dist/providers/vertexai.d.ts +1 -1
  116. package/dist/providers/vertexai.js +9 -11
  117. package/dist/providers/vertexai.js.map +1 -1
  118. package/dist/providers/xai.d.ts +1 -1
  119. package/dist/providers/xai.js +5 -10
  120. package/dist/providers/xai.js.map +1 -1
  121. package/dist/runtime/askStructured.d.ts +62 -0
  122. package/dist/runtime/askStructured.js +76 -0
  123. package/dist/runtime/askStructured.js.map +1 -0
  124. package/dist/runtime/conversation.d.ts +59 -0
  125. package/dist/runtime/conversation.js +137 -0
  126. package/dist/runtime/conversation.js.map +1 -0
  127. package/dist/runtime/singleShot.d.ts +24 -5
  128. package/dist/runtime/singleShot.js +56 -9
  129. package/dist/runtime/singleShot.js.map +1 -1
  130. package/dist/utils/aiignoreUtils.js.map +1 -1
  131. package/dist/utils/binaryOutputUtils.js.map +1 -1
  132. package/dist/utils/consoleUtils.d.ts +22 -0
  133. package/dist/utils/consoleUtils.js +45 -0
  134. package/dist/utils/consoleUtils.js.map +1 -1
  135. package/dist/utils/debugDump.d.ts +57 -0
  136. package/dist/utils/debugDump.js +236 -0
  137. package/dist/utils/debugDump.js.map +1 -0
  138. package/dist/utils/debugUtils.d.ts +13 -4
  139. package/dist/utils/debugUtils.js +36 -13
  140. package/dist/utils/debugUtils.js.map +1 -1
  141. package/dist/utils/fileUtils.d.ts +24 -2
  142. package/dist/utils/fileUtils.js +54 -12
  143. package/dist/utils/fileUtils.js.map +1 -1
  144. package/dist/utils/llmUtils.js.map +1 -1
  145. package/dist/utils/redactSecrets.d.ts +63 -0
  146. package/dist/utils/redactSecrets.js +238 -0
  147. package/dist/utils/redactSecrets.js.map +1 -0
  148. package/dist/utils/systemPromptNotes.d.ts +181 -0
  149. package/dist/utils/systemPromptNotes.js +298 -0
  150. package/dist/utils/systemPromptNotes.js.map +1 -0
  151. package/dist/utils/systemUtils.d.ts +32 -1
  152. package/dist/utils/systemUtils.js +50 -3
  153. package/dist/utils/systemUtils.js.map +1 -1
  154. package/dist/utils/toolMatching.d.ts +30 -0
  155. package/dist/utils/toolMatching.js +44 -0
  156. package/dist/utils/toolMatching.js.map +1 -0
  157. package/package.json +17 -8
  158. package/schema/gsloth-config.schema.json +2111 -0
@@ -1,5 +1,6 @@
1
1
  import { displayWarning } from '#src/utils/consoleUtils.js';
2
- import { writeFileIfNotExistsWithMessages } from '#src/utils/fileUtils.js';
2
+ import { writeConfigFileWithMessages } from '#src/utils/fileUtils.js';
3
+ import { buildInitConfigContent, getCuratedFallbackModel } from '#src/providers/modelDiscovery.js';
3
4
  import { env } from '#src/utils/systemUtils.js';
4
5
  /**
5
6
  * Function to process JSON config and create Anthropic LLM instance
@@ -12,22 +13,16 @@ export async function processJsonConfig(llmConfig) {
12
13
  return new anthropic.ChatAnthropic({
13
14
  ...llmConfig,
14
15
  apiKey: anthropicApiKey,
15
- model: llmConfig.model || 'claude-sonnet-4-6',
16
+ model: llmConfig.model || getCuratedFallbackModel('anthropic'),
16
17
  });
17
18
  }
18
- const jsonContent = `{
19
- "llm": {
20
- "type": "anthropic",
21
- "model": "claude-sonnet-4-6"
22
- }
23
- }`;
24
19
  // noinspection JSUnusedGlobalSymbols
25
- export function init(configFileName) {
20
+ export function init(configFileName, force = false, model) {
26
21
  // Determine which content to use based on file extension
27
22
  if (!configFileName.endsWith('.json')) {
28
23
  throw new Error('Only JSON config is supported.');
29
24
  }
30
- writeFileIfNotExistsWithMessages(configFileName, jsonContent);
25
+ writeConfigFileWithMessages(configFileName, buildInitConfigContent('anthropic', model), force);
31
26
  displayWarning(`You need to update your ${configFileName} to add your Anthropic API key, ` +
32
27
  'or define ANTHROPIC_API_KEY environment variable.');
33
28
  }
@@ -1 +1 @@
1
- {"version":3,"file":"anthropic.js","sourceRoot":"","sources":["../../src/providers/anthropic.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAC5D,OAAO,EAAE,gCAAgC,EAAE,MAAM,yBAAyB,CAAC;AAC3E,OAAO,EAAE,GAAG,EAAE,MAAM,2BAA2B,CAAC;AAOhD;;GAEG;AACH,qCAAqC;AACrC,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,SAA+C;IAE/C,MAAM,SAAS,GAAG,MAAM,MAAM,CAAC,sBAAsB,CAAC,CAAC;IACvD,wEAAwE;IACxE,MAAM,eAAe,GAAG,SAAS,CAAC,MAAM,IAAI,GAAG,CAAC,iBAAiB,CAAC;IAClE,OAAO,IAAI,SAAS,CAAC,aAAa,CAAC;QACjC,GAAG,SAAS;QACZ,MAAM,EAAE,eAAe;QACvB,KAAK,EAAE,SAAS,CAAC,KAAK,IAAI,mBAAmB;KAC9C,CAAC,CAAC;AACL,CAAC;AAED,MAAM,WAAW,GAAG;;;;;EAKlB,CAAC;AAEH,qCAAqC;AACrC,MAAM,UAAU,IAAI,CAAC,cAAsB;IACzC,yDAAyD;IACzD,IAAI,CAAC,cAAc,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;QACtC,MAAM,IAAI,KAAK,CAAC,gCAAgC,CAAC,CAAC;IACpD,CAAC;IAED,gCAAgC,CAAC,cAAc,EAAE,WAAW,CAAC,CAAC;IAC9D,cAAc,CACZ,2BAA2B,cAAc,kCAAkC;QACzE,mDAAmD,CACtD,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"anthropic.js","sourceRoot":"","sources":["../../src/providers/anthropic.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAC5D,OAAO,EAAE,2BAA2B,EAAE,MAAM,yBAAyB,CAAC;AACtE,OAAO,EAAE,sBAAsB,EAAE,uBAAuB,EAAE,MAAM,kCAAkC,CAAC;AACnG,OAAO,EAAE,GAAG,EAAE,MAAM,2BAA2B,CAAC;AAOhD;;GAEG;AACH,qCAAqC;AACrC,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,SAA+C;IAE/C,MAAM,SAAS,GAAG,MAAM,MAAM,CAAC,sBAAsB,CAAC,CAAC;IACvD,wEAAwE;IACxE,MAAM,eAAe,GAAG,SAAS,CAAC,MAAM,IAAI,GAAG,CAAC,iBAAiB,CAAC;IAClE,OAAO,IAAI,SAAS,CAAC,aAAa,CAAC;QACjC,GAAG,SAAS;QACZ,MAAM,EAAE,eAAe;QACvB,KAAK,EAAE,SAAS,CAAC,KAAK,IAAI,uBAAuB,CAAC,WAAW,CAAC;KAC/D,CAAC,CAAC;AACL,CAAC;AAED,qCAAqC;AACrC,MAAM,UAAU,IAAI,CAAC,cAAsB,EAAE,KAAK,GAAG,KAAK,EAAE,KAAc;IACxE,yDAAyD;IACzD,IAAI,CAAC,cAAc,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;QACtC,MAAM,IAAI,KAAK,CAAC,gCAAgC,CAAC,CAAC;IACpD,CAAC;IAED,2BAA2B,CAAC,cAAc,EAAE,sBAAsB,CAAC,WAAW,EAAE,KAAK,CAAC,EAAE,KAAK,CAAC,CAAC;IAC/F,cAAc,CACZ,2BAA2B,cAAc,kCAAkC;QACzE,mDAAmD,CACtD,CAAC;AACJ,CAAC"}
@@ -1,4 +1,4 @@
1
1
  import type { BaseChatModel, BaseChatModelParams } from '@langchain/core/language_models/chat_models';
2
2
  import { ChatDeepSeekInput } from '@langchain/deepseek';
3
3
  export declare function processJsonConfig(llmConfig: ChatDeepSeekInput & BaseChatModelParams): Promise<BaseChatModel>;
4
- export declare function init(configFileName: string): void;
4
+ export declare function init(configFileName: string, force?: boolean, model?: string): void;
@@ -1,6 +1,7 @@
1
1
  import { displayWarning } from '#src/utils/consoleUtils.js';
2
2
  import { env } from '#src/utils/systemUtils.js';
3
- import { writeFileIfNotExistsWithMessages } from '#src/utils/fileUtils.js';
3
+ import { writeConfigFileWithMessages } from '#src/utils/fileUtils.js';
4
+ import { buildInitConfigContent, getCuratedFallbackModel } from '#src/providers/modelDiscovery.js';
4
5
  // Function to process JSON config and create DeepSeek LLM instance
5
6
  export async function processJsonConfig(llmConfig) {
6
7
  const deepseek = await import('@langchain/deepseek');
@@ -9,21 +10,15 @@ export async function processJsonConfig(llmConfig) {
9
10
  return new deepseek.ChatDeepSeek({
10
11
  ...llmConfig,
11
12
  apiKey: deepseekApiKey,
12
- model: llmConfig.model || 'deepseek-v4-pro',
13
+ model: llmConfig.model || getCuratedFallbackModel('deepseek'),
13
14
  });
14
15
  }
15
- const jsonContent = `{
16
- "llm": {
17
- "type": "deepseek",
18
- "model": "deepseek-v4-pro"
19
- }
20
- }`;
21
- export function init(configFileName) {
16
+ export function init(configFileName, force = false, model) {
22
17
  // Determine which content to use based on file extension
23
18
  if (!configFileName.endsWith('.json')) {
24
19
  throw new Error('Only JSON config is supported.');
25
20
  }
26
- writeFileIfNotExistsWithMessages(configFileName, jsonContent);
21
+ writeConfigFileWithMessages(configFileName, buildInitConfigContent('deepseek', model), force);
27
22
  displayWarning(`You need to update your ${configFileName} to add your DeepSeek API key, ` +
28
23
  'or define DEEPSEEK_API_KEY environment variable.');
29
24
  }
@@ -1 +1 @@
1
- {"version":3,"file":"deepseek.js","sourceRoot":"","sources":["../../src/providers/deepseek.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAC5D,OAAO,EAAE,GAAG,EAAE,MAAM,2BAA2B,CAAC;AAOhD,OAAO,EAAE,gCAAgC,EAAE,MAAM,yBAAyB,CAAC;AAE3E,mEAAmE;AACnE,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,SAAkD;IAElD,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,qBAAqB,CAAC,CAAC;IACrD,yEAAyE;IACzE,MAAM,cAAc,GAAG,SAAS,CAAC,MAAM,IAAI,GAAG,CAAC,gBAAgB,CAAC;IAChE,OAAO,IAAI,QAAQ,CAAC,YAAY,CAAC;QAC/B,GAAG,SAAS;QACZ,MAAM,EAAE,cAAc;QACtB,KAAK,EAAE,SAAS,CAAC,KAAK,IAAI,iBAAiB;KAC5C,CAAC,CAAC;AACL,CAAC;AAED,MAAM,WAAW,GAAG;;;;;EAKlB,CAAC;AAEH,MAAM,UAAU,IAAI,CAAC,cAAsB;IACzC,yDAAyD;IACzD,IAAI,CAAC,cAAc,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;QACtC,MAAM,IAAI,KAAK,CAAC,gCAAgC,CAAC,CAAC;IACpD,CAAC;IAED,gCAAgC,CAAC,cAAc,EAAE,WAAW,CAAC,CAAC;IAC9D,cAAc,CACZ,2BAA2B,cAAc,iCAAiC;QACxE,kDAAkD,CACrD,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"deepseek.js","sourceRoot":"","sources":["../../src/providers/deepseek.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAC5D,OAAO,EAAE,GAAG,EAAE,MAAM,2BAA2B,CAAC;AAOhD,OAAO,EAAE,2BAA2B,EAAE,MAAM,yBAAyB,CAAC;AACtE,OAAO,EAAE,sBAAsB,EAAE,uBAAuB,EAAE,MAAM,kCAAkC,CAAC;AAEnG,mEAAmE;AACnE,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,SAAkD;IAElD,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,qBAAqB,CAAC,CAAC;IACrD,yEAAyE;IACzE,MAAM,cAAc,GAAG,SAAS,CAAC,MAAM,IAAI,GAAG,CAAC,gBAAgB,CAAC;IAChE,OAAO,IAAI,QAAQ,CAAC,YAAY,CAAC;QAC/B,GAAG,SAAS;QACZ,MAAM,EAAE,cAAc;QACtB,KAAK,EAAE,SAAS,CAAC,KAAK,IAAI,uBAAuB,CAAC,UAAU,CAAC;KAC9D,CAAC,CAAC;AACL,CAAC;AAED,MAAM,UAAU,IAAI,CAAC,cAAsB,EAAE,KAAK,GAAG,KAAK,EAAE,KAAc;IACxE,yDAAyD;IACzD,IAAI,CAAC,cAAc,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;QACtC,MAAM,IAAI,KAAK,CAAC,gCAAgC,CAAC,CAAC;IACpD,CAAC;IAED,2BAA2B,CAAC,cAAc,EAAE,sBAAsB,CAAC,UAAU,EAAE,KAAK,CAAC,EAAE,KAAK,CAAC,CAAC;IAC9F,cAAc,CACZ,2BAA2B,cAAc,iCAAiC;QACxE,kDAAkD,CACrD,CAAC;AACJ,CAAC"}
@@ -0,0 +1,52 @@
1
+ /**
2
+ * @packageDocumentation
3
+ * GS2-58 — systemic Gemini tool-schema sanitizer at the `@langchain/google` provider boundary.
4
+ *
5
+ * Google Gemini's function-declaration schema is a SELECT SUBSET of OpenAPI 3.0. `@langchain/google`'s
6
+ * own `removeAdditionalProperties` strips only `additionalProperties`, so every other JSON-Schema-draft
7
+ * keyword the subset does not declare (`exclusiveMinimum`/`exclusiveMaximum`/`multipleOf`, `$defs`,
8
+ * `patternProperties`, `const`, `$ref`, `allOf`/`oneOf`/`not`, …) is passed straight to the wire, and
9
+ * Gemini 400s at tool-declaration send time — before any tool runs.
10
+ *
11
+ * This is the DURABLE fix (GS2-58, fix-cycle 1): rather than a denylist that is always one unknown
12
+ * keyword behind, {@link sanitizeGeminiToolSchema} is an ALLOWLIST — it keeps ONLY the fields the
13
+ * installed Gemini `Schema` type declares and drops everything else, so a future keyword in ANY tool
14
+ * (built-in, custom, or MCP — including schemas gaunt-sloth does not author) cannot re-break Gemini.
15
+ *
16
+ * The allowlist ({@link GEMINI_SUPPORTED_SCHEMA_KEYWORDS}) is derived DIRECTLY from the authoritative
17
+ * in-repo type — the `Gemini.Tools.Schema` interface in
18
+ * `node_modules/@langchain/google/dist/chat_models/api-types.d.{ts,cts}` (a `FunctionDeclaration`'s
19
+ * `parameters?: Schema`), "a select subset of an OpenAPI 3.0 schema object". Keeping it aligned with
20
+ * that type (not a remembered list) is what prevents a stale allowlist silently over-stripping.
21
+ *
22
+ * Scope is the google/gemini provider path ONLY: {@link applyGeminiToolSchemaSanitizer} is wired into
23
+ * the `google-genai` and `vertexai` presets' `processJsonConfig`. It leaves OpenAI/Anthropic/Ollama
24
+ * wiring untouched, and does not weaken the GS2-56/57 build-time denylist guard (that test still runs;
25
+ * this transform runs ahead of the wire send, so a sanitized tool is what Gemini sees).
26
+ */
27
+ import type { BaseChatModel } from '@langchain/core/language_models/chat_models';
28
+ /**
29
+ * The EXACT set of schema keywords Gemini's function-declaration schema accepts, transcribed field-
30
+ * for-field from the `Gemini.Tools.Schema` interface in
31
+ * `@langchain/google/dist/chat_models/api-types.d.ts`. Anything not in this set is dropped (allowlist).
32
+ *
33
+ * Note `anyOf` IS supported (unions / nullable) and MUST survive — only `allOf`/`oneOf`/`not` are
34
+ * absent from the type and therefore dropped. `exclusiveMinimum`/`exclusiveMaximum` are NOT in the type
35
+ * either; they are handled specially by rewriting them to `minimum`/`maximum` (see {@link sanitizeNode})
36
+ * before the allowlist filter runs.
37
+ */
38
+ export declare const GEMINI_SUPPORTED_SCHEMA_KEYWORDS: ReadonlySet<string>;
39
+ /**
40
+ * Pure, recursive allowlist normaliser: returns a cleaned DEEP COPY of a JSON-Schema containing only
41
+ * keywords Gemini's function-declaration `Schema` accepts, so its OpenAPI-3.0 subset accepts the tool.
42
+ * Does not mutate the input. See {@link sanitizeNode} for the transform; `anyOf` unions survive.
43
+ */
44
+ export declare function sanitizeGeminiToolSchema<T = unknown>(schema: T): T;
45
+ /**
46
+ * Wire the sanitizer into a ChatGoogle model at the tool-binding boundary. Overrides the instance's
47
+ * `bindTools` so every tool passed to it — built-in, custom, or MCP, via `createAgent`/`createDeepAgent`
48
+ * which both call `model.bindTools(tools)` — is sanitized before it reaches `@langchain/google`'s
49
+ * Gemini converter. Provider-scoped: only the google-genai/vertexai presets call this, so no other
50
+ * provider's tools are affected. Returns the same model instance for convenient chaining.
51
+ */
52
+ export declare function applyGeminiToolSchemaSanitizer<T extends BaseChatModel>(model: T): T;
@@ -0,0 +1,201 @@
1
+ import { toJsonSchema } from '@langchain/core/utils/json_schema';
2
+ import { isInteropZodSchema } from '@langchain/core/utils/types';
3
+ import { isSerializableSchema } from '@langchain/core/utils/standard_schema';
4
+ /**
5
+ * The EXACT set of schema keywords Gemini's function-declaration schema accepts, transcribed field-
6
+ * for-field from the `Gemini.Tools.Schema` interface in
7
+ * `@langchain/google/dist/chat_models/api-types.d.ts`. Anything not in this set is dropped (allowlist).
8
+ *
9
+ * Note `anyOf` IS supported (unions / nullable) and MUST survive — only `allOf`/`oneOf`/`not` are
10
+ * absent from the type and therefore dropped. `exclusiveMinimum`/`exclusiveMaximum` are NOT in the type
11
+ * either; they are handled specially by rewriting them to `minimum`/`maximum` (see {@link sanitizeNode})
12
+ * before the allowlist filter runs.
13
+ */
14
+ export const GEMINI_SUPPORTED_SCHEMA_KEYWORDS = new Set([
15
+ 'anyOf',
16
+ 'default',
17
+ 'description',
18
+ 'enum',
19
+ 'example',
20
+ 'format',
21
+ 'items',
22
+ 'maxItems',
23
+ 'maxLength',
24
+ 'maxProperties',
25
+ 'maximum',
26
+ 'minItems',
27
+ 'minLength',
28
+ 'minProperties',
29
+ 'minimum',
30
+ 'nullable',
31
+ 'pattern',
32
+ 'properties',
33
+ 'propertyOrdering',
34
+ 'required',
35
+ 'title',
36
+ 'type',
37
+ ]);
38
+ function isPlainObject(value) {
39
+ return value !== null && typeof value === 'object' && !Array.isArray(value);
40
+ }
41
+ /** Deep-clone a literal (JSON-schema data) value so the returned schema never aliases the input. */
42
+ function cloneLiteral(value) {
43
+ if (value === null || typeof value !== 'object')
44
+ return value;
45
+ try {
46
+ return structuredClone(value);
47
+ }
48
+ catch {
49
+ // Non-cloneable (functions/symbols) never appear in JSON-schema data; keep the reference.
50
+ return value;
51
+ }
52
+ }
53
+ /**
54
+ * Recursively normalise one schema node to Gemini's supported subset:
55
+ * - rewrite `exclusiveMinimum`→`minimum` / `exclusiveMaximum`→`maximum` (Gemini has no exclusive
56
+ * bound), keeping the TIGHTER bound when an inclusive one is also present;
57
+ * - keep ONLY {@link GEMINI_SUPPORTED_SCHEMA_KEYWORDS}; drop everything else;
58
+ * - recurse ONLY through real subschema positions — `properties` (map), `items` (schema or tuple),
59
+ * and `anyOf` (array of schemas). Literal-data positions (`enum`, `default`, `example`, `required`,
60
+ * `propertyOrdering`) are copied verbatim, so a keyword that merely appears as DATA is untouched.
61
+ */
62
+ function sanitizeNode(node) {
63
+ if (Array.isArray(node)) {
64
+ return node.map(sanitizeNode);
65
+ }
66
+ if (!isPlainObject(node)) {
67
+ return node;
68
+ }
69
+ const out = {};
70
+ for (const [key, value] of Object.entries(node)) {
71
+ // Allowlist: silently drop any keyword Gemini's Schema type does not declare (this is where
72
+ // $defs / definitions / patternProperties / const / multipleOf / $ref / allOf / oneOf / not /
73
+ // additionalProperties / $schema / exclusive* are removed).
74
+ if (!GEMINI_SUPPORTED_SCHEMA_KEYWORDS.has(key)) {
75
+ continue;
76
+ }
77
+ if (key === 'properties' && isPlainObject(value)) {
78
+ const mapped = {};
79
+ for (const [name, sub] of Object.entries(value)) {
80
+ mapped[name] = sanitizeNode(sub);
81
+ }
82
+ out[key] = mapped;
83
+ }
84
+ else if (key === 'items') {
85
+ out[key] = Array.isArray(value) ? value.map(sanitizeNode) : sanitizeNode(value);
86
+ }
87
+ else if (key === 'anyOf' && Array.isArray(value)) {
88
+ out[key] = value.map(sanitizeNode);
89
+ }
90
+ else {
91
+ // Supported scalar / literal-data keyword (type, enum, required, description, default, …).
92
+ out[key] = cloneLiteral(value);
93
+ }
94
+ }
95
+ // exclusive* → inclusive of the SAME value. Gemini has no exclusive bound; args are hints, so the
96
+ // loosening is accepted. When both an exclusive and an inclusive bound are present, keep the TIGHTER
97
+ // one (higher lower-bound / lower upper-bound) rather than letting the exclusive value clobber it.
98
+ const exclusiveMinimum = node.exclusiveMinimum;
99
+ if (typeof exclusiveMinimum === 'number') {
100
+ out.minimum =
101
+ typeof out.minimum === 'number' ? Math.max(out.minimum, exclusiveMinimum) : exclusiveMinimum;
102
+ }
103
+ const exclusiveMaximum = node.exclusiveMaximum;
104
+ if (typeof exclusiveMaximum === 'number') {
105
+ out.maximum =
106
+ typeof out.maximum === 'number' ? Math.min(out.maximum, exclusiveMaximum) : exclusiveMaximum;
107
+ }
108
+ return out;
109
+ }
110
+ /**
111
+ * Pure, recursive allowlist normaliser: returns a cleaned DEEP COPY of a JSON-Schema containing only
112
+ * keywords Gemini's function-declaration `Schema` accepts, so its OpenAPI-3.0 subset accepts the tool.
113
+ * Does not mutate the input. See {@link sanitizeNode} for the transform; `anyOf` unions survive.
114
+ */
115
+ export function sanitizeGeminiToolSchema(schema) {
116
+ return sanitizeNode(schema);
117
+ }
118
+ /**
119
+ * The SINGLE normalization pass. Converts a schema to JSON (zod → JSON via the same `@langchain/core`
120
+ * converter `@langchain/google` uses internally) and runs the allowlist. Every tool-shape branch in
121
+ * {@link sanitizeToolForGemini} routes its schema(s) through here — so no branch can filter
122
+ * inconsistently, and a future tool format is covered the moment it calls this.
123
+ */
124
+ function normalizeSchema(rawSchema) {
125
+ const jsonSchema = isInteropZodSchema(rawSchema) || isSerializableSchema(rawSchema)
126
+ ? toJsonSchema(rawSchema)
127
+ : rawSchema;
128
+ return sanitizeGeminiToolSchema(jsonSchema);
129
+ }
130
+ /** Build a shallow copy of a tool that preserves its prototype (so it stays a recognisable
131
+ * LangChain tool) while overriding one own property (its schema) with the normalized value. */
132
+ function cloneWithOverride(source, key, value) {
133
+ const clone = Object.assign(Object.create(Object.getPrototypeOf(source)), source);
134
+ Object.defineProperty(clone, key, {
135
+ value,
136
+ enumerable: true,
137
+ writable: true,
138
+ configurable: true,
139
+ });
140
+ return clone;
141
+ }
142
+ /**
143
+ * Normalise one tool's argument schema for Gemini. Handles the three shapes that reach a ChatGoogle
144
+ * model's `bindTools`: a LangChain structured tool (`.schema`, zod or JSON), an OpenAI-format tool
145
+ * (`.function.parameters`), and a Gemini-native `functionDeclarations` tool. Every schema position in
146
+ * every branch is passed through the SINGLE {@link normalizeSchema} pass; anything else is untouched.
147
+ */
148
+ function sanitizeToolForGemini(tool) {
149
+ if (!tool || typeof tool !== 'object') {
150
+ return tool;
151
+ }
152
+ const record = tool;
153
+ // LangChain structured tool / StructuredToolParams — its arg schema is `.schema` (zod or JSON).
154
+ if ('schema' in record && record.schema != null) {
155
+ return cloneWithOverride(tool, 'schema', normalizeSchema(record.schema));
156
+ }
157
+ // OpenAI-format tool: { type: 'function', function: { parameters } }.
158
+ const fn = record.function;
159
+ if (fn && typeof fn === 'object' && 'parameters' in fn) {
160
+ const fnRecord = fn;
161
+ return {
162
+ ...record,
163
+ function: { ...fnRecord, parameters: normalizeSchema(fnRecord.parameters) },
164
+ };
165
+ }
166
+ // Gemini-native tool: { functionDeclarations: [{ parameters }, …] }.
167
+ if (Array.isArray(record.functionDeclarations)) {
168
+ return {
169
+ ...record,
170
+ functionDeclarations: record.functionDeclarations.map((decl) => {
171
+ if (decl && typeof decl === 'object' && 'parameters' in decl) {
172
+ const declRecord = decl;
173
+ return { ...declRecord, parameters: normalizeSchema(declRecord.parameters) };
174
+ }
175
+ return decl;
176
+ }),
177
+ };
178
+ }
179
+ return tool;
180
+ }
181
+ /**
182
+ * Wire the sanitizer into a ChatGoogle model at the tool-binding boundary. Overrides the instance's
183
+ * `bindTools` so every tool passed to it — built-in, custom, or MCP, via `createAgent`/`createDeepAgent`
184
+ * which both call `model.bindTools(tools)` — is sanitized before it reaches `@langchain/google`'s
185
+ * Gemini converter. Provider-scoped: only the google-genai/vertexai presets call this, so no other
186
+ * provider's tools are affected. Returns the same model instance for convenient chaining.
187
+ */
188
+ export function applyGeminiToolSchemaSanitizer(model) {
189
+ const holder = model;
190
+ const original = holder.bindTools;
191
+ if (typeof original !== 'function') {
192
+ return model;
193
+ }
194
+ const bound = original.bind(model);
195
+ holder.bindTools = function sanitizedBindTools(tools, kwargs) {
196
+ const nextTools = Array.isArray(tools) ? tools.map(sanitizeToolForGemini) : tools;
197
+ return bound(nextTools, kwargs);
198
+ };
199
+ return model;
200
+ }
201
+ //# sourceMappingURL=geminiSchemaSanitizer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"geminiSchemaSanitizer.js","sourceRoot":"","sources":["../../src/providers/geminiSchemaSanitizer.ts"],"names":[],"mappings":"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"}
@@ -4,4 +4,4 @@ export declare function processJsonConfig(llmConfig: ChatGoogleParams & {
4
4
  type?: string;
5
5
  apiKeyEnvironmentVariable?: string;
6
6
  }): Promise<BaseChatModel>;
7
- export declare function init(configFileName: string): void;
7
+ export declare function init(configFileName: string, force?: boolean, model?: string): void;
@@ -1,6 +1,8 @@
1
1
  import { displayWarning } from '#src/utils/consoleUtils.js';
2
2
  import { env } from '#src/utils/systemUtils.js';
3
- import { writeFileIfNotExistsWithMessages } from '#src/utils/fileUtils.js';
3
+ import { writeConfigFileWithMessages } from '#src/utils/fileUtils.js';
4
+ import { buildInitConfigContent, getCuratedFallbackModel } from '#src/providers/modelDiscovery.js';
5
+ import { applyGeminiToolSchemaSanitizer } from '#src/providers/geminiSchemaSanitizer.js';
4
6
  // Function to process JSON config and create Google GenAI LLM instance
5
7
  export async function processJsonConfig(llmConfig) {
6
8
  const { ChatGoogle } = await import('@langchain/google/node');
@@ -9,25 +11,21 @@ export async function processJsonConfig(llmConfig) {
9
11
  const configFields = {
10
12
  ...llmConfig,
11
13
  apiKey: googleApiKey,
12
- model: llmConfig.model || 'gemini-3.5-flash',
14
+ model: llmConfig.model || getCuratedFallbackModel('google-genai'),
13
15
  platformType: 'gai',
14
16
  };
15
17
  delete configFields.type;
16
18
  delete configFields.apiKeyEnvironmentVariable;
17
- return new ChatGoogle(configFields);
19
+ // GS2-58: normalise every tool's JSON-Schema at the ChatGoogle boundary so Gemini's OpenAPI-3.0
20
+ // subset accepts built-in, custom, and MCP tools alike (see geminiSchemaSanitizer).
21
+ return applyGeminiToolSchemaSanitizer(new ChatGoogle(configFields));
18
22
  }
19
- const jsonContent = `{
20
- "llm": {
21
- "type": "google-genai",
22
- "model": "gemini-3.5-flash"
23
- }
24
- }`;
25
- export function init(configFileName) {
23
+ export function init(configFileName, force = false, model) {
26
24
  // Determine which content to use based on file extension
27
25
  if (!configFileName.endsWith('.json')) {
28
26
  throw new Error('Only JSON config is supported.');
29
27
  }
30
- writeFileIfNotExistsWithMessages(configFileName, jsonContent);
28
+ writeConfigFileWithMessages(configFileName, buildInitConfigContent('google-genai', model), force);
31
29
  displayWarning(`You need to update your ${configFileName} to add your Google GenAI API key, ` +
32
30
  'or define GOOGLE_API_KEY environment variable.');
33
31
  }
@@ -1 +1 @@
1
- {"version":3,"file":"google-genai.js","sourceRoot":"","sources":["../../src/providers/google-genai.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAC5D,OAAO,EAAE,GAAG,EAAE,MAAM,2BAA2B,CAAC;AAIhD,OAAO,EAAE,gCAAgC,EAAE,MAAM,yBAAyB,CAAC;AAE3E,uEAAuE;AACvE,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,SAAmF;IAEnF,MAAM,EAAE,UAAU,EAAE,GAAG,MAAM,MAAM,CAAC,wBAAwB,CAAC,CAAC;IAC9D,wEAAwE;IACxE,MAAM,YAAY,GAAG,SAAS,CAAC,MAAM,IAAI,GAAG,CAAC,cAAc,CAAC;IAC5D,MAAM,YAAY,GAAG;QACnB,GAAG,SAAS;QACZ,MAAM,EAAE,YAAY;QACpB,KAAK,EAAE,SAAS,CAAC,KAAK,IAAI,kBAAkB;QAC5C,YAAY,EAAE,KAAc;KAC7B,CAAC;IACF,OAAO,YAAY,CAAC,IAAI,CAAC;IACzB,OAAO,YAAY,CAAC,yBAAyB,CAAC;IAC9C,OAAO,IAAI,UAAU,CAAC,YAAY,CAAC,CAAC;AACtC,CAAC;AAED,MAAM,WAAW,GAAG;;;;;EAKlB,CAAC;AAEH,MAAM,UAAU,IAAI,CAAC,cAAsB;IACzC,yDAAyD;IACzD,IAAI,CAAC,cAAc,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;QACtC,MAAM,IAAI,KAAK,CAAC,gCAAgC,CAAC,CAAC;IACpD,CAAC;IAED,gCAAgC,CAAC,cAAc,EAAE,WAAW,CAAC,CAAC;IAC9D,cAAc,CACZ,2BAA2B,cAAc,qCAAqC;QAC5E,gDAAgD,CACnD,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"google-genai.js","sourceRoot":"","sources":["../../src/providers/google-genai.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAC5D,OAAO,EAAE,GAAG,EAAE,MAAM,2BAA2B,CAAC;AAIhD,OAAO,EAAE,2BAA2B,EAAE,MAAM,yBAAyB,CAAC;AACtE,OAAO,EAAE,sBAAsB,EAAE,uBAAuB,EAAE,MAAM,kCAAkC,CAAC;AACnG,OAAO,EAAE,8BAA8B,EAAE,MAAM,yCAAyC,CAAC;AAEzF,uEAAuE;AACvE,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,SAAmF;IAEnF,MAAM,EAAE,UAAU,EAAE,GAAG,MAAM,MAAM,CAAC,wBAAwB,CAAC,CAAC;IAC9D,wEAAwE;IACxE,MAAM,YAAY,GAAG,SAAS,CAAC,MAAM,IAAI,GAAG,CAAC,cAAc,CAAC;IAC5D,MAAM,YAAY,GAAG;QACnB,GAAG,SAAS;QACZ,MAAM,EAAE,YAAY;QACpB,KAAK,EAAE,SAAS,CAAC,KAAK,IAAI,uBAAuB,CAAC,cAAc,CAAC;QACjE,YAAY,EAAE,KAAc;KAC7B,CAAC;IACF,OAAO,YAAY,CAAC,IAAI,CAAC;IACzB,OAAO,YAAY,CAAC,yBAAyB,CAAC;IAC9C,gGAAgG;IAChG,oFAAoF;IACpF,OAAO,8BAA8B,CAAC,IAAI,UAAU,CAAC,YAAY,CAAC,CAAC,CAAC;AACtE,CAAC;AAED,MAAM,UAAU,IAAI,CAAC,cAAsB,EAAE,KAAK,GAAG,KAAK,EAAE,KAAc;IACxE,yDAAyD;IACzD,IAAI,CAAC,cAAc,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;QACtC,MAAM,IAAI,KAAK,CAAC,gCAAgC,CAAC,CAAC;IACpD,CAAC;IAED,2BAA2B,CAAC,cAAc,EAAE,sBAAsB,CAAC,cAAc,EAAE,KAAK,CAAC,EAAE,KAAK,CAAC,CAAC;IAClG,cAAc,CACZ,2BAA2B,cAAc,qCAAqC;QAC5E,gDAAgD,CACnD,CAAC;AACJ,CAAC"}
@@ -1,4 +1,4 @@
1
1
  import { BaseChatModel } from '@langchain/core/language_models/chat_models';
2
2
  import { ChatGroqInput } from '@langchain/groq';
3
3
  export declare function processJsonConfig(llmConfig: ChatGroqInput): Promise<BaseChatModel>;
4
- export declare function init(configFileName: string): void;
4
+ export declare function init(configFileName: string, force?: boolean, model?: string): void;
@@ -1,6 +1,7 @@
1
1
  import { displayWarning } from '#src/utils/consoleUtils.js';
2
2
  import { env } from '#src/utils/systemUtils.js';
3
- import { writeFileIfNotExistsWithMessages } from '#src/utils/fileUtils.js';
3
+ import { writeConfigFileWithMessages } from '#src/utils/fileUtils.js';
4
+ import { buildInitConfigContent, getCuratedFallbackModel } from '#src/providers/modelDiscovery.js';
4
5
  // Function to process JSON config and create Groq LLM instance
5
6
  export async function processJsonConfig(llmConfig) {
6
7
  const groq = await import('@langchain/groq');
@@ -9,21 +10,15 @@ export async function processJsonConfig(llmConfig) {
9
10
  return new groq.ChatGroq({
10
11
  ...llmConfig,
11
12
  apiKey: groqApiKey,
12
- model: llmConfig.model || 'openai/gpt-oss-120b',
13
+ model: llmConfig.model || getCuratedFallbackModel('groq'),
13
14
  });
14
15
  }
15
- const jsonContent = `{
16
- "llm": {
17
- "type": "groq",
18
- "model": "openai/gpt-oss-120b"
19
- }
20
- }`;
21
- export function init(configFileName) {
16
+ export function init(configFileName, force = false, model) {
22
17
  // Determine which content to use based on file extension
23
18
  if (!configFileName.endsWith('.json')) {
24
19
  throw new Error('Only JSON config is supported.');
25
20
  }
26
- writeFileIfNotExistsWithMessages(configFileName, jsonContent);
21
+ writeConfigFileWithMessages(configFileName, buildInitConfigContent('groq', model), force);
27
22
  displayWarning(`You need to edit your ${configFileName} to configure model, ` +
28
23
  'or define GROQ_API_KEY environment variable.');
29
24
  }
@@ -1 +1 @@
1
- {"version":3,"file":"groq.js","sourceRoot":"","sources":["../../src/providers/groq.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAC5D,OAAO,EAAE,GAAG,EAAE,MAAM,2BAA2B,CAAC;AAIhD,OAAO,EAAE,gCAAgC,EAAE,MAAM,yBAAyB,CAAC;AAE3E,+DAA+D;AAC/D,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,SAAwB;IAC9D,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,iBAAiB,CAAC,CAAC;IAC7C,wEAAwE;IACxE,MAAM,UAAU,GAAG,SAAS,CAAC,MAAM,IAAI,GAAG,CAAC,YAAY,CAAC;IACxD,OAAO,IAAI,IAAI,CAAC,QAAQ,CAAC;QACvB,GAAG,SAAS;QACZ,MAAM,EAAE,UAAU;QAClB,KAAK,EAAE,SAAS,CAAC,KAAK,IAAI,qBAAqB;KAChD,CAAC,CAAC;AACL,CAAC;AAED,MAAM,WAAW,GAAG;;;;;EAKlB,CAAC;AAEH,MAAM,UAAU,IAAI,CAAC,cAAsB;IACzC,yDAAyD;IACzD,IAAI,CAAC,cAAc,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;QACtC,MAAM,IAAI,KAAK,CAAC,gCAAgC,CAAC,CAAC;IACpD,CAAC;IAED,gCAAgC,CAAC,cAAc,EAAE,WAAW,CAAC,CAAC;IAC9D,cAAc,CACZ,yBAAyB,cAAc,uBAAuB;QAC5D,8CAA8C,CACjD,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"groq.js","sourceRoot":"","sources":["../../src/providers/groq.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAC5D,OAAO,EAAE,GAAG,EAAE,MAAM,2BAA2B,CAAC;AAIhD,OAAO,EAAE,2BAA2B,EAAE,MAAM,yBAAyB,CAAC;AACtE,OAAO,EAAE,sBAAsB,EAAE,uBAAuB,EAAE,MAAM,kCAAkC,CAAC;AAEnG,+DAA+D;AAC/D,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,SAAwB;IAC9D,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,iBAAiB,CAAC,CAAC;IAC7C,wEAAwE;IACxE,MAAM,UAAU,GAAG,SAAS,CAAC,MAAM,IAAI,GAAG,CAAC,YAAY,CAAC;IACxD,OAAO,IAAI,IAAI,CAAC,QAAQ,CAAC;QACvB,GAAG,SAAS;QACZ,MAAM,EAAE,UAAU;QAClB,KAAK,EAAE,SAAS,CAAC,KAAK,IAAI,uBAAuB,CAAC,MAAM,CAAC;KAC1D,CAAC,CAAC;AACL,CAAC;AAED,MAAM,UAAU,IAAI,CAAC,cAAsB,EAAE,KAAK,GAAG,KAAK,EAAE,KAAc;IACxE,yDAAyD;IACzD,IAAI,CAAC,cAAc,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;QACtC,MAAM,IAAI,KAAK,CAAC,gCAAgC,CAAC,CAAC;IACpD,CAAC;IAED,2BAA2B,CAAC,cAAc,EAAE,sBAAsB,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,KAAK,CAAC,CAAC;IAC1F,cAAc,CACZ,yBAAyB,cAAc,uBAAuB;QAC5D,8CAA8C,CACjD,CAAC;AACJ,CAAC"}
@@ -0,0 +1,25 @@
1
+ import { BaseChatModel, type BaseChatModelParams } from '@langchain/core/language_models/chat_models';
2
+ import { OpenAIChatInput } from '@langchain/openai';
3
+ import { ChatOpenAIFields } from '@langchain/openai';
4
+ /**
5
+ * Hugging Face Inference Providers — OpenAI-compatible router.
6
+ *
7
+ * HF exposes a single OpenAI-compatible Chat Completions endpoint at
8
+ * `https://router.huggingface.co/v1` that fans requests out to the configured
9
+ * inference provider (Cerebras, Groq, Together, SambaNova, hf-inference, …). It
10
+ * supports tool/function calling, streaming and structured outputs, so we reuse
11
+ * LangChain's `ChatOpenAI` exactly like the OpenRouter provider — no dedicated
12
+ * `@langchain/huggingface` package is needed (the JS `@langchain/community`
13
+ * `HuggingFaceInference` is a text-completion LLM with no `bindTools`, so it is
14
+ * unusable for the agent loop).
15
+ *
16
+ * Auth uses a Hugging Face user access token with the "Inference Providers"
17
+ * permission, read from `HF_TOKEN` (canonical) or the `HUGGINGFACEHUB_API_TOKEN`
18
+ * / `HF_API_KEY` aliases.
19
+ *
20
+ * Model ids are Hub repo ids, e.g. `openai/gpt-oss-120b`. You may append a
21
+ * provider/policy suffix understood by the router (`:groq`, `:cheapest`,
22
+ * `:fastest`); it is part of the model id and passes straight through.
23
+ */
24
+ export declare function processJsonConfig(llmConfig: OpenAIChatInput & ChatOpenAIFields & BaseChatModelParams): Promise<BaseChatModel>;
25
+ export declare function init(configFileName: string, force?: boolean, model?: string): void;
@@ -0,0 +1,69 @@
1
+ import { displayWarning } from '#src/utils/consoleUtils.js';
2
+ import { env } from '#src/utils/systemUtils.js';
3
+ import { writeConfigFileWithMessages } from '#src/utils/fileUtils.js';
4
+ import { buildInitConfigContent, getCuratedFallbackModel } from '#src/providers/modelDiscovery.js';
5
+ /**
6
+ * Hugging Face Inference Providers — OpenAI-compatible router.
7
+ *
8
+ * HF exposes a single OpenAI-compatible Chat Completions endpoint at
9
+ * `https://router.huggingface.co/v1` that fans requests out to the configured
10
+ * inference provider (Cerebras, Groq, Together, SambaNova, hf-inference, …). It
11
+ * supports tool/function calling, streaming and structured outputs, so we reuse
12
+ * LangChain's `ChatOpenAI` exactly like the OpenRouter provider — no dedicated
13
+ * `@langchain/huggingface` package is needed (the JS `@langchain/community`
14
+ * `HuggingFaceInference` is a text-completion LLM with no `bindTools`, so it is
15
+ * unusable for the agent loop).
16
+ *
17
+ * Auth uses a Hugging Face user access token with the "Inference Providers"
18
+ * permission, read from `HF_TOKEN` (canonical) or the `HUGGINGFACEHUB_API_TOKEN`
19
+ * / `HF_API_KEY` aliases.
20
+ *
21
+ * Model ids are Hub repo ids, e.g. `openai/gpt-oss-120b`. You may append a
22
+ * provider/policy suffix understood by the router (`:groq`, `:cheapest`,
23
+ * `:fastest`); it is part of the model id and passes straight through.
24
+ */
25
+ // Function to process JSON config and create Hugging Face LLM instance
26
+ // noinspection JSUnusedGlobalSymbols
27
+ export async function processJsonConfig(llmConfig) {
28
+ const { ChatOpenAI } = await import('@langchain/openai');
29
+ // Use environment variable if available, otherwise use the config value
30
+ const huggingFaceToken = getApiKey(llmConfig);
31
+ if (!huggingFaceToken) {
32
+ throw new Error('You need to define HF_TOKEN environment variable (a Hugging Face access token ' +
33
+ 'with the "Inference Providers" permission), or set apiKey in your config file.');
34
+ }
35
+ const configFields = {
36
+ ...llmConfig,
37
+ apiKey: huggingFaceToken,
38
+ model: llmConfig.model || getCuratedFallbackModel('huggingface'),
39
+ configuration: {
40
+ baseURL: 'https://router.huggingface.co/v1',
41
+ ...(llmConfig.configuration || {}),
42
+ },
43
+ };
44
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
45
+ delete configFields.type;
46
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
47
+ delete configFields.apiKeyEnvironmentVariable;
48
+ return new ChatOpenAI(configFields);
49
+ }
50
+ function getApiKey(llmConfig) {
51
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
52
+ const conf = llmConfig;
53
+ if (conf.apiKeyEnvironmentVariable && env[conf.apiKeyEnvironmentVariable]) {
54
+ return env[conf.apiKeyEnvironmentVariable];
55
+ }
56
+ else {
57
+ return llmConfig.apiKey || env.HF_TOKEN || env.HUGGINGFACEHUB_API_TOKEN || env.HF_API_KEY;
58
+ }
59
+ }
60
+ export function init(configFileName, force = false, model) {
61
+ // Determine which content to use based on file extension
62
+ if (!configFileName.endsWith('.json')) {
63
+ throw new Error('Only JSON config is supported.');
64
+ }
65
+ writeConfigFileWithMessages(configFileName, buildInitConfigContent('huggingface', model), force);
66
+ displayWarning(`You need to edit your ${configFileName} to configure model, ` +
67
+ 'or define HF_TOKEN environment variable.');
68
+ }
69
+ //# sourceMappingURL=huggingface.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"huggingface.js","sourceRoot":"","sources":["../../src/providers/huggingface.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAC5D,OAAO,EAAE,GAAG,EAAE,MAAM,2BAA2B,CAAC;AAQhD,OAAO,EAAE,2BAA2B,EAAE,MAAM,yBAAyB,CAAC;AACtE,OAAO,EAAE,sBAAsB,EAAE,uBAAuB,EAAE,MAAM,kCAAkC,CAAC;AAEnG;;;;;;;;;;;;;;;;;;;GAmBG;AACH,uEAAuE;AACvE,qCAAqC;AACrC,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,SAAmE;IAEnE,MAAM,EAAE,UAAU,EAAE,GAAG,MAAM,MAAM,CAAC,mBAAmB,CAAC,CAAC;IACzD,wEAAwE;IACxE,MAAM,gBAAgB,GAAG,SAAS,CAAC,SAAS,CAAC,CAAC;IAC9C,IAAI,CAAC,gBAAgB,EAAE,CAAC;QACtB,MAAM,IAAI,KAAK,CACb,gFAAgF;YAC9E,gFAAgF,CACnF,CAAC;IACJ,CAAC;IACD,MAAM,YAAY,GAAG;QACnB,GAAG,SAAS;QACZ,MAAM,EAAE,gBAAgB;QACxB,KAAK,EAAE,SAAS,CAAC,KAAK,IAAI,uBAAuB,CAAC,aAAa,CAAC;QAChE,aAAa,EAAE;YACb,OAAO,EAAE,kCAAkC;YAC3C,GAAG,CAAC,SAAS,CAAC,aAAa,IAAI,EAAE,CAAC;SACnC;KACF,CAAC;IACF,8DAA8D;IAC9D,OAAQ,YAAoB,CAAC,IAAI,CAAC;IAClC,8DAA8D;IAC9D,OAAQ,YAAoB,CAAC,yBAAyB,CAAC;IACvD,OAAO,IAAI,UAAU,CAAC,YAAY,CAAC,CAAC;AACtC,CAAC;AAED,SAAS,SAAS,CAAC,SAAmE;IACpF,8DAA8D;IAC9D,MAAM,IAAI,GAAG,SAA0C,CAAC;IACxD,IAAI,IAAI,CAAC,yBAAyB,IAAI,GAAG,CAAC,IAAI,CAAC,yBAAyB,CAAC,EAAE,CAAC;QAC1E,OAAO,GAAG,CAAC,IAAI,CAAC,yBAAyB,CAAC,CAAC;IAC7C,CAAC;SAAM,CAAC;QACN,OAAO,SAAS,CAAC,MAAM,IAAI,GAAG,CAAC,QAAQ,IAAI,GAAG,CAAC,wBAAwB,IAAI,GAAG,CAAC,UAAU,CAAC;IAC5F,CAAC;AACH,CAAC;AAED,MAAM,UAAU,IAAI,CAAC,cAAsB,EAAE,KAAK,GAAG,KAAK,EAAE,KAAc;IACxE,yDAAyD;IACzD,IAAI,CAAC,cAAc,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;QACtC,MAAM,IAAI,KAAK,CAAC,gCAAgC,CAAC,CAAC;IACpD,CAAC;IAED,2BAA2B,CAAC,cAAc,EAAE,sBAAsB,CAAC,aAAa,EAAE,KAAK,CAAC,EAAE,KAAK,CAAC,CAAC;IACjG,cAAc,CACZ,yBAAyB,cAAc,uBAAuB;QAC5D,0CAA0C,CAC7C,CAAC;AACJ,CAAC"}