@gaunt-sloth/core 0.1.7 → 2.0.0-alpha.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.exec.md +26 -0
- package/dist/config.d.ts +81 -1
- package/dist/config.js +118 -3
- package/dist/config.js.map +1 -1
- package/dist/constants.d.ts +1 -0
- package/dist/constants.js +1 -0
- package/dist/constants.js.map +1 -1
- package/dist/core/GthAbstractAgent.d.ts +73 -0
- package/dist/core/GthAbstractAgent.js +448 -0
- package/dist/core/GthAbstractAgent.js.map +1 -0
- package/dist/core/GthAgentRunner.d.ts +21 -2
- package/dist/core/GthAgentRunner.js +30 -2
- package/dist/core/GthAgentRunner.js.map +1 -1
- package/dist/core/GthLangChainAgent.d.ts +9 -75
- package/dist/core/GthLangChainAgent.js +13 -433
- package/dist/core/GthLangChainAgent.js.map +1 -1
- package/dist/core/types.d.ts +63 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/providers/anthropic.js +2 -2
- package/dist/providers/deepseek.js +2 -2
- package/dist/providers/deepseek.js.map +1 -1
- package/dist/providers/google-genai.js +2 -2
- package/dist/providers/google-genai.js.map +1 -1
- package/dist/providers/modelDiscovery.d.ts +196 -0
- package/dist/providers/modelDiscovery.js +362 -0
- package/dist/providers/modelDiscovery.js.map +1 -0
- package/dist/providers/ollama.d.ts +5 -0
- package/dist/providers/ollama.js +79 -0
- package/dist/providers/ollama.js.map +1 -0
- package/dist/providers/openai.js +28 -2
- package/dist/providers/openai.js.map +1 -1
- package/dist/providers/vertexai.js +2 -2
- package/dist/providers/vertexai.js.map +1 -1
- package/dist/providers/xai.js +2 -2
- package/dist/providers/xai.js.map +1 -1
- package/dist/runtime/singleShot.d.ts +19 -0
- package/dist/runtime/singleShot.js +62 -0
- package/dist/runtime/singleShot.js.map +1 -0
- package/dist/utils/fileUtils.d.ts +6 -0
- package/dist/utils/fileUtils.js +17 -0
- package/dist/utils/fileUtils.js.map +1 -1
- package/dist/utils/globalConfigUtils.d.ts +21 -0
- package/dist/utils/globalConfigUtils.js +25 -0
- package/dist/utils/globalConfigUtils.js.map +1 -1
- package/dist/utils/llmUtils.d.ts +1 -0
- package/dist/utils/llmUtils.js +4 -1
- package/dist/utils/llmUtils.js.map +1 -1
- package/dist/utils/systemUtils.d.ts +10 -0
- package/dist/utils/systemUtils.js +18 -1
- package/dist/utils/systemUtils.js.map +1 -1
- package/package.json +17 -14
|
@@ -0,0 +1,362 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @packageDocumentation
|
|
3
|
+
* Provider / API-key detection and per-provider model listing.
|
|
4
|
+
*
|
|
5
|
+
* This is the data layer that powers the first-run configuration dialog (CFG-2),
|
|
6
|
+
* ACP model selection (CFG-5) and downstream propagation (CFG-6). It answers two
|
|
7
|
+
* questions without ever instantiating an LLM:
|
|
8
|
+
*
|
|
9
|
+
* 1. **Which providers are usable on this machine?** — by inspecting the
|
|
10
|
+
* environment (and config) for API keys, and by probing for a local Ollama.
|
|
11
|
+
* 2. **What models does each usable provider offer?** — a live `GET /v1/models`
|
|
12
|
+
* query for providers that expose an OpenAI-compatible (or, for Anthropic, a
|
|
13
|
+
* native) models endpoint, falling back to a curated ⭐ "preferred" / tested
|
|
14
|
+
* list when the live query is unavailable, errors, or is empty.
|
|
15
|
+
*
|
|
16
|
+
* Live discovery (CFG-12) is best-effort and never fatal: a bad key, an offline
|
|
17
|
+
* machine, or a malformed response simply degrades to the curated catalog so the
|
|
18
|
+
* first-run dialog always has something to show. The curated `preferredModels`
|
|
19
|
+
* therefore do double duty — the ⭐ ranking overlay over live ids **and** the
|
|
20
|
+
* offline/timeout fallback.
|
|
21
|
+
*
|
|
22
|
+
* The provider ids here are the same strings used by {@link LLMConfig.type} and
|
|
23
|
+
* the provider factory in `#src/providers/<type>.js`, so a selected
|
|
24
|
+
* `{ providerId, model }` maps directly onto a `RawGthConfig.llm`.
|
|
25
|
+
*/
|
|
26
|
+
import { availableDefaultConfigs } from '#src/config.js';
|
|
27
|
+
import { displayDebug } from '#src/utils/consoleUtils.js';
|
|
28
|
+
import { env } from '#src/utils/systemUtils.js';
|
|
29
|
+
/** Default Ollama host, matching the Ollama CLI/library default. */
|
|
30
|
+
export const DEFAULT_OLLAMA_HOST = 'http://127.0.0.1:11434';
|
|
31
|
+
function resolveOllamaHost() {
|
|
32
|
+
const host = env.OLLAMA_HOST;
|
|
33
|
+
if (!host)
|
|
34
|
+
return DEFAULT_OLLAMA_HOST;
|
|
35
|
+
// OLLAMA_HOST may be a bare host:port; normalize to a URL.
|
|
36
|
+
if (/^https?:\/\//.test(host))
|
|
37
|
+
return host.replace(/\/$/, '');
|
|
38
|
+
return `http://${host}`.replace(/\/$/, '');
|
|
39
|
+
}
|
|
40
|
+
/** `Authorization: Bearer <key>` — the OpenAI-compatible auth scheme. */
|
|
41
|
+
const bearer = (key) => ({ Authorization: `Bearer ${key}` });
|
|
42
|
+
/**
|
|
43
|
+
* Chat-only filter for OpenAI-shaped catalogs. Drops the obvious non-chat model
|
|
44
|
+
* classes (embeddings, audio/speech, image, moderation, guard) that the live
|
|
45
|
+
* endpoints return alongside chat models. Deliberately permissive: anything not
|
|
46
|
+
* recognised as non-chat is kept, so a new chat family is never hidden.
|
|
47
|
+
*/
|
|
48
|
+
const NON_CHAT_PATTERN = /(embed|moderation|whisper|tts|audio|transcribe|dall-e|image|imagine|guard|rerank|vision-ocr)/i;
|
|
49
|
+
const chatOnly = (id) => !NON_CHAT_PATTERN.test(id);
|
|
50
|
+
/**
|
|
51
|
+
* Provider registry. The curated `preferredModels` are the models we have
|
|
52
|
+
* tested with Gaunt Sloth's agent loop; defaults mirror the `init` templates in
|
|
53
|
+
* each `#src/providers/<id>.js` factory.
|
|
54
|
+
*/
|
|
55
|
+
export const PROVIDER_DESCRIPTORS = [
|
|
56
|
+
{
|
|
57
|
+
id: 'anthropic',
|
|
58
|
+
label: 'Anthropic (Claude)',
|
|
59
|
+
apiKeyEnvironmentVariables: ['ANTHROPIC_API_KEY'],
|
|
60
|
+
preferredModels: ['claude-sonnet-4-6', 'claude-opus-4-8', 'claude-haiku-4-5'],
|
|
61
|
+
discovery: {
|
|
62
|
+
kind: 'anthropic',
|
|
63
|
+
modelsUrl: () => 'https://api.anthropic.com/v1/models',
|
|
64
|
+
authHeader: (key) => ({ 'x-api-key': key, 'anthropic-version': '2023-06-01' }),
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
id: 'openai',
|
|
69
|
+
label: 'OpenAI',
|
|
70
|
+
apiKeyEnvironmentVariables: ['OPENAI_API_KEY'],
|
|
71
|
+
preferredModels: ['gpt-5.5', 'gpt-5.4-mini', 'gpt-5.3-codex', 'gpt-5.4-nano'],
|
|
72
|
+
discovery: {
|
|
73
|
+
kind: 'openai',
|
|
74
|
+
modelsUrl: () => 'https://api.openai.com/v1/models',
|
|
75
|
+
authHeader: bearer,
|
|
76
|
+
filter: chatOnly,
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
id: 'google-genai',
|
|
81
|
+
label: 'Google AI Studio (Gemini)',
|
|
82
|
+
apiKeyEnvironmentVariables: ['GOOGLE_API_KEY'],
|
|
83
|
+
// AI Studio exposes the 3.1 Pro tier only as a `-preview` slug.
|
|
84
|
+
preferredModels: [
|
|
85
|
+
'gemini-3.5-flash',
|
|
86
|
+
'gemini-3.1-pro-preview',
|
|
87
|
+
'gemini-2.5-pro',
|
|
88
|
+
'gemini-2.5-flash',
|
|
89
|
+
],
|
|
90
|
+
discovery: { kind: 'none' },
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
id: 'vertexai',
|
|
94
|
+
label: 'Google Vertex AI (Gemini)',
|
|
95
|
+
apiKeyEnvironmentVariables: [],
|
|
96
|
+
// Vertex publishes the same family under bare (non-preview) slugs.
|
|
97
|
+
preferredModels: ['gemini-3.5-flash', 'gemini-3.1-pro', 'gemini-2.5-pro', 'gemini-2.5-flash'],
|
|
98
|
+
discovery: { kind: 'none' },
|
|
99
|
+
requiresExternalAuth: true,
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
id: 'groq',
|
|
103
|
+
label: 'Groq',
|
|
104
|
+
apiKeyEnvironmentVariables: ['GROQ_API_KEY'],
|
|
105
|
+
preferredModels: ['openai/gpt-oss-120b', 'qwen/qwen3.6-27b', 'openai/gpt-oss-20b'],
|
|
106
|
+
discovery: {
|
|
107
|
+
kind: 'openai',
|
|
108
|
+
// Groq's OpenAI-compatible surface lives under /openai/v1.
|
|
109
|
+
modelsUrl: () => 'https://api.groq.com/openai/v1/models',
|
|
110
|
+
authHeader: bearer,
|
|
111
|
+
filter: chatOnly,
|
|
112
|
+
},
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
id: 'deepseek',
|
|
116
|
+
label: 'DeepSeek',
|
|
117
|
+
apiKeyEnvironmentVariables: ['DEEPSEEK_API_KEY'],
|
|
118
|
+
preferredModels: ['deepseek-v4-pro', 'deepseek-v4-flash'],
|
|
119
|
+
discovery: {
|
|
120
|
+
kind: 'openai',
|
|
121
|
+
modelsUrl: () => 'https://api.deepseek.com/v1/models',
|
|
122
|
+
authHeader: bearer,
|
|
123
|
+
filter: chatOnly,
|
|
124
|
+
},
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
id: 'xai',
|
|
128
|
+
label: 'xAI (Grok)',
|
|
129
|
+
apiKeyEnvironmentVariables: ['XAI_API_KEY'],
|
|
130
|
+
preferredModels: ['grok-4.3', 'grok-build-0.1'],
|
|
131
|
+
discovery: {
|
|
132
|
+
kind: 'openai',
|
|
133
|
+
modelsUrl: () => 'https://api.x.ai/v1/models',
|
|
134
|
+
authHeader: bearer,
|
|
135
|
+
filter: chatOnly,
|
|
136
|
+
},
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
id: 'openrouter',
|
|
140
|
+
// OpenRouter primarily reads OPEN_ROUTER_API_KEY (see providers/openrouter.ts),
|
|
141
|
+
// OPENROUTER_API_KEY is accepted as an alias.
|
|
142
|
+
label: 'OpenRouter',
|
|
143
|
+
apiKeyEnvironmentVariables: ['OPEN_ROUTER_API_KEY', 'OPENROUTER_API_KEY'],
|
|
144
|
+
preferredModels: ['qwen/qwen3-coder', 'anthropic/claude-sonnet-4.6', 'openai/gpt-5.5'],
|
|
145
|
+
discovery: {
|
|
146
|
+
kind: 'openai',
|
|
147
|
+
modelsUrl: () => 'https://openrouter.ai/api/v1/models',
|
|
148
|
+
authHeader: bearer,
|
|
149
|
+
filter: chatOnly,
|
|
150
|
+
},
|
|
151
|
+
},
|
|
152
|
+
{
|
|
153
|
+
id: 'ollama',
|
|
154
|
+
label: 'Ollama (local)',
|
|
155
|
+
apiKeyEnvironmentVariables: [],
|
|
156
|
+
// Models we have tested locally; only marked preferred when actually pulled.
|
|
157
|
+
preferredModels: ['qwen3-coder', 'qwen3', 'deepseek-r1', 'gemma3'],
|
|
158
|
+
discovery: {
|
|
159
|
+
kind: 'openai',
|
|
160
|
+
// Ollama serves an OpenAI-compatible /v1/models on the local daemon.
|
|
161
|
+
modelsUrl: (host) => `${host ?? resolveOllamaHost()}/v1/models`,
|
|
162
|
+
authHeader: () => ({}),
|
|
163
|
+
},
|
|
164
|
+
requiresExternalAuth: true,
|
|
165
|
+
},
|
|
166
|
+
];
|
|
167
|
+
/**
|
|
168
|
+
* Compile-time guard: every `availableDefaultConfigs` entry (including ollama)
|
|
169
|
+
* must have a model-discovery descriptor.
|
|
170
|
+
*/
|
|
171
|
+
const DESCRIPTOR_IDS = new Set(PROVIDER_DESCRIPTORS.map((d) => d.id));
|
|
172
|
+
for (const cfg of availableDefaultConfigs) {
|
|
173
|
+
if (!DESCRIPTOR_IDS.has(cfg)) {
|
|
174
|
+
// This is a developer error surfaced at module load only in debug runs.
|
|
175
|
+
displayDebug(`Provider "${cfg}" has no model-discovery descriptor.`);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Resolve the API key for a cloud provider by checking its env vars in order.
|
|
180
|
+
* @returns the matching env var name, or undefined when none is set.
|
|
181
|
+
*/
|
|
182
|
+
export function findApiKeyEnvVar(descriptor) {
|
|
183
|
+
for (const name of descriptor.apiKeyEnvironmentVariables) {
|
|
184
|
+
const value = env[name];
|
|
185
|
+
if (value && value.trim().length > 0) {
|
|
186
|
+
return name;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
return undefined;
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Build the {@link ModelInfo} list for a provider given the descriptor and an
|
|
193
|
+
* optional set of model ids known to actually exist (e.g. from a live
|
|
194
|
+
* `/v1/models` query or the local Ollama daemon).
|
|
195
|
+
*
|
|
196
|
+
* - When `discoveredModels` is omitted, the curated `preferredModels` are
|
|
197
|
+
* returned, all flagged ⭐ preferred.
|
|
198
|
+
* - When provided, every discovered model is listed; those that also appear in
|
|
199
|
+
* the curated `preferredModels` are flagged ⭐ preferred.
|
|
200
|
+
*/
|
|
201
|
+
export function buildModelList(descriptor, discoveredModels) {
|
|
202
|
+
if (!discoveredModels) {
|
|
203
|
+
return descriptor.preferredModels.map((id) => ({ id, preferred: true }));
|
|
204
|
+
}
|
|
205
|
+
const preferred = new Set(descriptor.preferredModels);
|
|
206
|
+
// Match preferred ids against the discovered tag, ignoring an explicit
|
|
207
|
+
// `:latest` suffix so `qwen3` flags `qwen3:latest`.
|
|
208
|
+
const isPreferred = (id) => preferred.has(id) || preferred.has(id.replace(/:latest$/, ''));
|
|
209
|
+
return discoveredModels.map((id) => ({ id, preferred: isPreferred(id) }));
|
|
210
|
+
}
|
|
211
|
+
/** Live-fetch timeout: short enough to never block first-run for long. */
|
|
212
|
+
const DISCOVERY_TIMEOUT_MS = 2000;
|
|
213
|
+
/**
|
|
214
|
+
* Parse the `id`s out of a models-endpoint payload. Both the OpenAI-compatible
|
|
215
|
+
* shape and Anthropic's native shape use a top-level `{ data: [{ id }] }`
|
|
216
|
+
* envelope, so a single parser covers both `kind`s.
|
|
217
|
+
*/
|
|
218
|
+
function parseModelIds(body) {
|
|
219
|
+
const data = body?.data;
|
|
220
|
+
if (!Array.isArray(data))
|
|
221
|
+
return [];
|
|
222
|
+
return data
|
|
223
|
+
.map((m) => m?.id)
|
|
224
|
+
.filter((id) => typeof id === 'string' && id.length > 0);
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Internal: run live discovery for one descriptor, distinguishing a successful
|
|
228
|
+
* live query from a curated fallback.
|
|
229
|
+
*
|
|
230
|
+
* @returns `{ models, live }` where `live` is true only when the models came
|
|
231
|
+
* from a successful, non-empty live `/v1/models` query (used as the Ollama
|
|
232
|
+
* availability signal). `live` is false for curated/fallback results.
|
|
233
|
+
*/
|
|
234
|
+
async function discoverModelsInternal(descriptor) {
|
|
235
|
+
const curated = () => ({
|
|
236
|
+
models: buildModelList(descriptor),
|
|
237
|
+
live: false,
|
|
238
|
+
});
|
|
239
|
+
const { discovery } = descriptor;
|
|
240
|
+
if (discovery.kind === 'none' || !discovery.modelsUrl) {
|
|
241
|
+
return curated();
|
|
242
|
+
}
|
|
243
|
+
// Resolve an API key for cloud providers; ollama needs none.
|
|
244
|
+
let key = '';
|
|
245
|
+
if (descriptor.id !== 'ollama') {
|
|
246
|
+
const envVar = findApiKeyEnvVar(descriptor);
|
|
247
|
+
if (!envVar) {
|
|
248
|
+
// No key → don't hit the network; show the curated catalog.
|
|
249
|
+
return curated();
|
|
250
|
+
}
|
|
251
|
+
key = env[envVar] ?? '';
|
|
252
|
+
}
|
|
253
|
+
try {
|
|
254
|
+
const url = discovery.modelsUrl(descriptor.id === 'ollama' ? resolveOllamaHost() : undefined);
|
|
255
|
+
const headers = {
|
|
256
|
+
Accept: 'application/json',
|
|
257
|
+
...(discovery.authHeader ? discovery.authHeader(key) : {}),
|
|
258
|
+
};
|
|
259
|
+
const res = await fetch(url, {
|
|
260
|
+
method: 'GET',
|
|
261
|
+
headers,
|
|
262
|
+
signal: AbortSignal.timeout(DISCOVERY_TIMEOUT_MS),
|
|
263
|
+
});
|
|
264
|
+
if (!res.ok) {
|
|
265
|
+
displayDebug(`Model discovery for "${descriptor.id}" returned HTTP ${res.status}.`);
|
|
266
|
+
return curated();
|
|
267
|
+
}
|
|
268
|
+
const body = await res.json();
|
|
269
|
+
let ids = parseModelIds(body);
|
|
270
|
+
if (discovery.filter) {
|
|
271
|
+
ids = ids.filter(discovery.filter);
|
|
272
|
+
}
|
|
273
|
+
if (ids.length === 0) {
|
|
274
|
+
displayDebug(`Model discovery for "${descriptor.id}" returned no usable models.`);
|
|
275
|
+
return curated();
|
|
276
|
+
}
|
|
277
|
+
return { models: buildModelList(descriptor, ids), live: true };
|
|
278
|
+
}
|
|
279
|
+
catch (e) {
|
|
280
|
+
displayDebug(`Model discovery for "${descriptor.id}" failed: ${e instanceof Error ? e.message : String(e)}`);
|
|
281
|
+
return curated();
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* Discover the models for a single provider.
|
|
286
|
+
*
|
|
287
|
+
* - `kind: 'none'` → returns the curated `buildModelList(descriptor)`.
|
|
288
|
+
* - `kind: 'openai' | 'anthropic'` → fetches the models endpoint with a short
|
|
289
|
+
* timeout, parses `data[].id`, applies the chat-only `filter`, and overlays
|
|
290
|
+
* the ⭐ preferred flags via `buildModelList(descriptor, liveIds)`.
|
|
291
|
+
*
|
|
292
|
+
* Best-effort: any error / non-2xx / malformed / empty payload falls back to
|
|
293
|
+
* the curated list. This function NEVER throws — a bad key must degrade to the
|
|
294
|
+
* curated catalog, not break first-run config.
|
|
295
|
+
*
|
|
296
|
+
* Cloud providers are only probed live when an API key is present; without a key
|
|
297
|
+
* the curated catalog is returned directly. Ollama (no key, local daemon) is
|
|
298
|
+
* always probed.
|
|
299
|
+
*/
|
|
300
|
+
export async function discoverModels(providerId) {
|
|
301
|
+
const descriptor = PROVIDER_DESCRIPTORS.find((d) => d.id === providerId);
|
|
302
|
+
if (!descriptor) {
|
|
303
|
+
throw new Error(`Unknown provider: ${providerId}`);
|
|
304
|
+
}
|
|
305
|
+
const { models } = await discoverModelsInternal(descriptor);
|
|
306
|
+
return models;
|
|
307
|
+
}
|
|
308
|
+
/**
|
|
309
|
+
* List the models for a single provider.
|
|
310
|
+
*
|
|
311
|
+
* Live-discovers from the provider's models endpoint where possible (with a
|
|
312
|
+
* curated fallback); returns the curated set for `kind: 'none'` providers. Does
|
|
313
|
+
* not require the provider to be "available".
|
|
314
|
+
*/
|
|
315
|
+
export async function listModels(providerId) {
|
|
316
|
+
return discoverModels(providerId);
|
|
317
|
+
}
|
|
318
|
+
/**
|
|
319
|
+
* Detect every known provider on this machine and list its models.
|
|
320
|
+
*
|
|
321
|
+
* - Cloud providers are `available` when one of their API-key env vars is set;
|
|
322
|
+
* their model list is live-discovered (curated fallback) when a key is present.
|
|
323
|
+
* - `vertexai` is reported with `requiresExternalAuth: true` and
|
|
324
|
+
* `available: false`; usability via gcloud ADC cannot be cheaply verified
|
|
325
|
+
* here and is left to the caller / a live run.
|
|
326
|
+
* - `ollama` is `available` when the local daemon's `/v1/models` responds, and
|
|
327
|
+
* its model list is that live inventory.
|
|
328
|
+
*
|
|
329
|
+
* @param options.includeUnavailable when true (default), every provider is
|
|
330
|
+
* returned (so a dialog can offer "set a key" flows); when false, only
|
|
331
|
+
* available providers are returned.
|
|
332
|
+
*/
|
|
333
|
+
export async function detectProviders(options = {}) {
|
|
334
|
+
const { includeUnavailable = true } = options;
|
|
335
|
+
const results = [];
|
|
336
|
+
for (const descriptor of PROVIDER_DESCRIPTORS) {
|
|
337
|
+
if (descriptor.id === 'ollama') {
|
|
338
|
+
// A successful, non-empty /v1/models probe = the daemon is available.
|
|
339
|
+
const { models, live } = await discoverModelsInternal(descriptor);
|
|
340
|
+
results.push({
|
|
341
|
+
id: descriptor.id,
|
|
342
|
+
label: descriptor.label,
|
|
343
|
+
available: live,
|
|
344
|
+
requiresExternalAuth: true,
|
|
345
|
+
models,
|
|
346
|
+
});
|
|
347
|
+
continue;
|
|
348
|
+
}
|
|
349
|
+
const apiKeyEnvironmentVariable = findApiKeyEnvVar(descriptor);
|
|
350
|
+
const models = await discoverModels(descriptor.id);
|
|
351
|
+
results.push({
|
|
352
|
+
id: descriptor.id,
|
|
353
|
+
label: descriptor.label,
|
|
354
|
+
available: Boolean(apiKeyEnvironmentVariable),
|
|
355
|
+
apiKeyEnvironmentVariable,
|
|
356
|
+
requiresExternalAuth: Boolean(descriptor.requiresExternalAuth),
|
|
357
|
+
models,
|
|
358
|
+
});
|
|
359
|
+
}
|
|
360
|
+
return includeUnavailable ? results : results.filter((p) => p.available);
|
|
361
|
+
}
|
|
362
|
+
//# sourceMappingURL=modelDiscovery.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"modelDiscovery.js","sourceRoot":"","sources":["../../src/providers/modelDiscovery.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,5 @@
|
|
|
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
|
+
export declare function processJsonConfig(llmConfig: OpenAIChatInput & ChatOpenAIFields & BaseChatModelParams): Promise<BaseChatModel>;
|
|
5
|
+
export declare function init(configFileName: string): void;
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { displayWarning } from '#src/utils/consoleUtils.js';
|
|
2
|
+
import { env } from '#src/utils/systemUtils.js';
|
|
3
|
+
import { writeFileIfNotExistsWithMessages } from '#src/utils/fileUtils.js';
|
|
4
|
+
/**
|
|
5
|
+
* Default Ollama daemon host, matching the Ollama CLI/library default. The
|
|
6
|
+
* OpenAI-compatible surface lives under `/v1` on this host. Kept in sync with
|
|
7
|
+
* `DEFAULT_OLLAMA_HOST` in `modelDiscovery.ts`.
|
|
8
|
+
*/
|
|
9
|
+
const DEFAULT_OLLAMA_HOST = 'http://127.0.0.1:11434';
|
|
10
|
+
/**
|
|
11
|
+
* Curated default model — mirrors the first ⭐ preferred model advertised for
|
|
12
|
+
* ollama by first-run discovery (`PROVIDER_DESCRIPTORS` in modelDiscovery.ts).
|
|
13
|
+
*/
|
|
14
|
+
const DEFAULT_OLLAMA_MODEL = 'qwen3-coder';
|
|
15
|
+
/**
|
|
16
|
+
* Ollama serves an unauthenticated local daemon, but `ChatOpenAI` requires a
|
|
17
|
+
* non-empty `apiKey` string. Send a harmless placeholder so the client builds;
|
|
18
|
+
* the local daemon ignores it.
|
|
19
|
+
*/
|
|
20
|
+
const OLLAMA_PLACEHOLDER_API_KEY = 'ollama';
|
|
21
|
+
/**
|
|
22
|
+
* Resolve the OpenAI-compatible base URL for the local Ollama daemon.
|
|
23
|
+
*
|
|
24
|
+
* Honors the `OLLAMA_HOST` env override (the same variable the Ollama CLI uses).
|
|
25
|
+
* `OLLAMA_HOST` is typically a full URL (`http://127.0.0.1:11434`) or a bare
|
|
26
|
+
* `host:port`; either form is normalized to a `http(s)://host[:port]/v1` base.
|
|
27
|
+
*/
|
|
28
|
+
function resolveBaseUrl() {
|
|
29
|
+
const host = env.OLLAMA_HOST;
|
|
30
|
+
let base;
|
|
31
|
+
if (!host) {
|
|
32
|
+
base = DEFAULT_OLLAMA_HOST;
|
|
33
|
+
}
|
|
34
|
+
else if (/^https?:\/\//.test(host)) {
|
|
35
|
+
base = host.replace(/\/+$/, '');
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
base = `http://${host}`.replace(/\/+$/, '');
|
|
39
|
+
}
|
|
40
|
+
return `${base}/v1`;
|
|
41
|
+
}
|
|
42
|
+
// Function to process JSON config and create an Ollama (OpenAI-compatible) LLM instance
|
|
43
|
+
// noinspection JSUnusedGlobalSymbols
|
|
44
|
+
export async function processJsonConfig(llmConfig) {
|
|
45
|
+
const { ChatOpenAI } = await import('@langchain/openai');
|
|
46
|
+
// Ollama is local and unauthenticated; ChatOpenAI still needs a non-empty key.
|
|
47
|
+
const apiKey = llmConfig.apiKey || OLLAMA_PLACEHOLDER_API_KEY;
|
|
48
|
+
const configFields = {
|
|
49
|
+
...llmConfig,
|
|
50
|
+
apiKey,
|
|
51
|
+
model: llmConfig.model || DEFAULT_OLLAMA_MODEL,
|
|
52
|
+
configuration: {
|
|
53
|
+
baseURL: resolveBaseUrl(),
|
|
54
|
+
...(llmConfig.configuration || {}),
|
|
55
|
+
},
|
|
56
|
+
};
|
|
57
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
58
|
+
delete configFields.type;
|
|
59
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
60
|
+
delete configFields.apiKeyEnvironmentVariable;
|
|
61
|
+
return new ChatOpenAI(configFields);
|
|
62
|
+
}
|
|
63
|
+
const jsonContent = `{
|
|
64
|
+
"llm": {
|
|
65
|
+
"type": "ollama",
|
|
66
|
+
"model": "qwen3-coder"
|
|
67
|
+
}
|
|
68
|
+
}`;
|
|
69
|
+
export function init(configFileName) {
|
|
70
|
+
// Determine which content to use based on file extension
|
|
71
|
+
if (!configFileName.endsWith('.json')) {
|
|
72
|
+
throw new Error('Only JSON config is supported.');
|
|
73
|
+
}
|
|
74
|
+
writeFileIfNotExistsWithMessages(configFileName, jsonContent);
|
|
75
|
+
displayWarning(`You need to edit your ${configFileName} to configure the model. ` +
|
|
76
|
+
'Ollama runs locally and needs no API key; set OLLAMA_HOST if your daemon ' +
|
|
77
|
+
'is not on the default http://127.0.0.1:11434.');
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=ollama.js.map
|
|
@@ -0,0 +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;AAQhD,OAAO,EAAE,gCAAgC,EAAE,MAAM,yBAAyB,CAAC;AAE3E;;;;GAIG;AACH,MAAM,mBAAmB,GAAG,wBAAwB,CAAC;AAErD;;;GAGG;AACH,MAAM,oBAAoB,GAAG,aAAa,CAAC;AAE3C;;;;GAIG;AACH,MAAM,0BAA0B,GAAG,QAAQ,CAAC;AAE5C;;;;;;GAMG;AACH,SAAS,cAAc;IACrB,MAAM,IAAI,GAAG,GAAG,CAAC,WAAW,CAAC;IAC7B,IAAI,IAAY,CAAC;IACjB,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,IAAI,GAAG,mBAAmB,CAAC;IAC7B,CAAC;SAAM,IAAI,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QACrC,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IAClC,CAAC;SAAM,CAAC;QACN,IAAI,GAAG,UAAU,IAAI,EAAE,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IAC9C,CAAC;IACD,OAAO,GAAG,IAAI,KAAK,CAAC;AACtB,CAAC;AAED,wFAAwF;AACxF,qCAAqC;AACrC,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,SAAmE;IAEnE,MAAM,EAAE,UAAU,EAAE,GAAG,MAAM,MAAM,CAAC,mBAAmB,CAAC,CAAC;IACzD,+EAA+E;IAC/E,MAAM,MAAM,GAAG,SAAS,CAAC,MAAM,IAAI,0BAA0B,CAAC;IAC9D,MAAM,YAAY,GAAG;QACnB,GAAG,SAAS;QACZ,MAAM;QACN,KAAK,EAAE,SAAS,CAAC,KAAK,IAAI,oBAAoB;QAC9C,aAAa,EAAE;YACb,OAAO,EAAE,cAAc,EAAE;YACzB,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;IAEvD,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,yBAAyB,cAAc,2BAA2B;QAChE,2EAA2E;QAC3E,+CAA+C,CAClD,CAAC;AACJ,CAAC"}
|
package/dist/providers/openai.js
CHANGED
|
@@ -1,6 +1,20 @@
|
|
|
1
1
|
import { displayWarning } from '#src/utils/consoleUtils.js';
|
|
2
2
|
import { env } from '#src/utils/systemUtils.js';
|
|
3
3
|
import { writeFileIfNotExistsWithMessages } from '#src/utils/fileUtils.js';
|
|
4
|
+
/**
|
|
5
|
+
* OpenAI reasoning-model families that reject any non-default `temperature`: the API 400s with
|
|
6
|
+
* "Unsupported value: 'temperature' does not support 0 ... Only the default (1) value is
|
|
7
|
+
* supported." (confirmed live for gpt-5.x, o3-mini, o4-mini). The denylist matches the
|
|
8
|
+
* `gpt-5` family and the `o<digit>` series (o1/o3/o4/...) so it stays future-proof for new
|
|
9
|
+
* minor/point releases (gpt-5.5, o5, etc.) without enumerating every id. Models like `gpt-4o`
|
|
10
|
+
* are NOT matched and keep their configured temperature.
|
|
11
|
+
*/
|
|
12
|
+
const TEMPERATURE_RESTRICTED_MODEL = /^(gpt-5|o\d)/i;
|
|
13
|
+
/** The only `temperature` value OpenAI reasoning models accept (their fixed default). */
|
|
14
|
+
const SUPPORTED_DEFAULT_TEMPERATURE = 1;
|
|
15
|
+
function isTemperatureRestrictedModel(model) {
|
|
16
|
+
return !!model && TEMPERATURE_RESTRICTED_MODEL.test(model);
|
|
17
|
+
}
|
|
4
18
|
// Function to process JSON config and create OpenAI LLM instance
|
|
5
19
|
// noinspection JSUnusedGlobalSymbols
|
|
6
20
|
export async function processJsonConfig(llmConfig) {
|
|
@@ -10,12 +24,24 @@ export async function processJsonConfig(llmConfig) {
|
|
|
10
24
|
const configFields = {
|
|
11
25
|
...llmConfig,
|
|
12
26
|
apiKey: openaiApiKey,
|
|
13
|
-
model: llmConfig.model || 'gpt-
|
|
27
|
+
model: llmConfig.model || 'gpt-5.5',
|
|
14
28
|
};
|
|
15
29
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
16
30
|
delete configFields.type;
|
|
17
31
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
18
32
|
delete configFields.apiKeyEnvironmentVariable;
|
|
33
|
+
// OpenAI reasoning models (gpt-5.x, o-series) reject any non-default temperature with a 400.
|
|
34
|
+
// A user setting `temperature: 0` (e.g. via `exec -t 0` for determinism) would otherwise fail
|
|
35
|
+
// even though the model is valid. Drop the unsupported temperature and warn rather than 400.
|
|
36
|
+
if (isTemperatureRestrictedModel(configFields.model) &&
|
|
37
|
+
configFields.temperature !== undefined &&
|
|
38
|
+
configFields.temperature !== SUPPORTED_DEFAULT_TEMPERATURE) {
|
|
39
|
+
displayWarning(`Model "${configFields.model}" does not support a custom temperature ` +
|
|
40
|
+
`(only the default ${SUPPORTED_DEFAULT_TEMPERATURE} is allowed); ` +
|
|
41
|
+
`ignoring the configured temperature of ${configFields.temperature}.`);
|
|
42
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
43
|
+
delete configFields.temperature;
|
|
44
|
+
}
|
|
19
45
|
return new ChatOpenAI(configFields);
|
|
20
46
|
}
|
|
21
47
|
function getApiKey(llmConfig) {
|
|
@@ -31,7 +57,7 @@ function getApiKey(llmConfig) {
|
|
|
31
57
|
const jsonContent = `{
|
|
32
58
|
"llm": {
|
|
33
59
|
"type": "openai",
|
|
34
|
-
"model": "gpt-
|
|
60
|
+
"model": "gpt-5.5"
|
|
35
61
|
}
|
|
36
62
|
}`;
|
|
37
63
|
export function init(configFileName) {
|
|
@@ -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,gCAAgC,EAAE,MAAM,yBAAyB,CAAC;AAE3E,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,
|
|
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,gCAAgC,EAAE,MAAM,yBAAyB,CAAC;AAE3E;;;;;;;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,SAAS;KACpC,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,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,gDAAgD,CACnD,CAAC;AACJ,CAAC"}
|
|
@@ -15,7 +15,7 @@ import { writeFileIfNotExistsWithMessages } from '#src/utils/fileUtils.js';
|
|
|
15
15
|
const jsonContent = `{
|
|
16
16
|
"llm": {
|
|
17
17
|
"type": "vertexai",
|
|
18
|
-
"model": "gemini-
|
|
18
|
+
"model": "gemini-3.5-flash"
|
|
19
19
|
}
|
|
20
20
|
}`;
|
|
21
21
|
export function init(configFileName) {
|
|
@@ -31,7 +31,7 @@ export async function processJsonConfig(llmConfig) {
|
|
|
31
31
|
const { ChatGoogle } = await import('@langchain/google/node');
|
|
32
32
|
const configFields = {
|
|
33
33
|
...llmConfig,
|
|
34
|
-
model: llmConfig.model || 'gemini-
|
|
34
|
+
model: llmConfig.model || 'gemini-3.5-flash',
|
|
35
35
|
vertexai: true,
|
|
36
36
|
};
|
|
37
37
|
delete configFields.type;
|
|
@@ -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,gCAAgC,EAAE,MAAM,yBAAyB,CAAC;AAE3E,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,+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,
|
|
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,gCAAgC,EAAE,MAAM,yBAAyB,CAAC;AAE3E,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,+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,kBAAkB;QAC5C,QAAQ,EAAE,IAAI;KACf,CAAC;IACF,OAAO,YAAY,CAAC,IAAI,CAAC;IACzB,OAAO,YAAY,CAAC,yBAAyB,CAAC;IAC9C,OAAO,IAAI,UAAU,CAAC,YAAY,CAAC,CAAC;AACtC,CAAC"}
|
package/dist/providers/xai.js
CHANGED
|
@@ -9,13 +9,13 @@ export async function processJsonConfig(llmConfig) {
|
|
|
9
9
|
return new ChatXAI({
|
|
10
10
|
...llmConfig,
|
|
11
11
|
apiKey,
|
|
12
|
-
model: llmConfig.model || 'grok-4
|
|
12
|
+
model: llmConfig.model || 'grok-4.3',
|
|
13
13
|
});
|
|
14
14
|
}
|
|
15
15
|
const jsonContent = `{
|
|
16
16
|
"llm": {
|
|
17
17
|
"type": "xai",
|
|
18
|
-
"model": "grok-4
|
|
18
|
+
"model": "grok-4.3"
|
|
19
19
|
}
|
|
20
20
|
}`;
|
|
21
21
|
export function init(configFileName) {
|
|
@@ -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,gCAAgC,EAAE,MAAM,yBAAyB,CAAC;AAE3E,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,OAAO,IAAI,OAAO,CAAC;QACjB,GAAG,SAAS;QACZ,MAAM;QACN,KAAK,EAAE,SAAS,CAAC,KAAK,IAAI,
|
|
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,gCAAgC,EAAE,MAAM,yBAAyB,CAAC;AAE3E,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,OAAO,IAAI,OAAO,CAAC;QACjB,GAAG,SAAS;QACZ,MAAM;QACN,KAAK,EAAE,SAAS,CAAC,KAAK,IAAI,UAAU;KACrC,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,4BAA4B;QACnE,6CAA6C,CAChD,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { GthConfig } from '#src/config.js';
|
|
2
|
+
import type { AgentResolvers, GthCommand } from '#src/core/types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Ask a question and get an answer from the LLM.
|
|
5
|
+
*
|
|
6
|
+
* This is the shared, non-interactive single-shot runtime behind both the conversational
|
|
7
|
+
* `ask` command and the scripted `exec` command (prompt-as-script). The `command` argument
|
|
8
|
+
* is forwarded to the agent so it can pick the right mode prompt (e.g. exec-mode for `exec`).
|
|
9
|
+
*
|
|
10
|
+
* @param source - The source of the question (used for file naming)
|
|
11
|
+
* @param preamble - The preamble to send to the LLM
|
|
12
|
+
* @param content - The content of the question
|
|
13
|
+
* @param config - The resolved config
|
|
14
|
+
* @param resolvers - Optional agent resolvers (tools/middleware)
|
|
15
|
+
* @param command - The originating command (defaults to `ask`); selects the agent mode prompt
|
|
16
|
+
* @returns `true` when the run completed without error, `false` when it failed (so callers
|
|
17
|
+
* such as `exec` can set a non-zero exit code).
|
|
18
|
+
*/
|
|
19
|
+
export declare function runSingleShot(source: string, preamble: string, content: string, config: GthConfig, resolvers?: AgentResolvers, command?: GthCommand): Promise<boolean>;
|