@gaunt-sloth/core 2.0.0-alpha.8 → 2.0.0-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.gsloth.review.md +2 -0
- package/README.md +71 -20
- package/dist/config/colour.d.ts +38 -0
- package/dist/config/colour.js +36 -0
- package/dist/config/colour.js.map +1 -0
- package/dist/config/configDiscovery.d.ts +79 -0
- package/dist/config/configDiscovery.js +80 -0
- package/dist/config/configDiscovery.js.map +1 -0
- package/dist/config/defaults.d.ts +20 -20
- package/dist/config/defaults.js +10 -8
- package/dist/config/defaults.js.map +1 -1
- package/dist/config/filesystem-tools.d.ts +41 -0
- package/dist/config/filesystem-tools.js +56 -0
- package/dist/config/filesystem-tools.js.map +1 -0
- package/dist/config/loader.d.ts +171 -19
- package/dist/config/loader.js +1054 -144
- package/dist/config/loader.js.map +1 -1
- package/dist/config/mouse.d.ts +50 -0
- package/dist/config/mouse.js +44 -0
- package/dist/config/mouse.js.map +1 -0
- package/dist/config/profiles.d.ts +68 -0
- package/dist/config/profiles.js +93 -0
- package/dist/config/profiles.js.map +1 -0
- package/dist/config/providerKeys.d.ts +69 -0
- package/dist/config/providerKeys.js +69 -0
- package/dist/config/providerKeys.js.map +1 -0
- package/dist/config/schema.d.ts +2671 -138
- package/dist/config/schema.js +1361 -85
- package/dist/config/schema.js.map +1 -1
- package/dist/config/shell-policy.d.ts +899 -111
- package/dist/config/shell-policy.js +800 -70
- package/dist/config/shell-policy.js.map +1 -1
- package/dist/config/tool-descriptions.d.ts +211 -0
- package/dist/config/tool-descriptions.js +272 -0
- package/dist/config/tool-descriptions.js.map +1 -0
- package/dist/config/types.d.ts +352 -41
- package/dist/config/types.js +1 -0
- package/dist/config/types.js.map +1 -1
- package/dist/config.d.ts +35 -1
- package/dist/config.js +16 -1
- package/dist/config.js.map +1 -1
- package/dist/constants.d.ts +28 -1
- package/dist/constants.js +28 -1
- package/dist/constants.js.map +1 -1
- package/dist/core/GthAbstractAgent.d.ts +166 -11
- package/dist/core/GthAbstractAgent.js +484 -45
- package/dist/core/GthAbstractAgent.js.map +1 -1
- package/dist/core/GthAgentRunner.d.ts +543 -57
- package/dist/core/GthAgentRunner.js +1494 -140
- package/dist/core/GthAgentRunner.js.map +1 -1
- package/dist/core/GthLangChainAgent.d.ts +117 -2
- package/dist/core/GthLangChainAgent.js +602 -29
- package/dist/core/GthLangChainAgent.js.map +1 -1
- package/dist/core/approvals/annotations.d.ts +122 -0
- package/dist/core/approvals/annotations.js +137 -0
- package/dist/core/approvals/annotations.js.map +1 -0
- package/dist/core/approvals/grants.d.ts +216 -0
- package/dist/core/approvals/grants.js +469 -0
- package/dist/core/approvals/grants.js.map +1 -0
- package/dist/core/approvals/matcher.d.ts +202 -0
- package/dist/core/approvals/matcher.js +267 -0
- package/dist/core/approvals/matcher.js.map +1 -0
- package/dist/core/approvals/mcpSubjects.d.ts +40 -0
- package/dist/core/approvals/mcpSubjects.js +99 -0
- package/dist/core/approvals/mcpSubjects.js.map +1 -0
- package/dist/core/approvals/promptHeader.d.ts +28 -0
- package/dist/core/approvals/promptHeader.js +62 -0
- package/dist/core/approvals/promptHeader.js.map +1 -0
- package/dist/core/approvals/toolAnnotationSources.d.ts +105 -0
- package/dist/core/approvals/toolAnnotationSources.js +277 -0
- package/dist/core/approvals/toolAnnotationSources.js.map +1 -0
- package/dist/core/approvals/toolHost.d.ts +46 -0
- package/dist/core/approvals/toolHost.js +108 -0
- package/dist/core/approvals/toolHost.js.map +1 -0
- package/dist/core/debugCapture.d.ts +74 -0
- package/dist/core/debugCapture.js +100 -0
- package/dist/core/debugCapture.js.map +1 -0
- package/dist/core/gthLeanAgentFactory.d.ts +4 -4
- package/dist/core/gthLeanAgentFactory.js +4 -4
- package/dist/core/launchBanner.d.ts +127 -0
- package/dist/core/launchBanner.js +414 -0
- package/dist/core/launchBanner.js.map +1 -0
- package/dist/core/modelLabel.d.ts +19 -0
- package/dist/core/modelLabel.js +26 -0
- package/dist/core/modelLabel.js.map +1 -0
- package/dist/core/plainToolIndication.d.ts +15 -0
- package/dist/core/plainToolIndication.js +174 -0
- package/dist/core/plainToolIndication.js.map +1 -0
- package/dist/core/reasoningBlocks.d.ts +65 -0
- package/dist/core/reasoningBlocks.js +103 -0
- package/dist/core/reasoningBlocks.js.map +1 -0
- package/dist/core/refusal.d.ts +53 -0
- package/dist/core/refusal.js +133 -0
- package/dist/core/refusal.js.map +1 -0
- package/dist/core/runHeader.d.ts +38 -0
- package/dist/core/runHeader.js +42 -0
- package/dist/core/runHeader.js.map +1 -0
- package/dist/core/runStats.d.ts +14 -3
- package/dist/core/runStats.js +48 -3
- package/dist/core/runStats.js.map +1 -1
- package/dist/core/shell/ShellCommandFailedError.d.ts +3 -4
- package/dist/core/shell/ShellCommandFailedError.js +3 -4
- package/dist/core/shell/ShellCommandFailedError.js.map +1 -1
- package/dist/core/shell/abstention.d.ts +88 -0
- package/dist/core/shell/abstention.js +184 -0
- package/dist/core/shell/abstention.js.map +1 -0
- package/dist/core/shell/approvalCapture.d.ts +271 -0
- package/dist/core/shell/approvalCapture.js +108 -0
- package/dist/core/shell/approvalCapture.js.map +1 -0
- package/dist/core/shell/approvalStop.d.ts +123 -0
- package/dist/core/shell/approvalStop.js +269 -0
- package/dist/core/shell/approvalStop.js.map +1 -0
- package/dist/core/shell/arity.d.ts +6 -0
- package/dist/core/shell/arity.js +20 -6
- package/dist/core/shell/arity.js.map +1 -1
- package/dist/core/shell/denylist.d.ts +11 -0
- package/dist/core/shell/denylist.js +37 -0
- package/dist/core/shell/denylist.js.map +1 -0
- package/dist/core/shell/escalationSeverity.d.ts +141 -0
- package/dist/core/shell/escalationSeverity.js +89 -0
- package/dist/core/shell/escalationSeverity.js.map +1 -0
- package/dist/core/shell/framing.d.ts +190 -0
- package/dist/core/shell/framing.js +633 -0
- package/dist/core/shell/framing.js.map +1 -0
- package/dist/core/shell/hardline.d.ts +103 -0
- package/dist/core/shell/hardline.js +780 -0
- package/dist/core/shell/hardline.js.map +1 -0
- package/dist/core/shell/negotiation.d.ts +328 -0
- package/dist/core/shell/negotiation.js +488 -0
- package/dist/core/shell/negotiation.js.map +1 -0
- package/dist/core/shell/normalize.d.ts +44 -4
- package/dist/core/shell/normalize.js +61 -7
- package/dist/core/shell/normalize.js.map +1 -1
- package/dist/core/shell/openWorld.d.ts +263 -0
- package/dist/core/shell/openWorld.js +1188 -0
- package/dist/core/shell/openWorld.js.map +1 -0
- package/dist/core/shell/rater.d.ts +873 -0
- package/dist/core/shell/rater.js +1454 -0
- package/dist/core/shell/rater.js.map +1 -0
- package/dist/core/shell/raterModel.d.ts +41 -0
- package/dist/core/shell/raterModel.js +51 -0
- package/dist/core/shell/raterModel.js.map +1 -0
- package/dist/core/shell/raterVocabulary.d.ts +121 -0
- package/dist/core/shell/raterVocabulary.js +116 -0
- package/dist/core/shell/raterVocabulary.js.map +1 -0
- package/dist/core/shell/rejection.d.ts +69 -0
- package/dist/core/shell/rejection.js +38 -0
- package/dist/core/shell/rejection.js.map +1 -0
- package/dist/core/toolCallRepair/grammar.d.ts +41 -0
- package/dist/core/toolCallRepair/grammar.js +116 -0
- package/dist/core/toolCallRepair/grammar.js.map +1 -0
- package/dist/core/toolCallRepair/index.d.ts +2 -0
- package/dist/core/toolCallRepair/index.js +7 -0
- package/dist/core/toolCallRepair/index.js.map +1 -0
- package/dist/core/toolCallRepair/payload.d.ts +36 -0
- package/dist/core/toolCallRepair/payload.js +341 -0
- package/dist/core/toolCallRepair/payload.js.map +1 -0
- package/dist/core/toolCallRepair/promote.d.ts +45 -0
- package/dist/core/toolCallRepair/promote.js +90 -0
- package/dist/core/toolCallRepair/promote.js.map +1 -0
- package/dist/core/toolDisplay.d.ts +123 -0
- package/dist/core/toolDisplay.js +451 -0
- package/dist/core/toolDisplay.js.map +1 -0
- package/dist/core/toolOutputChannel.d.ts +95 -0
- package/dist/core/toolOutputChannel.js +165 -0
- package/dist/core/toolOutputChannel.js.map +1 -0
- package/dist/core/types.d.ts +349 -16
- package/dist/core/types.js.map +1 -1
- package/dist/history/historyFormat.d.ts +12 -3
- package/dist/history/historyFormat.js +50 -8
- package/dist/history/historyFormat.js.map +1 -1
- package/dist/history/historyStore.d.ts +77 -0
- package/dist/history/historyStore.js +173 -6
- package/dist/history/historyStore.js.map +1 -1
- package/dist/history/recordSession.d.ts +10 -1
- package/dist/history/recordSession.js +27 -0
- package/dist/history/recordSession.js.map +1 -1
- package/dist/providers/anthropic.js +12 -0
- package/dist/providers/anthropic.js.map +1 -1
- package/dist/providers/configurationPassthrough.d.ts +107 -0
- package/dist/providers/configurationPassthrough.js +148 -0
- package/dist/providers/configurationPassthrough.js.map +1 -0
- package/dist/providers/geminiSchemaSanitizer.d.ts +55 -0
- package/dist/providers/geminiSchemaSanitizer.js +347 -0
- package/dist/providers/geminiSchemaSanitizer.js.map +1 -0
- package/dist/providers/geminiThinking.d.ts +60 -0
- package/dist/providers/geminiThinking.js +92 -0
- package/dist/providers/geminiThinking.js.map +1 -0
- package/dist/providers/google-genai.js +18 -1
- package/dist/providers/google-genai.js.map +1 -1
- package/dist/providers/groq.js +12 -0
- package/dist/providers/groq.js.map +1 -1
- package/dist/providers/huggingface.d.ts +25 -0
- package/dist/providers/huggingface.js +69 -0
- package/dist/providers/huggingface.js.map +1 -0
- package/dist/providers/modelCatalog.d.ts +109 -0
- package/dist/providers/modelCatalog.js +245 -0
- package/dist/providers/modelCatalog.js.map +1 -0
- package/dist/providers/modelDiscovery.d.ts +99 -5
- package/dist/providers/modelDiscovery.js +191 -35
- package/dist/providers/modelDiscovery.js.map +1 -1
- package/dist/providers/ollama.d.ts +18 -4
- package/dist/providers/ollama.js +67 -37
- package/dist/providers/ollama.js.map +1 -1
- package/dist/providers/openai.js +34 -0
- package/dist/providers/openai.js.map +1 -1
- package/dist/providers/openrouter.d.ts +26 -4
- package/dist/providers/openrouter.js +83 -26
- package/dist/providers/openrouter.js.map +1 -1
- package/dist/providers/vertexai.js +19 -1
- package/dist/providers/vertexai.js.map +1 -1
- package/dist/providers/xai.js +20 -0
- package/dist/providers/xai.js.map +1 -1
- package/dist/runtime/askStructured.d.ts +105 -0
- package/dist/runtime/askStructured.js +120 -0
- package/dist/runtime/askStructured.js.map +1 -0
- package/dist/runtime/conversation.d.ts +64 -0
- package/dist/runtime/conversation.js +171 -0
- package/dist/runtime/conversation.js.map +1 -0
- package/dist/runtime/singleShot.d.ts +37 -6
- package/dist/runtime/singleShot.js +113 -67
- package/dist/runtime/singleShot.js.map +1 -1
- package/dist/runtime/structuredOutput.d.ts +104 -0
- package/dist/runtime/structuredOutput.js +393 -0
- package/dist/runtime/structuredOutput.js.map +1 -0
- package/dist/utils/ProgressIndicator.d.ts +21 -0
- package/dist/utils/ProgressIndicator.js +30 -3
- package/dist/utils/ProgressIndicator.js.map +1 -1
- package/dist/utils/aiignoreUtils.js.map +1 -1
- package/dist/utils/binaryOutputUtils.js.map +1 -1
- package/dist/utils/consoleUtils.d.ts +95 -0
- package/dist/utils/consoleUtils.js +112 -2
- package/dist/utils/consoleUtils.js.map +1 -1
- package/dist/utils/crashHandler.d.ts +87 -0
- package/dist/utils/crashHandler.js +128 -0
- package/dist/utils/crashHandler.js.map +1 -0
- package/dist/utils/debugDump.d.ts +134 -0
- package/dist/utils/debugDump.js +381 -0
- package/dist/utils/debugDump.js.map +1 -0
- package/dist/utils/debugUtils.d.ts +13 -4
- package/dist/utils/debugUtils.js +36 -13
- package/dist/utils/debugUtils.js.map +1 -1
- package/dist/utils/displayWidth.d.ts +53 -0
- package/dist/utils/displayWidth.js +195 -0
- package/dist/utils/displayWidth.js.map +1 -0
- package/dist/utils/fileUtils.js.map +1 -1
- package/dist/utils/globalConfigUtils.d.ts +14 -2
- package/dist/utils/globalConfigUtils.js +22 -4
- package/dist/utils/globalConfigUtils.js.map +1 -1
- package/dist/utils/llmUtils.d.ts +39 -8
- package/dist/utils/llmUtils.js +76 -8
- package/dist/utils/llmUtils.js.map +1 -1
- package/dist/utils/redactSecrets.d.ts +63 -0
- package/dist/utils/redactSecrets.js +286 -0
- package/dist/utils/redactSecrets.js.map +1 -0
- package/dist/utils/systemPromptNotes.d.ts +222 -0
- package/dist/utils/systemPromptNotes.js +338 -0
- package/dist/utils/systemPromptNotes.js.map +1 -0
- package/dist/utils/systemUtils.d.ts +18 -1
- package/dist/utils/systemUtils.js +38 -3
- package/dist/utils/systemUtils.js.map +1 -1
- package/dist/utils/toolMatching.d.ts +30 -0
- package/dist/utils/toolMatching.js +44 -0
- package/dist/utils/toolMatching.js.map +1 -0
- package/dist/utils/untrustedText.d.ts +86 -0
- package/dist/utils/untrustedText.js +101 -0
- package/dist/utils/untrustedText.js.map +1 -0
- package/package.json +21 -7
- package/schema/gsloth-config.schema.json +1921 -326
- package/dist/core/shell/allowlist.d.ts +0 -75
- package/dist/core/shell/allowlist.js +0 -187
- package/dist/core/shell/allowlist.js.map +0 -1
- package/dist/core/shell/judge.d.ts +0 -161
- package/dist/core/shell/judge.js +0 -261
- package/dist/core/shell/judge.js.map +0 -1
package/dist/providers/ollama.js
CHANGED
|
@@ -2,59 +2,89 @@ import { displayWarning } from '#src/utils/consoleUtils.js';
|
|
|
2
2
|
import { env } from '#src/utils/systemUtils.js';
|
|
3
3
|
import { writeConfigFileWithMessages } from '#src/utils/fileUtils.js';
|
|
4
4
|
import { buildInitConfigContent, getCuratedFallbackModel } from '#src/providers/modelDiscovery.js';
|
|
5
|
+
import { NATIVE_CLIENT_REASON, warnUnusedConfiguration, } from '#src/providers/configurationPassthrough.js';
|
|
5
6
|
/**
|
|
6
|
-
* Default Ollama daemon host, matching the Ollama CLI/library default.
|
|
7
|
-
*
|
|
8
|
-
*
|
|
7
|
+
* Default Ollama daemon host, matching the Ollama CLI/library default. GS2-59 — `ChatOllama` talks
|
|
8
|
+
* to the daemon's NATIVE endpoint (`/api/chat`), NOT the OpenAI-compatible `/v1` shim this provider
|
|
9
|
+
* used before. The same root host is reused by `modelDiscovery.ts`'s model picker, which still
|
|
10
|
+
* queries the `/v1/models` surface (discovery only); both derive from this root — they stay in sync
|
|
11
|
+
* at the host level even though the two use different endpoints under it.
|
|
9
12
|
*/
|
|
10
13
|
const DEFAULT_OLLAMA_HOST = 'http://127.0.0.1:11434';
|
|
11
14
|
/**
|
|
12
|
-
* Ollama
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
+
* GS2-59 — default context window (`num_ctx`) for Ollama models. Ollama's OWN default is 4096, but
|
|
16
|
+
* gaunt-sloth's agentic prompt (system + full lean toolset + a tool result) already lands ~4000
|
|
17
|
+
* tokens; at 4096 a thinking model (e.g. gemma4:31b) spends its entire remaining budget on the
|
|
18
|
+
* reasoning field and emits EMPTY `content` on the turn after a tool executes — the GS2-59
|
|
19
|
+
* blank-answer regression. The previous `ChatOpenAI`→`/v1` path could not fix this because the
|
|
20
|
+
* OpenAI-compat shim IGNORES `num_ctx`; the native `/api/chat` path honors it.
|
|
21
|
+
*
|
|
22
|
+
* 16384 is chosen as the largest window that is BOTH safely above the ~4000-token starvation point
|
|
23
|
+
* (4× headroom for reasoning + a few tool results) AND fits constrained consumer VRAM: Ollama
|
|
24
|
+
* preallocates the KV cache at `num_ctx`, so on a box where a large model already spills partly to
|
|
25
|
+
* CPU (e.g. a 19GB model on ~18GB of GPU), a 32768 cache tips the GPU allocation into an
|
|
26
|
+
* out-of-memory error. 16384 was verified live to run the agentic tool→synthesis turn on such a
|
|
27
|
+
* box; 32768 OOM'd it. Overridable per config via `llm.numCtx` — raise it if you have the VRAM and
|
|
28
|
+
* run long sessions, lower it on very tight hardware. NOTE: a per-request `num_ctx` overrides the
|
|
29
|
+
* daemon's `OLLAMA_CONTEXT_LENGTH`, so a user who tuned their server window higher should set
|
|
30
|
+
* `llm.numCtx` to match rather than rely on the server default.
|
|
15
31
|
*/
|
|
16
|
-
const
|
|
32
|
+
const DEFAULT_OLLAMA_NUM_CTX = 16384;
|
|
17
33
|
/**
|
|
18
|
-
* Resolve the
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
* `
|
|
22
|
-
* `host:port`; either form is normalized to a `http(s)://host[:port]/v1` base.
|
|
34
|
+
* Resolve the base URL for the local Ollama daemon — the NATIVE root (no `/v1` suffix; `ChatOllama`
|
|
35
|
+
* appends its own `/api/chat` path). Honors the `OLLAMA_HOST` env override (the same variable the
|
|
36
|
+
* Ollama CLI uses): a full URL (`http://127.0.0.1:11434`) or a bare `host:port` is normalized to
|
|
37
|
+
* `http(s)://host[:port]` with any trailing slash stripped.
|
|
23
38
|
*/
|
|
24
39
|
function resolveBaseUrl() {
|
|
25
40
|
const host = env.OLLAMA_HOST;
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
else if (/^https?:\/\//.test(host)) {
|
|
31
|
-
base = host.replace(/\/+$/, '');
|
|
32
|
-
}
|
|
33
|
-
else {
|
|
34
|
-
base = `http://${host}`.replace(/\/+$/, '');
|
|
35
|
-
}
|
|
36
|
-
return `${base}/v1`;
|
|
41
|
+
if (!host)
|
|
42
|
+
return DEFAULT_OLLAMA_HOST;
|
|
43
|
+
const base = /^https?:\/\//.test(host) ? host : `http://${host}`;
|
|
44
|
+
return base.replace(/\/+$/, '');
|
|
37
45
|
}
|
|
38
|
-
|
|
46
|
+
/**
|
|
47
|
+
* GS2-59 — build a native `ChatOllama` client (`@langchain/ollama`, talking to Ollama's
|
|
48
|
+
* `/api/chat`). This replaces the previous `ChatOpenAI`→`/v1` client, which had two fatal
|
|
49
|
+
* properties for local thinking models: it DROPPED the model's separate reasoning field (the
|
|
50
|
+
* completions converter discards it — `__includeRawResponse` is openrouter-only), and it IGNORED
|
|
51
|
+
* `num_ctx` so a large agentic prompt starved the answer to empty content. `ChatOllama` fixes both:
|
|
52
|
+
* it honors `numCtx`, and it surfaces the model's thinking in `additional_kwargs.reasoning_content`
|
|
53
|
+
* — the exact field the reasoning pipeline (`pickReasoningDelta`) already reads — so the `/reasoning`
|
|
54
|
+
* panel populates with no downstream change.
|
|
55
|
+
*
|
|
56
|
+
* Ollama is a local, unauthenticated daemon, so the old OpenAI-client knobs are gone (2.0 migration
|
|
57
|
+
* note): point elsewhere with `OLLAMA_HOST`; the previous `configuration.baseURL` / placeholder
|
|
58
|
+
* `apiKey` fields no longer apply. If a reverse proxy in front of Ollama needs auth, set `headers`
|
|
59
|
+
* in the config (passed straight through to `ChatOllama`).
|
|
60
|
+
*/
|
|
39
61
|
// noinspection JSUnusedGlobalSymbols
|
|
40
62
|
export async function processJsonConfig(llmConfig) {
|
|
41
|
-
const {
|
|
42
|
-
// Ollama is local and unauthenticated; ChatOpenAI still needs a non-empty key.
|
|
43
|
-
const apiKey = llmConfig.apiKey || OLLAMA_PLACEHOLDER_API_KEY;
|
|
63
|
+
const { ChatOllama } = await import('@langchain/ollama');
|
|
44
64
|
const configFields = {
|
|
45
65
|
...llmConfig,
|
|
46
|
-
apiKey,
|
|
47
66
|
model: llmConfig.model || getCuratedFallbackModel('ollama'),
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
67
|
+
// Precedence: an explicit config `baseUrl` (the native ChatOllama field) wins, else the
|
|
68
|
+
// `OLLAMA_HOST` env, else the local default. Config is the more specific/intentional signal.
|
|
69
|
+
baseUrl: llmConfig.baseUrl ?? resolveBaseUrl(),
|
|
70
|
+
numCtx: llmConfig.numCtx ?? DEFAULT_OLLAMA_NUM_CTX,
|
|
52
71
|
};
|
|
53
|
-
//
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
72
|
+
// `ChatOllama` is a native client, so NOTHING in a `configuration` block reaches it — say so
|
|
73
|
+
// before dropping it, rather than letting a stale config behave differently than it reads.
|
|
74
|
+
warnUnusedConfiguration({
|
|
75
|
+
provider: 'ollama',
|
|
76
|
+
configuration: configFields.configuration,
|
|
77
|
+
consumedPaths: [],
|
|
78
|
+
reason: NATIVE_CLIENT_REASON,
|
|
79
|
+
guidance: 'Ollama runs as a local, unauthenticated daemon: point elsewhere with the OLLAMA_HOST ' +
|
|
80
|
+
'environment variable or the "baseUrl" field of the "llm" block, and set "headers" there ' +
|
|
81
|
+
'if a reverse proxy in front of it needs auth.',
|
|
82
|
+
});
|
|
83
|
+
// Strip OpenAI-client / gaunt-sloth-internal keys ChatOllama neither needs nor understands.
|
|
84
|
+
for (const key of ['type', 'apiKeyEnvironmentVariable', 'apiKey', 'configuration']) {
|
|
85
|
+
delete configFields[key];
|
|
86
|
+
}
|
|
87
|
+
return new ChatOllama(configFields);
|
|
58
88
|
}
|
|
59
89
|
export function init(configFileName, force = false, model) {
|
|
60
90
|
// Determine which content to use based on file extension
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ollama.js","sourceRoot":"","sources":["../../src/providers/ollama.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAC5D,OAAO,EAAE,GAAG,EAAE,MAAM,2BAA2B,CAAC;
|
|
1
|
+
{"version":3,"file":"ollama.js","sourceRoot":"","sources":["../../src/providers/ollama.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;AACnG,OAAO,EACL,oBAAoB,EACpB,uBAAuB,GACxB,MAAM,4CAA4C,CAAC;AAEpD;;;;;;GAMG;AACH,MAAM,mBAAmB,GAAG,wBAAwB,CAAC;AAErD;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,sBAAsB,GAAG,KAAK,CAAC;AAErC;;;;;GAKG;AACH,SAAS,cAAc;IACrB,MAAM,IAAI,GAAG,GAAG,CAAC,WAAW,CAAC;IAC7B,IAAI,CAAC,IAAI;QAAE,OAAO,mBAAmB,CAAC;IACtC,MAAM,IAAI,GAAG,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,UAAU,IAAI,EAAE,CAAC;IACjE,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;AAClC,CAAC;AAED;;;;;;;;;;;;;;GAcG;AACH,qCAAqC;AACrC,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,SAAgD;IAEhD,MAAM,EAAE,UAAU,EAAE,GAAG,MAAM,MAAM,CAAC,mBAAmB,CAAC,CAAC;IACzD,MAAM,YAAY,GAA4B;QAC5C,GAAG,SAAS;QACZ,KAAK,EAAE,SAAS,CAAC,KAAK,IAAI,uBAAuB,CAAC,QAAQ,CAAC;QAC3D,wFAAwF;QACxF,6FAA6F;QAC7F,OAAO,EAAE,SAAS,CAAC,OAAO,IAAI,cAAc,EAAE;QAC9C,MAAM,EAAE,SAAS,CAAC,MAAM,IAAI,sBAAsB;KACnD,CAAC;IACF,6FAA6F;IAC7F,2FAA2F;IAC3F,uBAAuB,CAAC;QACtB,QAAQ,EAAE,QAAQ;QAClB,aAAa,EAAE,YAAY,CAAC,aAAa;QACzC,aAAa,EAAE,EAAE;QACjB,MAAM,EAAE,oBAAoB;QAC5B,QAAQ,EACN,uFAAuF;YACvF,0FAA0F;YAC1F,+CAA+C;KAClD,CAAC,CAAC;IACH,4FAA4F;IAC5F,KAAK,MAAM,GAAG,IAAI,CAAC,MAAM,EAAE,2BAA2B,EAAE,QAAQ,EAAE,eAAe,CAAC,EAAE,CAAC;QACnF,OAAO,YAAY,CAAC,GAAG,CAAC,CAAC;IAC3B,CAAC;IACD,OAAO,IAAI,UAAU,CAAC,YAA+B,CAAC,CAAC;AACzD,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,QAAQ,EAAE,KAAK,CAAC,EAAE,KAAK,CAAC,CAAC;IAC5F,cAAc,CACZ,yBAAyB,cAAc,2BAA2B;QAChE,2EAA2E;QAC3E,+CAA+C,CAClD,CAAC;AACJ,CAAC"}
|
package/dist/providers/openai.js
CHANGED
|
@@ -16,6 +16,35 @@ const SUPPORTED_DEFAULT_TEMPERATURE = 1;
|
|
|
16
16
|
function isTemperatureRestrictedModel(model) {
|
|
17
17
|
return !!model && TEMPERATURE_RESTRICTED_MODEL.test(model);
|
|
18
18
|
}
|
|
19
|
+
/**
|
|
20
|
+
* Decide whether to force the OpenAI Responses API (`/v1/responses`) for this request.
|
|
21
|
+
*
|
|
22
|
+
* OpenAI's `/v1/chat/completions` endpoint rejects reasoning-capable models (the gpt-5.x / o-series
|
|
23
|
+
* families) when `reasoning_effort` is combined with function tools:
|
|
24
|
+
* "Function tools with reasoning_effort are not supported for gpt-5.6-luna in
|
|
25
|
+
* /v1/chat/completions. To use function tools, use /v1/responses or set reasoning_effort to
|
|
26
|
+
* 'none'."
|
|
27
|
+
* LangChain's auto-router only flips to the Responses API for OpenAI built-in tools,
|
|
28
|
+
* `reasoning.summary` (not `.effort`), custom tools, or a hardcoded model allowlist — none of which
|
|
29
|
+
* covers ordinary function tools on a reasoning model that isn't yet on that allowlist (e.g.
|
|
30
|
+
* gpt-5.6-luna). Crucially, these models **default `reasoning_effort` to a non-`none` value**, so the
|
|
31
|
+
* collision fires even when the gth config sets NO reasoning at all (the failing case: a bare
|
|
32
|
+
* `{ type: openai, model: gpt-5.6-luna }` still 400s on `chat/completions` + tools). So for any
|
|
33
|
+
* reasoning-capable model, when the user has not made an explicit choice, default `useResponsesApi`
|
|
34
|
+
* to true — do NOT gate on reasoning being explicitly configured (GS2-74's original bug: it required
|
|
35
|
+
* `reasoningEffort`/`reasoning` to be set, missing the far more common bare-config path).
|
|
36
|
+
*
|
|
37
|
+
* Guards: (1) only reasoning-capable models — leave gpt-4o etc. on completions; (2) never override an
|
|
38
|
+
* explicit `useResponsesApi` the user set (true OR false). The model-name guard also scopes this away
|
|
39
|
+
* from OpenAI-compatible providers that reuse this file (Inception/DeepSeek/xAI ids don't match the
|
|
40
|
+
* regex). Routing a no-tools/plain call to Responses is still correct FOR GTH because gth never
|
|
41
|
+
* injects reasoning params itself — so even a non-reasoning `gpt-5`-prefixed id (e.g.
|
|
42
|
+
* `gpt-5-chat-latest`, which this regex also sweeps) is accepted there. (Responses isn't a universal
|
|
43
|
+
* superset — it drops some chat/completions-only params like `n>1` — but gth uses none of those.)
|
|
44
|
+
*/
|
|
45
|
+
function shouldUseResponsesApi(fields) {
|
|
46
|
+
return fields.useResponsesApi === undefined && isTemperatureRestrictedModel(fields.model);
|
|
47
|
+
}
|
|
19
48
|
// Function to process JSON config and create OpenAI LLM instance
|
|
20
49
|
// noinspection JSUnusedGlobalSymbols
|
|
21
50
|
export async function processJsonConfig(llmConfig) {
|
|
@@ -43,6 +72,11 @@ export async function processJsonConfig(llmConfig) {
|
|
|
43
72
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
44
73
|
delete configFields.temperature;
|
|
45
74
|
}
|
|
75
|
+
// Reasoning-capable OpenAI models 400 on `/v1/chat/completions` when `reasoning_effort` meets
|
|
76
|
+
// function tools; route them to the Responses API unless the user pinned `useResponsesApi`.
|
|
77
|
+
if (shouldUseResponsesApi(configFields)) {
|
|
78
|
+
configFields.useResponsesApi = true;
|
|
79
|
+
}
|
|
46
80
|
return new ChatOpenAI(configFields);
|
|
47
81
|
}
|
|
48
82
|
function getApiKey(llmConfig) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"openai.js","sourceRoot":"","sources":["../../src/providers/openai.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;;;;;;;GAOG;AACH,MAAM,4BAA4B,GAAG,eAAe,CAAC;AAErD,yFAAyF;AACzF,MAAM,6BAA6B,GAAG,CAAC,CAAC;AAExC,SAAS,4BAA4B,CAAC,KAAyB;IAC7D,OAAO,CAAC,CAAC,KAAK,IAAI,4BAA4B,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;AAC7D,CAAC;AAED,iEAAiE;AACjE,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,YAAY,GAAG,SAAS,CAAC,SAAS,CAAC,CAAC;IAC1C,MAAM,YAAY,GAAG;QACnB,GAAG,SAAS;QACZ,MAAM,EAAE,YAAY;QACpB,KAAK,EAAE,SAAS,CAAC,KAAK,IAAI,uBAAuB,CAAC,QAAQ,CAAC;KAC5D,CAAC;IACF,8DAA8D;IAC9D,OAAQ,YAAoB,CAAC,IAAI,CAAC;IAClC,8DAA8D;IAC9D,OAAQ,YAAoB,CAAC,yBAAyB,CAAC;IAEvD,6FAA6F;IAC7F,8FAA8F;IAC9F,6FAA6F;IAC7F,IACE,4BAA4B,CAAC,YAAY,CAAC,KAAK,CAAC;QAChD,YAAY,CAAC,WAAW,KAAK,SAAS;QACtC,YAAY,CAAC,WAAW,KAAK,6BAA6B,EAC1D,CAAC;QACD,cAAc,CACZ,UAAU,YAAY,CAAC,KAAK,0CAA0C;YACpE,qBAAqB,6BAA6B,gBAAgB;YAClE,0CAA0C,YAAY,CAAC,WAAW,GAAG,CACxE,CAAC;QACF,8DAA8D;QAC9D,OAAQ,YAAoB,CAAC,WAAW,CAAC;IAC3C,CAAC;IAED,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,cAAc,CAAC;IAChD,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,QAAQ,EAAE,KAAK,CAAC,EAAE,KAAK,CAAC,CAAC;IAC5F,cAAc,CACZ,yBAAyB,cAAc,uBAAuB;QAC5D,gDAAgD,CACnD,CAAC;AACJ,CAAC"}
|
|
1
|
+
{"version":3,"file":"openai.js","sourceRoot":"","sources":["../../src/providers/openai.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,27 @@
|
|
|
1
|
-
import { BaseChatModel,
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
import type { BaseChatModel, BaseChatModelParams } from '@langchain/core/language_models/chat_models';
|
|
2
|
+
import type { ChatOpenRouterInput } from '@langchain/openrouter';
|
|
3
|
+
/**
|
|
4
|
+
* Build a native `ChatOpenRouter` (`@langchain/openrouter`). It is NOT a `ChatOpenAI` subclass: it
|
|
5
|
+
* talks to the OpenRouter REST API through the global `fetch` with no SDK client in between, and
|
|
6
|
+
* `_llmType()` reports `openrouter`.
|
|
7
|
+
*
|
|
8
|
+
* OpenRouter's own options are therefore native TOP-LEVEL fields of the `llm` block, forwarded by
|
|
9
|
+
* the `...restConfig` spread below: `provider` (routing preferences), `models` (the fallback list),
|
|
10
|
+
* `route`, `plugins`, `transforms`, `trace`, `minP`, `topA`, `repetitionPenalty`, `seed`,
|
|
11
|
+
* `logitBias`, `topLogprobs`, `sessionId`. Write them beside `model`, not under `configuration`.
|
|
12
|
+
*
|
|
13
|
+
* Migration note — the OpenAI-client knobs a `configuration` block used to carry are gone, because
|
|
14
|
+
* there is no client to give them to. `configuration.baseURL` still works (mapped to the native
|
|
15
|
+
* `baseURL`), and the two attribution headers are still honoured as a fallback for
|
|
16
|
+
* `siteUrl`/`siteName`. What has no injection point at all is transport: `fetch`, `fetchOptions`,
|
|
17
|
+
* `timeout`, `maxRetries`, `defaultQuery`, `dangerouslyAllowBrowser`, and any other
|
|
18
|
+
* `defaultHeaders` entry. That is an upstream surface gap, not something this module can restore —
|
|
19
|
+
* per-provider transport would need a `fetch` option added to `@langchain/openrouter`.
|
|
20
|
+
*
|
|
21
|
+
* The replacement is PROCESS-WIDE rather than per-provider, and it is verified working: run node
|
|
22
|
+
* with `--use-env-proxy` (or set `NODE_USE_ENV_PROXY=1`) together with `HTTP_PROXY`/`HTTPS_PROXY`,
|
|
23
|
+
* and the global `fetch` this model calls routes through the proxy — measured end to end against a
|
|
24
|
+
* local proxy, with a no-proxy control that stayed direct.
|
|
25
|
+
*/
|
|
26
|
+
export declare function processJsonConfig(llmConfig: ChatOpenRouterInput & BaseChatModelParams & Record<string, unknown>): Promise<BaseChatModel>;
|
|
5
27
|
export declare function init(configFileName: string, force?: boolean, model?: string): void;
|
|
@@ -2,43 +2,100 @@ import { displayWarning } from '#src/utils/consoleUtils.js';
|
|
|
2
2
|
import { env } from '#src/utils/systemUtils.js';
|
|
3
3
|
import { writeConfigFileWithMessages } from '#src/utils/fileUtils.js';
|
|
4
4
|
import { buildInitConfigContent, getCuratedFallbackModel } from '#src/providers/modelDiscovery.js';
|
|
5
|
-
|
|
5
|
+
import { NATIVE_CLIENT_REASON, warnConfigurationOverridesTopLevelField, warnUnappliedConfigurationPath, warnUnusedConfiguration, } from '#src/providers/configurationPassthrough.js';
|
|
6
|
+
/**
|
|
7
|
+
* The OpenRouter attribution headers, and the native `ChatOpenRouter` field each one is set from.
|
|
8
|
+
* Declared once because they are read in two places that MUST agree: the `siteUrl`/`siteName`
|
|
9
|
+
* fallback below, and the list of `configuration` paths this factory consumes — if those drift, the
|
|
10
|
+
* orphaned-config warning starts firing on a header that is in fact honoured.
|
|
11
|
+
*/
|
|
12
|
+
const ATTRIBUTION_HEADERS = { siteUrl: 'HTTP-Referer', siteName: 'X-Title' };
|
|
13
|
+
/**
|
|
14
|
+
* The only paths inside a user's `configuration` block that this factory reads. Everything else in
|
|
15
|
+
* it reaches nothing — see the migration note on {@link processJsonConfig}.
|
|
16
|
+
*/
|
|
17
|
+
const CONSUMED_CONFIGURATION_PATHS = [
|
|
18
|
+
'baseURL',
|
|
19
|
+
`defaultHeaders.${ATTRIBUTION_HEADERS.siteUrl}`,
|
|
20
|
+
`defaultHeaders.${ATTRIBUTION_HEADERS.siteName}`,
|
|
21
|
+
];
|
|
22
|
+
/**
|
|
23
|
+
* Build a native `ChatOpenRouter` (`@langchain/openrouter`). It is NOT a `ChatOpenAI` subclass: it
|
|
24
|
+
* talks to the OpenRouter REST API through the global `fetch` with no SDK client in between, and
|
|
25
|
+
* `_llmType()` reports `openrouter`.
|
|
26
|
+
*
|
|
27
|
+
* OpenRouter's own options are therefore native TOP-LEVEL fields of the `llm` block, forwarded by
|
|
28
|
+
* the `...restConfig` spread below: `provider` (routing preferences), `models` (the fallback list),
|
|
29
|
+
* `route`, `plugins`, `transforms`, `trace`, `minP`, `topA`, `repetitionPenalty`, `seed`,
|
|
30
|
+
* `logitBias`, `topLogprobs`, `sessionId`. Write them beside `model`, not under `configuration`.
|
|
31
|
+
*
|
|
32
|
+
* Migration note — the OpenAI-client knobs a `configuration` block used to carry are gone, because
|
|
33
|
+
* there is no client to give them to. `configuration.baseURL` still works (mapped to the native
|
|
34
|
+
* `baseURL`), and the two attribution headers are still honoured as a fallback for
|
|
35
|
+
* `siteUrl`/`siteName`. What has no injection point at all is transport: `fetch`, `fetchOptions`,
|
|
36
|
+
* `timeout`, `maxRetries`, `defaultQuery`, `dangerouslyAllowBrowser`, and any other
|
|
37
|
+
* `defaultHeaders` entry. That is an upstream surface gap, not something this module can restore —
|
|
38
|
+
* per-provider transport would need a `fetch` option added to `@langchain/openrouter`.
|
|
39
|
+
*
|
|
40
|
+
* The replacement is PROCESS-WIDE rather than per-provider, and it is verified working: run node
|
|
41
|
+
* with `--use-env-proxy` (or set `NODE_USE_ENV_PROXY=1`) together with `HTTP_PROXY`/`HTTPS_PROXY`,
|
|
42
|
+
* and the global `fetch` this model calls routes through the proxy — measured end to end against a
|
|
43
|
+
* local proxy, with a no-proxy control that stayed direct.
|
|
44
|
+
*/
|
|
6
45
|
// noinspection JSUnusedGlobalSymbols
|
|
7
46
|
export async function processJsonConfig(llmConfig) {
|
|
8
|
-
const {
|
|
9
|
-
// Use environment variable if available, otherwise use the config value
|
|
47
|
+
const { ChatOpenRouter } = await import('@langchain/openrouter');
|
|
10
48
|
const openRouterApiKey = getApiKey(llmConfig);
|
|
11
49
|
if (!openRouterApiKey) {
|
|
12
50
|
throw new Error('You need to define OPEN_ROUTER_API_KEY environment variable, or set apiKey in your config file.');
|
|
13
51
|
}
|
|
14
|
-
const
|
|
15
|
-
|
|
52
|
+
const { type: _type, apiKeyEnvironmentVariable: _envVar, configuration, siteUrl, siteName, ...restConfig } = llmConfig;
|
|
53
|
+
const configObj = configuration;
|
|
54
|
+
// The one place that decides whether the user's `configuration.baseURL` is applied; the warning
|
|
55
|
+
// below reads THIS result instead of re-testing the value, so the guard and what the user is told
|
|
56
|
+
// cannot drift apart.
|
|
57
|
+
const baseURLOverride = configObj?.baseURL ? { baseURL: configObj.baseURL } : {};
|
|
58
|
+
warnUnusedConfiguration({
|
|
59
|
+
provider: 'openrouter',
|
|
60
|
+
configuration,
|
|
61
|
+
consumedPaths: CONSUMED_CONFIGURATION_PATHS,
|
|
62
|
+
reason: NATIVE_CLIENT_REASON,
|
|
63
|
+
// Scope the advice to the options that HAVE a top-level home: told plainly to "put it at the
|
|
64
|
+
// top level", a user with a transport key would move it from a warned location to an unwarned
|
|
65
|
+
// one, which is worse than where they started.
|
|
66
|
+
guidance: 'OpenRouter\'s own options are native top-level fields of the "llm" block — set "provider" ' +
|
|
67
|
+
'(routing preferences), "models", "route", "plugins" or "transforms" beside "model" rather ' +
|
|
68
|
+
'than under "configuration". Transport settings (proxy, custom fetch, timeout, other ' +
|
|
69
|
+
'headers) have no top-level field and no per-provider hook at all: run node with ' +
|
|
70
|
+
'--use-env-proxy (or set NODE_USE_ENV_PROXY=1) plus HTTP_PROXY/HTTPS_PROXY to route this ' +
|
|
71
|
+
'provider through a proxy process-wide.',
|
|
72
|
+
});
|
|
73
|
+
warnUnappliedConfigurationPath('openrouter', configuration, 'baseURL', 'baseURL' in baseURLOverride, 'Give it the full base URL of the OpenRouter-compatible endpoint, or remove it to use the ' +
|
|
74
|
+
'default https://openrouter.ai/api/v1.');
|
|
75
|
+
// `baseURL` is ALSO a native top-level field of `ChatOpenRouter`, forwarded by `...restConfig`
|
|
76
|
+
// below — and the block's value is spread after it, so it wins. Both are honoured surfaces, so
|
|
77
|
+
// the defect is only the silence: say which one takes effect rather than dropping the other
|
|
78
|
+
// without a word.
|
|
79
|
+
warnConfigurationOverridesTopLevelField('openrouter', 'baseURL', restConfig.baseURL, baseURLOverride.baseURL);
|
|
80
|
+
const resolvedSiteUrl = siteUrl ??
|
|
81
|
+
configObj?.defaultHeaders?.[ATTRIBUTION_HEADERS.siteUrl] ??
|
|
82
|
+
'https://gauntsloth.app/';
|
|
83
|
+
const resolvedSiteName = siteName ?? configObj?.defaultHeaders?.[ATTRIBUTION_HEADERS.siteName] ?? 'Gaunt Sloth';
|
|
84
|
+
return new ChatOpenRouter({
|
|
85
|
+
...restConfig,
|
|
16
86
|
apiKey: openRouterApiKey,
|
|
17
87
|
model: llmConfig.model || getCuratedFallbackModel('openrouter'),
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
'HTTP-Referer': 'https://gauntsloth.app/',
|
|
23
|
-
'X-Title': 'Gaunt Sloth',
|
|
24
|
-
},
|
|
25
|
-
},
|
|
26
|
-
};
|
|
27
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
28
|
-
delete configFields.type;
|
|
29
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
30
|
-
delete configFields.apiKeyEnvironmentVariable;
|
|
31
|
-
return new ChatOpenAI(configFields);
|
|
88
|
+
siteUrl: resolvedSiteUrl,
|
|
89
|
+
siteName: resolvedSiteName,
|
|
90
|
+
...baseURLOverride,
|
|
91
|
+
});
|
|
32
92
|
}
|
|
33
93
|
function getApiKey(llmConfig) {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
return env[conf.apiKeyEnvironmentVariable];
|
|
38
|
-
}
|
|
39
|
-
else {
|
|
40
|
-
return llmConfig.apiKey || env.OPEN_ROUTER_API_KEY || env.OPENROUTER_API_KEY;
|
|
94
|
+
const envVarName = llmConfig.apiKeyEnvironmentVariable;
|
|
95
|
+
if (envVarName && env[envVarName]) {
|
|
96
|
+
return env[envVarName];
|
|
41
97
|
}
|
|
98
|
+
return (llmConfig.apiKey || env.OPEN_ROUTER_API_KEY || env.OPENROUTER_API_KEY);
|
|
42
99
|
}
|
|
43
100
|
export function init(configFileName, force = false, model) {
|
|
44
101
|
// Determine which content to use based on file extension
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"openrouter.js","sourceRoot":"","sources":["../../src/providers/openrouter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAC5D,OAAO,EAAE,GAAG,EAAE,MAAM,2BAA2B,CAAC;
|
|
1
|
+
{"version":3,"file":"openrouter.js","sourceRoot":"","sources":["../../src/providers/openrouter.ts"],"names":[],"mappings":"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"}
|
|
@@ -13,6 +13,9 @@
|
|
|
13
13
|
import { displayWarning } from '#src/utils/consoleUtils.js';
|
|
14
14
|
import { writeConfigFileWithMessages } from '#src/utils/fileUtils.js';
|
|
15
15
|
import { buildInitConfigContent, getCuratedFallbackModel } from '#src/providers/modelDiscovery.js';
|
|
16
|
+
import { applyGeminiToolSchemaSanitizer } from '#src/providers/geminiSchemaSanitizer.js';
|
|
17
|
+
import { applyGeminiThoughtSummaries } from '#src/providers/geminiThinking.js';
|
|
18
|
+
import { NATIVE_CLIENT_REASON, warnUnusedConfiguration, } from '#src/providers/configurationPassthrough.js';
|
|
16
19
|
export function init(configFileName, force = false, model) {
|
|
17
20
|
// Determine which content to use based on file extension
|
|
18
21
|
if (!configFileName.endsWith('.json')) {
|
|
@@ -31,6 +34,21 @@ export async function processJsonConfig(llmConfig) {
|
|
|
31
34
|
};
|
|
32
35
|
delete configFields.type;
|
|
33
36
|
delete configFields.apiKeyEnvironmentVariable;
|
|
34
|
-
|
|
37
|
+
// `ChatGoogle` is a native client for the Gemini API, so nothing in a `configuration` block
|
|
38
|
+
// reaches it — say so before dropping it.
|
|
39
|
+
warnUnusedConfiguration({
|
|
40
|
+
provider: 'vertexai',
|
|
41
|
+
configuration: llmConfig.configuration,
|
|
42
|
+
consumedPaths: [],
|
|
43
|
+
reason: NATIVE_CLIENT_REASON,
|
|
44
|
+
guidance: 'ChatGoogle talks to Vertex AI through its own client instead: set "customHeaders", ' +
|
|
45
|
+
'"endpoint", "apiVersion" or "location" as top-level fields of the "llm" block beside ' +
|
|
46
|
+
'"model".',
|
|
47
|
+
});
|
|
48
|
+
// GS2-58: normalise every tool's JSON-Schema at the ChatGoogle boundary so Gemini's OpenAPI-3.0
|
|
49
|
+
// subset accepts built-in, custom, and MCP tools alike (see geminiSchemaSanitizer).
|
|
50
|
+
// CFG-33: Vertex serves the same Gemini models through the same ChatGoogle class, so it has the
|
|
51
|
+
// same silently-discarded thinking; ask for the summaries here too (see geminiThinking).
|
|
52
|
+
return applyGeminiThoughtSummaries(applyGeminiToolSchemaSanitizer(new ChatGoogle(configFields)));
|
|
35
53
|
}
|
|
36
54
|
//# sourceMappingURL=vertexai.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vertexai.js","sourceRoot":"","sources":["../../src/providers/vertexai.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAI5D,OAAO,EAAE,2BAA2B,EAAE,MAAM,yBAAyB,CAAC;AACtE,OAAO,EAAE,sBAAsB,EAAE,uBAAuB,EAAE,MAAM,kCAAkC,CAAC;
|
|
1
|
+
{"version":3,"file":"vertexai.js","sourceRoot":"","sources":["../../src/providers/vertexai.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAI5D,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;AACzF,OAAO,EAAE,2BAA2B,EAAE,MAAM,kCAAkC,CAAC;AAC/E,OAAO,EACL,oBAAoB,EACpB,uBAAuB,GACxB,MAAM,4CAA4C,CAAC;AAEpD,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,+GAA+G,CAChH,CAAC;AACJ,CAAC;AAED,mEAAmE;AACnE,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,SAAmF;IAEnF,MAAM,EAAE,UAAU,EAAE,GAAG,MAAM,MAAM,CAAC,wBAAwB,CAAC,CAAC;IAC9D,MAAM,YAAY,GAAG;QACnB,GAAG,SAAS;QACZ,KAAK,EAAE,SAAS,CAAC,KAAK,IAAI,uBAAuB,CAAC,UAAU,CAAC;QAC7D,QAAQ,EAAE,IAAI;KACf,CAAC;IACF,OAAO,YAAY,CAAC,IAAI,CAAC;IACzB,OAAO,YAAY,CAAC,yBAAyB,CAAC;IAC9C,4FAA4F;IAC5F,0CAA0C;IAC1C,uBAAuB,CAAC;QACtB,QAAQ,EAAE,UAAU;QACpB,aAAa,EAAG,SAAyC,CAAC,aAAa;QACvE,aAAa,EAAE,EAAE;QACjB,MAAM,EAAE,oBAAoB;QAC5B,QAAQ,EACN,qFAAqF;YACrF,uFAAuF;YACvF,UAAU;KACb,CAAC,CAAC;IACH,gGAAgG;IAChG,oFAAoF;IACpF,gGAAgG;IAChG,yFAAyF;IACzF,OAAO,2BAA2B,CAAC,8BAA8B,CAAC,IAAI,UAAU,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;AACnG,CAAC"}
|
package/dist/providers/xai.js
CHANGED
|
@@ -2,11 +2,31 @@ import { displayWarning } from '#src/utils/consoleUtils.js';
|
|
|
2
2
|
import { env } from '#src/utils/systemUtils.js';
|
|
3
3
|
import { writeConfigFileWithMessages } from '#src/utils/fileUtils.js';
|
|
4
4
|
import { buildInitConfigContent, getCuratedFallbackModel } from '#src/providers/modelDiscovery.js';
|
|
5
|
+
import { warnUnusedConfiguration } from '#src/providers/configurationPassthrough.js';
|
|
5
6
|
// Function to process JSON config and create XAI LLM instance
|
|
6
7
|
export async function processJsonConfig(llmConfig) {
|
|
7
8
|
const { ChatXAI } = await import('@langchain/xai');
|
|
8
9
|
// Use config value if available, otherwise use the environment variable
|
|
9
10
|
const apiKey = llmConfig.apiKey || env.XAI_API_KEY;
|
|
11
|
+
// `ChatXAI` DOES build an OpenAI client, and still consumes none of the user's block: its
|
|
12
|
+
// constructor passes `configuration: { baseURL: fields.baseURL ?? <xAI default> }` to `super`,
|
|
13
|
+
// replacing whatever was set here. So the reason clause is xai's own — the shared native-client
|
|
14
|
+
// sentence would be a false explanation — and the replacements are the top-level fields that were
|
|
15
|
+
// measured to reach the client: `baseURL` (a declared `ChatXAIInput` field) and `timeout` (read
|
|
16
|
+
// by the OpenAI base class from the same spread). Headers reach it by no route at all.
|
|
17
|
+
warnUnusedConfiguration({
|
|
18
|
+
provider: 'xai',
|
|
19
|
+
configuration: llmConfig.configuration,
|
|
20
|
+
consumedPaths: [],
|
|
21
|
+
reason: 'builds its OpenAI client with a "configuration" block of its own, replacing anything set here',
|
|
22
|
+
// The escape hatch is only followable with the key hint: the "openai" provider reads
|
|
23
|
+
// OPENAI_API_KEY, so a user who moves to it for the headers alone hits a missing-key error.
|
|
24
|
+
guidance: 'Set "baseURL" or "timeout" as top-level fields of the "llm" block instead, beside "model". ' +
|
|
25
|
+
'Extra headers have no home on this provider at all — to send them, use the "openai" ' +
|
|
26
|
+
'provider with "configuration.baseURL" pointed at the xAI endpoint, and set ' +
|
|
27
|
+
'"apiKeyEnvironmentVariable" to XAI_API_KEY there, since that provider otherwise reads ' +
|
|
28
|
+
'OPENAI_API_KEY.',
|
|
29
|
+
});
|
|
10
30
|
return new ChatXAI({
|
|
11
31
|
...llmConfig,
|
|
12
32
|
apiKey,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"xai.js","sourceRoot":"","sources":["../../src/providers/xai.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;
|
|
1
|
+
{"version":3,"file":"xai.js","sourceRoot":"","sources":["../../src/providers/xai.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;AACnG,OAAO,EAAE,uBAAuB,EAAE,MAAM,4CAA4C,CAAC;AAErF,8DAA8D;AAC9D,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,SAA6C;IAE7C,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,MAAM,CAAC,gBAAgB,CAAC,CAAC;IACnD,wEAAwE;IACxE,MAAM,MAAM,GAAG,SAAS,CAAC,MAAM,IAAI,GAAG,CAAC,WAAW,CAAC;IACnD,0FAA0F;IAC1F,+FAA+F;IAC/F,gGAAgG;IAChG,kGAAkG;IAClG,gGAAgG;IAChG,uFAAuF;IACvF,uBAAuB,CAAC;QACtB,QAAQ,EAAE,KAAK;QACf,aAAa,EAAG,SAAyC,CAAC,aAAa;QACvE,aAAa,EAAE,EAAE;QACjB,MAAM,EACJ,+FAA+F;QACjG,qFAAqF;QACrF,4FAA4F;QAC5F,QAAQ,EACN,6FAA6F;YAC7F,sFAAsF;YACtF,6EAA6E;YAC7E,wFAAwF;YACxF,iBAAiB;KACpB,CAAC,CAAC;IACH,OAAO,IAAI,OAAO,CAAC;QACjB,GAAG,SAAS;QACZ,MAAM;QACN,KAAK,EAAE,SAAS,CAAC,KAAK,IAAI,uBAAuB,CAAC,KAAK,CAAC;KACzD,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,KAAK,EAAE,KAAK,CAAC,EAAE,KAAK,CAAC,CAAC;IACzF,cAAc,CACZ,2BAA2B,cAAc,4BAA4B;QACnE,6CAA6C,CAChD,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module runtime/askStructured
|
|
3
|
+
*
|
|
4
|
+
* A reusable, non-agentic "ask the LLM and get a schema-validated object back" primitive — the
|
|
5
|
+
* structured-output half the (later) `gth workflow` host calls. Deliberately mirrors the
|
|
6
|
+
* *mechanism* of the structured-evaluation calls ({@link judgeEvalCase} — the EVAL GRADER in
|
|
7
|
+
* `@gaunt-sloth/batch`'s `judge.ts` — and its in-core sibling {@link rateShellCommand}, the
|
|
8
|
+
* approvals AI rater in `core/shell/rater.ts`):
|
|
9
|
+
* `model.withStructuredOutput(schema)` for a single structured call, `.invoke([SystemMessage,
|
|
10
|
+
* HumanMessage])` raced against a wall-clock timeout via `Promise.race`, a defensive `safeParse`
|
|
11
|
+
* re-validation, `clearTimeout` in `finally`, and — crucially — it **never throws**, returning a
|
|
12
|
+
* failure object instead.
|
|
13
|
+
*
|
|
14
|
+
* Differences from those two: this one is **generic** over the Zod schema and takes the
|
|
15
|
+
* system/user strings from the caller (they hard-code a schema and build a rubric/safety
|
|
16
|
+
* prompt), and it reads the model from `config.llm` (like `runSingleShot`/`rateShellCommand`),
|
|
17
|
+
* so the workflow host can hand it the resolved {@link GthConfig} directly. `judgeEvalCase` could
|
|
18
|
+
* later be refactored to delegate to this primitive — out of scope here.
|
|
19
|
+
*/
|
|
20
|
+
import * as z from 'zod';
|
|
21
|
+
import type { GthConfig } from '#src/config.js';
|
|
22
|
+
/**
|
|
23
|
+
* Default wall-clock budget (ms) for the structured LLM call — same value as their
|
|
24
|
+
* `EVAL_JUDGE_DEFAULT_TIMEOUT_MS` / `JUDGE_DEFAULT_TIMEOUT_MS`, kept as this module's own constant
|
|
25
|
+
* since the primitive is conceptually independent of them.
|
|
26
|
+
*/
|
|
27
|
+
export declare const ASK_STRUCTURED_DEFAULT_TIMEOUT_MS = 30000;
|
|
28
|
+
/**
|
|
29
|
+
* The exact `error` text {@link askStructured} returns when its wall-clock budget fires — i.e. the
|
|
30
|
+
* provider produced **no answer at all** within the budget.
|
|
31
|
+
*
|
|
32
|
+
* Exported as a builder rather than left as an inline literal because the three failure classes here
|
|
33
|
+
* (no response · an answer that does not fit the schema · a call that failed outright) are the
|
|
34
|
+
* question a reader of a red run has to answer first, and they are only distinguishable by this text.
|
|
35
|
+
* A caller that re-types the sentence to recognise it stops recognising it the day the wording moves,
|
|
36
|
+
* and silently re-files a stall as a rejection — which is precisely the confusion this exists to end.
|
|
37
|
+
*/
|
|
38
|
+
export declare function structuredCallTimedOutError(timeoutMs: number): string;
|
|
39
|
+
/**
|
|
40
|
+
* The exact `error` text {@link askStructured} returns when the provider **did** answer and the
|
|
41
|
+
* answer failed the caller's own schema. The counterpart of {@link structuredCallTimedOutError};
|
|
42
|
+
* see that doc for why both are exported rather than written inline.
|
|
43
|
+
*/
|
|
44
|
+
export declare const STRUCTURED_CALL_UNPARSEABLE_ERROR = "Model returned unparseable output.";
|
|
45
|
+
/**
|
|
46
|
+
* Which of {@link askStructured}'s three failure paths a result came from.
|
|
47
|
+
*
|
|
48
|
+
* - `timeout` — the budget fired and the provider had returned nothing at all.
|
|
49
|
+
* - `unparseable` — the provider answered, and the answer failed the caller's schema.
|
|
50
|
+
* - `call-failed` — the call threw before any answer arrived: a provider rejection (OpenAI's 400 on
|
|
51
|
+
* a strict `json_schema` is the canonical one), an auth failure, a transport failure.
|
|
52
|
+
*/
|
|
53
|
+
export type StructuredFailureKind = 'timeout' | 'unparseable' | 'call-failed';
|
|
54
|
+
/**
|
|
55
|
+
* Name which failure happened, given the `error` string and the budget the call was made with.
|
|
56
|
+
*
|
|
57
|
+
* The single place the three classes are told apart. A stall and a rejection are the two failures a
|
|
58
|
+
* reader has to distinguish first and the two that look most alike from the outside — a red that
|
|
59
|
+
* cannot say which happened sends the reader to re-run a real defect, or to investigate a provider
|
|
60
|
+
* hiccup. Every caller that wants to say which one it was calls this rather than matching the prose
|
|
61
|
+
* itself, so the wording can only be got wrong in one place, and that place has a test.
|
|
62
|
+
*
|
|
63
|
+
* @param error The `error` from a `{ ok: false }` {@link AskStructuredResult}.
|
|
64
|
+
* @param timeoutMs The budget passed to that same {@link askStructured} call — the timeout text
|
|
65
|
+
* carries it, so a different value here reports a genuine timeout as `call-failed`.
|
|
66
|
+
*/
|
|
67
|
+
export declare function classifyStructuredFailure(error: string, timeoutMs: number): StructuredFailureKind;
|
|
68
|
+
/** Inputs to {@link askStructured}. The caller supplies the model (via config), the two message
|
|
69
|
+
* texts, and an optional timeout — the Zod schema is a separate positional argument so `<T>` can
|
|
70
|
+
* be inferred from it. */
|
|
71
|
+
export interface AskStructuredOptions {
|
|
72
|
+
config: GthConfig;
|
|
73
|
+
/** System-message text (instructions). May be empty. */
|
|
74
|
+
system: string;
|
|
75
|
+
/** Human-message text (the actual content/question). */
|
|
76
|
+
user: string;
|
|
77
|
+
/** Wall-clock budget in ms. Default {@link ASK_STRUCTURED_DEFAULT_TIMEOUT_MS} (30_000). */
|
|
78
|
+
timeoutMs?: number;
|
|
79
|
+
}
|
|
80
|
+
/** Discriminated result of {@link askStructured}: the parsed value on success, an error string on
|
|
81
|
+
* any failure (unusable model, timeout, unparseable output, or a thrown error). Never throws. */
|
|
82
|
+
export type AskStructuredResult<T> = {
|
|
83
|
+
ok: true;
|
|
84
|
+
value: T;
|
|
85
|
+
} | {
|
|
86
|
+
ok: false;
|
|
87
|
+
error: string;
|
|
88
|
+
};
|
|
89
|
+
/**
|
|
90
|
+
* Ask the configured model for a single schema-validated object — a non-agentic structured-output
|
|
91
|
+
* call that mirrors their mechanism (see the module doc) and never throws.
|
|
92
|
+
*
|
|
93
|
+
* - No usable model (`config.llm` missing or lacking `withStructuredOutput`) →
|
|
94
|
+
* `{ ok: false, error: 'No usable model configured.' }`.
|
|
95
|
+
* - Timeout → `ok: false` with the error {@link structuredCallTimedOutError} builds for that budget.
|
|
96
|
+
* - Output that fails `schema.safeParse` → `{ ok: false, error: {@link STRUCTURED_CALL_UNPARSEABLE_ERROR} }`.
|
|
97
|
+
* - Any thrown error → `{ ok: false, error: <message> }` — a provider rejection (e.g. OpenAI's 400 on
|
|
98
|
+
* a strict `json_schema`), an auth or transport failure. Distinct from the two above BY CODE PATH,
|
|
99
|
+
* so a caller can classify a failure by comparing against those two exported texts.
|
|
100
|
+
* - Success → `{ ok: true, value }` with the parsed data.
|
|
101
|
+
*
|
|
102
|
+
* @param schema The Zod schema the model output must satisfy; `<T>` is inferred from it.
|
|
103
|
+
* @param opts The model (via `config.llm`), the system/user message texts, and an optional timeout.
|
|
104
|
+
*/
|
|
105
|
+
export declare function askStructured<T>(schema: z.ZodType<T>, opts: AskStructuredOptions): Promise<AskStructuredResult<T>>;
|