@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.
- package/.gsloth.code.md +10 -0
- package/README.md +6 -6
- package/dist/config/defaults.d.ts +85 -0
- package/dist/config/defaults.js +103 -0
- package/dist/config/defaults.js.map +1 -0
- package/dist/config/jsonc.d.ts +12 -0
- package/dist/config/jsonc.js +41 -0
- package/dist/config/jsonc.js.map +1 -0
- package/dist/config/loader.d.ts +169 -0
- package/dist/config/loader.js +872 -0
- package/dist/config/loader.js.map +1 -0
- package/dist/config/schema.d.ts +673 -0
- package/dist/config/schema.js +524 -0
- package/dist/config/schema.js.map +1 -0
- package/dist/config/shell-policy.d.ts +331 -0
- package/dist/config/shell-policy.js +238 -0
- package/dist/config/shell-policy.js.map +1 -0
- package/dist/config/types.d.ts +596 -0
- package/dist/config/types.js +13 -0
- package/dist/config/types.js.map +1 -0
- package/dist/config.d.ts +18 -647
- package/dist/config.js +15 -516
- package/dist/config.js.map +1 -1
- package/dist/constants.d.ts +42 -0
- package/dist/constants.js +42 -0
- package/dist/constants.js.map +1 -1
- package/dist/core/GthAbstractAgent.d.ts +85 -2
- package/dist/core/GthAbstractAgent.js +398 -28
- package/dist/core/GthAbstractAgent.js.map +1 -1
- package/dist/core/GthAgentRunner.d.ts +158 -1
- package/dist/core/GthAgentRunner.js +367 -4
- package/dist/core/GthAgentRunner.js.map +1 -1
- package/dist/core/GthLangChainAgent.js +193 -16
- package/dist/core/GthLangChainAgent.js.map +1 -1
- package/dist/core/debugCapture.d.ts +59 -0
- package/dist/core/debugCapture.js +101 -0
- package/dist/core/debugCapture.js.map +1 -0
- package/dist/core/gthLeanAgentFactory.d.ts +9 -0
- package/dist/core/gthLeanAgentFactory.js +10 -0
- package/dist/core/gthLeanAgentFactory.js.map +1 -0
- package/dist/core/plainToolIndication.d.ts +15 -0
- package/dist/core/plainToolIndication.js +148 -0
- package/dist/core/plainToolIndication.js.map +1 -0
- package/dist/core/runStats.d.ts +41 -0
- package/dist/core/runStats.js +73 -0
- package/dist/core/runStats.js.map +1 -0
- package/dist/core/shell/ShellCommandFailedError.d.ts +54 -0
- package/dist/core/shell/ShellCommandFailedError.js +68 -0
- package/dist/core/shell/ShellCommandFailedError.js.map +1 -0
- package/dist/core/shell/allowlist.d.ts +75 -0
- package/dist/core/shell/allowlist.js +187 -0
- package/dist/core/shell/allowlist.js.map +1 -0
- package/dist/core/shell/arity.d.ts +75 -0
- package/dist/core/shell/arity.js +313 -0
- package/dist/core/shell/arity.js.map +1 -0
- package/dist/core/shell/judge.d.ts +161 -0
- package/dist/core/shell/judge.js +261 -0
- package/dist/core/shell/judge.js.map +1 -0
- package/dist/core/shell/normalize.d.ts +27 -0
- package/dist/core/shell/normalize.js +53 -0
- package/dist/core/shell/normalize.js.map +1 -0
- package/dist/core/toolDisplay.d.ts +101 -0
- package/dist/core/toolDisplay.js +374 -0
- package/dist/core/toolDisplay.js.map +1 -0
- package/dist/core/toolOutputChannel.d.ts +68 -0
- package/dist/core/toolOutputChannel.js +112 -0
- package/dist/core/toolOutputChannel.js.map +1 -0
- package/dist/core/types.d.ts +173 -0
- package/dist/core/types.js.map +1 -1
- package/dist/history/historyFormat.d.ts +28 -0
- package/dist/history/historyFormat.js +127 -0
- package/dist/history/historyFormat.js.map +1 -0
- package/dist/history/historyStore.d.ts +198 -0
- package/dist/history/historyStore.js +482 -0
- package/dist/history/historyStore.js.map +1 -0
- package/dist/history/recordSession.d.ts +37 -0
- package/dist/history/recordSession.js +56 -0
- package/dist/history/recordSession.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/providers/anthropic.d.ts +1 -1
- package/dist/providers/anthropic.js +5 -10
- package/dist/providers/anthropic.js.map +1 -1
- package/dist/providers/deepseek.d.ts +1 -1
- package/dist/providers/deepseek.js +5 -10
- package/dist/providers/deepseek.js.map +1 -1
- package/dist/providers/geminiSchemaSanitizer.d.ts +52 -0
- package/dist/providers/geminiSchemaSanitizer.js +201 -0
- package/dist/providers/geminiSchemaSanitizer.js.map +1 -0
- package/dist/providers/google-genai.d.ts +1 -1
- package/dist/providers/google-genai.js +9 -11
- package/dist/providers/google-genai.js.map +1 -1
- package/dist/providers/groq.d.ts +1 -1
- package/dist/providers/groq.js +5 -10
- 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 +111 -3
- package/dist/providers/modelDiscovery.js +183 -27
- package/dist/providers/modelDiscovery.js.map +1 -1
- package/dist/providers/ollama.d.ts +19 -5
- package/dist/providers/ollama.js +60 -52
- package/dist/providers/ollama.js.map +1 -1
- package/dist/providers/openai.d.ts +1 -1
- package/dist/providers/openai.js +5 -10
- package/dist/providers/openai.js.map +1 -1
- package/dist/providers/openrouter.d.ts +1 -1
- package/dist/providers/openrouter.js +12 -12
- package/dist/providers/openrouter.js.map +1 -1
- package/dist/providers/vertexai.d.ts +1 -1
- package/dist/providers/vertexai.js +9 -11
- package/dist/providers/vertexai.js.map +1 -1
- package/dist/providers/xai.d.ts +1 -1
- package/dist/providers/xai.js +5 -10
- package/dist/providers/xai.js.map +1 -1
- package/dist/runtime/askStructured.d.ts +62 -0
- package/dist/runtime/askStructured.js +76 -0
- package/dist/runtime/askStructured.js.map +1 -0
- package/dist/runtime/conversation.d.ts +59 -0
- package/dist/runtime/conversation.js +137 -0
- package/dist/runtime/conversation.js.map +1 -0
- package/dist/runtime/singleShot.d.ts +24 -5
- package/dist/runtime/singleShot.js +56 -9
- package/dist/runtime/singleShot.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 +22 -0
- package/dist/utils/consoleUtils.js +45 -0
- package/dist/utils/consoleUtils.js.map +1 -1
- package/dist/utils/debugDump.d.ts +57 -0
- package/dist/utils/debugDump.js +236 -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/fileUtils.d.ts +24 -2
- package/dist/utils/fileUtils.js +54 -12
- package/dist/utils/fileUtils.js.map +1 -1
- package/dist/utils/llmUtils.js.map +1 -1
- package/dist/utils/redactSecrets.d.ts +63 -0
- package/dist/utils/redactSecrets.js +238 -0
- package/dist/utils/redactSecrets.js.map +1 -0
- package/dist/utils/systemPromptNotes.d.ts +181 -0
- package/dist/utils/systemPromptNotes.js +298 -0
- package/dist/utils/systemPromptNotes.js.map +1 -0
- package/dist/utils/systemUtils.d.ts +32 -1
- package/dist/utils/systemUtils.js +50 -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/package.json +17 -8
- package/schema/gsloth-config.schema.json +2111 -0
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import type { ProviderId } from '#src/providers/modelDiscovery.js';
|
|
2
|
+
/** The single models.dev dataset endpoint (whole catalog; sliced per-provider on our side). */
|
|
3
|
+
export declare const MODELS_DEV_URL = "https://models.dev/api.json";
|
|
4
|
+
/**
|
|
5
|
+
* Cache freshness window. Pricing/limit metadata is near-static, so a day between refreshes is
|
|
6
|
+
* plenty; `gth models --refresh` is the manual escape hatch when a newer number is needed sooner.
|
|
7
|
+
*/
|
|
8
|
+
export declare const CATALOG_TTL_MS: number;
|
|
9
|
+
/**
|
|
10
|
+
* Catalog fetch timeout. Deliberately generous (and separate from `/v1/models` discovery's 2 s
|
|
11
|
+
* interactive-path probe): `api.json` is a few MB and this fetch runs off the explicit `gth models`
|
|
12
|
+
* cache-fill path, not a first-run keystroke, so latency headroom matters more than snappiness.
|
|
13
|
+
*/
|
|
14
|
+
export declare const CATALOG_TIMEOUT_MS = 10000;
|
|
15
|
+
/** Shown next to any enriched price/metadata. Costs from models.dev are US$ per 1M tokens. */
|
|
16
|
+
export declare const CATALOG_ATTRIBUTION = "* model prices provided by models.dev";
|
|
17
|
+
/**
|
|
18
|
+
* Map a Gaunt Sloth {@link ProviderId} to its models.dev top-level provider key. Cloud providers
|
|
19
|
+
* only; local/self-hosted providers (`ollama`) are intentionally absent so they get NO catalog
|
|
20
|
+
* lookup. Returns `undefined` for any provider without a models.dev slice.
|
|
21
|
+
*/
|
|
22
|
+
export declare const MODELS_DEV_PROVIDER_KEY: Partial<Record<ProviderId, string>>;
|
|
23
|
+
/** Per-token cost metadata (US$ per 1M tokens), as published by models.dev. */
|
|
24
|
+
export interface ModelCost {
|
|
25
|
+
input?: number;
|
|
26
|
+
output?: number;
|
|
27
|
+
cacheRead?: number;
|
|
28
|
+
cacheWrite?: number;
|
|
29
|
+
}
|
|
30
|
+
/** Context / output token limits. */
|
|
31
|
+
export interface ModelLimit {
|
|
32
|
+
context?: number;
|
|
33
|
+
output?: number;
|
|
34
|
+
}
|
|
35
|
+
/** Input/output modalities (e.g. `text`, `image`, `pdf`). */
|
|
36
|
+
export interface ModelModalities {
|
|
37
|
+
input?: string[];
|
|
38
|
+
output?: string[];
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Normalised catalog metadata for one model id — the models.dev fields we surface, camelCased and
|
|
42
|
+
* detached from models.dev's raw JSON shape so a schema change there is absorbed in the parser.
|
|
43
|
+
*/
|
|
44
|
+
export interface ModelCatalogEntry {
|
|
45
|
+
/** models.dev display name (e.g. "Claude Opus 4.5"). */
|
|
46
|
+
name?: string;
|
|
47
|
+
cost?: ModelCost;
|
|
48
|
+
limit?: ModelLimit;
|
|
49
|
+
modalities?: ModelModalities;
|
|
50
|
+
/** True when the model exposes an extended-reasoning / thinking mode. */
|
|
51
|
+
reasoning?: boolean;
|
|
52
|
+
/** True when the model supports tool/function calling. */
|
|
53
|
+
toolCall?: boolean;
|
|
54
|
+
}
|
|
55
|
+
/** A provider's catalog slice: model-id → metadata, plus provenance for TTL / display. */
|
|
56
|
+
export interface ProviderCatalog {
|
|
57
|
+
/** Gaunt Sloth provider id this slice belongs to. */
|
|
58
|
+
providerId: ProviderId;
|
|
59
|
+
/** models.dev provider key it was sliced from. */
|
|
60
|
+
providerKey: string;
|
|
61
|
+
/** Epoch ms the slice was fetched (drives TTL freshness). */
|
|
62
|
+
fetchedAt: number;
|
|
63
|
+
/** Metadata keyed by model id, exactly as models.dev keys them. */
|
|
64
|
+
models: Record<string, ModelCatalogEntry>;
|
|
65
|
+
}
|
|
66
|
+
/** Options shared by catalog reads; every field is injectable for hermetic tests. */
|
|
67
|
+
export interface CatalogOptions {
|
|
68
|
+
/** Force a network refresh even when a fresh cache exists (backs `gth models --refresh`). */
|
|
69
|
+
refresh?: boolean;
|
|
70
|
+
/** Override the cache directory (defaults to `~/.gsloth/model-catalog`). */
|
|
71
|
+
cacheDir?: string;
|
|
72
|
+
/** Override "now" for TTL math (defaults to `Date.now()`). */
|
|
73
|
+
now?: number;
|
|
74
|
+
/** Override the fetch implementation (defaults to the global `fetch`). */
|
|
75
|
+
fetchImpl?: typeof fetch;
|
|
76
|
+
/** Override the TTL window in ms (defaults to {@link CATALOG_TTL_MS}). */
|
|
77
|
+
ttlMs?: number;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Get the models.dev catalog slice for one provider, cache-first.
|
|
81
|
+
*
|
|
82
|
+
* - **Local / self-hosted** (`ollama`, or any provider without a {@link MODELS_DEV_PROVIDER_KEY}):
|
|
83
|
+
* returns `null` immediately, with **no network call**.
|
|
84
|
+
* - **Cloud, fresh cache** (and not `refresh`): returns the cached slice, no network call.
|
|
85
|
+
* - **Cloud, missing / stale cache, or `refresh`**: fetches `api.json` once, slices this provider,
|
|
86
|
+
* writes the slice to the per-provider cache, and returns it.
|
|
87
|
+
* - **Fetch fails**: returns the stale cached slice if one exists (degrade, don't blank), else `null`.
|
|
88
|
+
*
|
|
89
|
+
* NEVER throws — catalog availability must never block model use.
|
|
90
|
+
*/
|
|
91
|
+
export declare function getProviderCatalog(providerId: ProviderId, options?: CatalogOptions): Promise<ProviderCatalog | null>;
|
|
92
|
+
/** A {@link ModelInfo}-shaped record decorated with optional catalog metadata. */
|
|
93
|
+
export interface EnrichedModel {
|
|
94
|
+
id: string;
|
|
95
|
+
preferred: boolean;
|
|
96
|
+
/** models.dev metadata for this id, when the catalog has an entry; absent = unenriched. */
|
|
97
|
+
enrichment?: ModelCatalogEntry;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Enrich a provider's discovered model list with catalog metadata, cache-first.
|
|
101
|
+
*
|
|
102
|
+
* `/v1/models` stays authoritative: every input model is returned, in order, whether or not
|
|
103
|
+
* models.dev knows it. Matched ids gain an `enrichment`; unmatched ids (and every model when the
|
|
104
|
+
* catalog is unavailable or the provider is local) are returned untouched. NEVER throws.
|
|
105
|
+
*/
|
|
106
|
+
export declare function enrichModels(providerId: ProviderId, models: ReadonlyArray<{
|
|
107
|
+
id: string;
|
|
108
|
+
preferred: boolean;
|
|
109
|
+
}>, options?: CatalogOptions): Promise<EnrichedModel[]>;
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @packageDocumentation
|
|
3
|
+
* Model catalog (GS2-6 / B16) — enriches CLOUD model entries with cost, context-limit and
|
|
4
|
+
* capability metadata sourced from {@link https://models.dev | models.dev} (MIT-licensed).
|
|
5
|
+
*
|
|
6
|
+
* ## What this is (and is not)
|
|
7
|
+
* This is an **enrichment** layer, never a gate. The authoritative set of callable models stays
|
|
8
|
+
* `/v1/models` live discovery (see `modelDiscovery.ts`). models.dev only decorates cloud model
|
|
9
|
+
* ids with `{ cost, limit, modalities, reasoning, toolCall }` where a match exists; a cloud model
|
|
10
|
+
* that models.dev has never heard of is still listed and still callable, just unenriched. If
|
|
11
|
+
* models.dev is unreachable (offline / on-prem no-egress / down) every model keeps working — it
|
|
12
|
+
* simply carries no metadata until the cache fills. Nothing here ever blocks model use.
|
|
13
|
+
*
|
|
14
|
+
* ## Fetch / cache shape
|
|
15
|
+
* models.dev publishes a **single** dataset at {@link MODELS_DEV_URL} (`api.json`, ~3 MB); there is
|
|
16
|
+
* no per-provider endpoint. So "lazy, per-provider" is realised on the **cache side**: on a miss for
|
|
17
|
+
* provider *X* we fetch `api.json` once, **slice out just X's models**, and persist that slice to a
|
|
18
|
+
* per-provider cache file (`~/.gsloth/model-catalog/<providerKey>.json`). Subsequent reads are served
|
|
19
|
+
* **cache-first** from that per-provider file and never touch the network within the {@link CATALOG_TTL_MS}
|
|
20
|
+
* TTL. A stale cache is refreshed on read, or on demand via `gth models --refresh`. If a refresh fetch
|
|
21
|
+
* fails but a stale slice exists, the stale slice is returned (degrade, don't blank).
|
|
22
|
+
*
|
|
23
|
+
* ## Local / self-hosted
|
|
24
|
+
* Only cloud providers have a models.dev key (see {@link MODELS_DEV_PROVIDER_KEY}). `ollama` and any
|
|
25
|
+
* other local/self-hosted provider return `null` from {@link getProviderCatalog} **without any network
|
|
26
|
+
* call** — they rely solely on `/v1/models` discovery.
|
|
27
|
+
*
|
|
28
|
+
* ## Attribution
|
|
29
|
+
* Wherever enriched prices/metadata are shown, surface {@link CATALOG_ATTRIBUTION}. models.dev is MIT
|
|
30
|
+
* licensed; we do not bundle or redistribute a seed dataset here, so no license file needs shipping.
|
|
31
|
+
*/
|
|
32
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
33
|
+
import { resolve } from 'node:path';
|
|
34
|
+
import { getGlobalGslothDir } from '#src/utils/globalConfigUtils.js';
|
|
35
|
+
import { displayDebug } from '#src/utils/consoleUtils.js';
|
|
36
|
+
/** The single models.dev dataset endpoint (whole catalog; sliced per-provider on our side). */
|
|
37
|
+
export const MODELS_DEV_URL = 'https://models.dev/api.json';
|
|
38
|
+
/**
|
|
39
|
+
* Cache freshness window. Pricing/limit metadata is near-static, so a day between refreshes is
|
|
40
|
+
* plenty; `gth models --refresh` is the manual escape hatch when a newer number is needed sooner.
|
|
41
|
+
*/
|
|
42
|
+
export const CATALOG_TTL_MS = 24 * 60 * 60 * 1000; // 24h
|
|
43
|
+
/**
|
|
44
|
+
* Catalog fetch timeout. Deliberately generous (and separate from `/v1/models` discovery's 2 s
|
|
45
|
+
* interactive-path probe): `api.json` is a few MB and this fetch runs off the explicit `gth models`
|
|
46
|
+
* cache-fill path, not a first-run keystroke, so latency headroom matters more than snappiness.
|
|
47
|
+
*/
|
|
48
|
+
export const CATALOG_TIMEOUT_MS = 10_000;
|
|
49
|
+
/** Shown next to any enriched price/metadata. Costs from models.dev are US$ per 1M tokens. */
|
|
50
|
+
export const CATALOG_ATTRIBUTION = '* model prices provided by models.dev';
|
|
51
|
+
/**
|
|
52
|
+
* Map a Gaunt Sloth {@link ProviderId} to its models.dev top-level provider key. Cloud providers
|
|
53
|
+
* only; local/self-hosted providers (`ollama`) are intentionally absent so they get NO catalog
|
|
54
|
+
* lookup. Returns `undefined` for any provider without a models.dev slice.
|
|
55
|
+
*/
|
|
56
|
+
export const MODELS_DEV_PROVIDER_KEY = {
|
|
57
|
+
anthropic: 'anthropic',
|
|
58
|
+
openai: 'openai',
|
|
59
|
+
'google-genai': 'google',
|
|
60
|
+
vertexai: 'google-vertex',
|
|
61
|
+
groq: 'groq',
|
|
62
|
+
deepseek: 'deepseek',
|
|
63
|
+
xai: 'xai',
|
|
64
|
+
openrouter: 'openrouter',
|
|
65
|
+
huggingface: 'huggingface',
|
|
66
|
+
// ollama — local, no models.dev entry (excluded on purpose).
|
|
67
|
+
};
|
|
68
|
+
/** Resolve the per-provider cache directory. */
|
|
69
|
+
function catalogCacheDir(options) {
|
|
70
|
+
return options.cacheDir ?? resolve(getGlobalGslothDir(), 'model-catalog');
|
|
71
|
+
}
|
|
72
|
+
/** Resolve the cache file path for one models.dev provider key. */
|
|
73
|
+
function catalogCacheFile(providerKey, options) {
|
|
74
|
+
return resolve(catalogCacheDir(options), `${providerKey}.json`);
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Coerce one models.dev raw model object into a {@link ModelCatalogEntry}, keeping only the fields
|
|
78
|
+
* we surface and only when they are the expected type (defensive against dataset drift).
|
|
79
|
+
*/
|
|
80
|
+
function normaliseEntry(raw) {
|
|
81
|
+
const m = (raw ?? {});
|
|
82
|
+
const entry = {};
|
|
83
|
+
if (typeof m.name === 'string')
|
|
84
|
+
entry.name = m.name;
|
|
85
|
+
if (typeof m.reasoning === 'boolean')
|
|
86
|
+
entry.reasoning = m.reasoning;
|
|
87
|
+
if (typeof m.tool_call === 'boolean')
|
|
88
|
+
entry.toolCall = m.tool_call;
|
|
89
|
+
const cost = m.cost;
|
|
90
|
+
if (cost && typeof cost === 'object') {
|
|
91
|
+
const c = {};
|
|
92
|
+
if (typeof cost.input === 'number')
|
|
93
|
+
c.input = cost.input;
|
|
94
|
+
if (typeof cost.output === 'number')
|
|
95
|
+
c.output = cost.output;
|
|
96
|
+
if (typeof cost.cache_read === 'number')
|
|
97
|
+
c.cacheRead = cost.cache_read;
|
|
98
|
+
if (typeof cost.cache_write === 'number')
|
|
99
|
+
c.cacheWrite = cost.cache_write;
|
|
100
|
+
if (Object.keys(c).length > 0)
|
|
101
|
+
entry.cost = c;
|
|
102
|
+
}
|
|
103
|
+
const limit = m.limit;
|
|
104
|
+
if (limit && typeof limit === 'object') {
|
|
105
|
+
const l = {};
|
|
106
|
+
if (typeof limit.context === 'number')
|
|
107
|
+
l.context = limit.context;
|
|
108
|
+
if (typeof limit.output === 'number')
|
|
109
|
+
l.output = limit.output;
|
|
110
|
+
if (Object.keys(l).length > 0)
|
|
111
|
+
entry.limit = l;
|
|
112
|
+
}
|
|
113
|
+
const modalities = m.modalities;
|
|
114
|
+
if (modalities && typeof modalities === 'object') {
|
|
115
|
+
const mod = {};
|
|
116
|
+
if (Array.isArray(modalities.input))
|
|
117
|
+
mod.input = modalities.input.filter((x) => typeof x === 'string');
|
|
118
|
+
if (Array.isArray(modalities.output))
|
|
119
|
+
mod.output = modalities.output.filter((x) => typeof x === 'string');
|
|
120
|
+
if ((mod.input?.length ?? 0) > 0 || (mod.output?.length ?? 0) > 0)
|
|
121
|
+
entry.modalities = mod;
|
|
122
|
+
}
|
|
123
|
+
return entry;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Slice one provider's models out of the full models.dev `api.json` payload and normalise them.
|
|
127
|
+
* Returns `{}` when the provider key is absent or malformed — an empty-but-present slice, which the
|
|
128
|
+
* caller still caches (models.dev simply has no data for that provider yet).
|
|
129
|
+
*/
|
|
130
|
+
function sliceProvider(payload, providerKey) {
|
|
131
|
+
const providerBlock = payload?.[providerKey];
|
|
132
|
+
const models = providerBlock?.models;
|
|
133
|
+
if (!models || typeof models !== 'object')
|
|
134
|
+
return {};
|
|
135
|
+
const out = {};
|
|
136
|
+
for (const [id, raw] of Object.entries(models)) {
|
|
137
|
+
out[id] = normaliseEntry(raw);
|
|
138
|
+
}
|
|
139
|
+
return out;
|
|
140
|
+
}
|
|
141
|
+
/** Read and validate a cached per-provider slice; returns null on any miss / parse failure. */
|
|
142
|
+
function readCache(providerKey, options) {
|
|
143
|
+
const file = catalogCacheFile(providerKey, options);
|
|
144
|
+
if (!existsSync(file))
|
|
145
|
+
return null;
|
|
146
|
+
try {
|
|
147
|
+
const parsed = JSON.parse(readFileSync(file, 'utf8'));
|
|
148
|
+
if (parsed && typeof parsed.fetchedAt === 'number' && parsed.models)
|
|
149
|
+
return parsed;
|
|
150
|
+
return null;
|
|
151
|
+
}
|
|
152
|
+
catch (e) {
|
|
153
|
+
displayDebug(`Model catalog cache for "${providerKey}" is unreadable: ${errMsg(e)}`);
|
|
154
|
+
return null;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
/** Persist a per-provider slice to the cache (best-effort; a write failure is non-fatal). */
|
|
158
|
+
function writeCache(catalog, options) {
|
|
159
|
+
try {
|
|
160
|
+
const dir = catalogCacheDir(options);
|
|
161
|
+
if (!existsSync(dir))
|
|
162
|
+
mkdirSync(dir, { recursive: true });
|
|
163
|
+
writeFileSync(catalogCacheFile(catalog.providerKey, options), JSON.stringify(catalog), 'utf8');
|
|
164
|
+
}
|
|
165
|
+
catch (e) {
|
|
166
|
+
displayDebug(`Failed to write model catalog cache for "${catalog.providerKey}": ${errMsg(e)}`);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
function errMsg(e) {
|
|
170
|
+
return e instanceof Error ? e.message : String(e);
|
|
171
|
+
}
|
|
172
|
+
/** True when a cached slice is still within the TTL window. */
|
|
173
|
+
function isFresh(cache, options) {
|
|
174
|
+
const now = options.now ?? Date.now();
|
|
175
|
+
const ttl = options.ttlMs ?? CATALOG_TTL_MS;
|
|
176
|
+
return now - cache.fetchedAt < ttl;
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Get the models.dev catalog slice for one provider, cache-first.
|
|
180
|
+
*
|
|
181
|
+
* - **Local / self-hosted** (`ollama`, or any provider without a {@link MODELS_DEV_PROVIDER_KEY}):
|
|
182
|
+
* returns `null` immediately, with **no network call**.
|
|
183
|
+
* - **Cloud, fresh cache** (and not `refresh`): returns the cached slice, no network call.
|
|
184
|
+
* - **Cloud, missing / stale cache, or `refresh`**: fetches `api.json` once, slices this provider,
|
|
185
|
+
* writes the slice to the per-provider cache, and returns it.
|
|
186
|
+
* - **Fetch fails**: returns the stale cached slice if one exists (degrade, don't blank), else `null`.
|
|
187
|
+
*
|
|
188
|
+
* NEVER throws — catalog availability must never block model use.
|
|
189
|
+
*/
|
|
190
|
+
export async function getProviderCatalog(providerId, options = {}) {
|
|
191
|
+
const providerKey = MODELS_DEV_PROVIDER_KEY[providerId];
|
|
192
|
+
if (!providerKey)
|
|
193
|
+
return null; // local / unmapped provider → no catalog lookup at all.
|
|
194
|
+
const cached = readCache(providerKey, options);
|
|
195
|
+
if (cached && !options.refresh && isFresh(cached, options)) {
|
|
196
|
+
return cached;
|
|
197
|
+
}
|
|
198
|
+
const fetchImpl = options.fetchImpl ?? fetch;
|
|
199
|
+
try {
|
|
200
|
+
const res = await fetchImpl(MODELS_DEV_URL, {
|
|
201
|
+
method: 'GET',
|
|
202
|
+
headers: { Accept: 'application/json' },
|
|
203
|
+
signal: AbortSignal.timeout(CATALOG_TIMEOUT_MS),
|
|
204
|
+
});
|
|
205
|
+
if (!res.ok) {
|
|
206
|
+
displayDebug(`models.dev returned HTTP ${res.status}; using cached catalog if available.`);
|
|
207
|
+
return cached; // may be stale; may be null.
|
|
208
|
+
}
|
|
209
|
+
const payload = await res.json();
|
|
210
|
+
const catalog = {
|
|
211
|
+
providerId,
|
|
212
|
+
providerKey,
|
|
213
|
+
fetchedAt: options.now ?? Date.now(),
|
|
214
|
+
models: sliceProvider(payload, providerKey),
|
|
215
|
+
};
|
|
216
|
+
writeCache(catalog, options);
|
|
217
|
+
return catalog;
|
|
218
|
+
}
|
|
219
|
+
catch (e) {
|
|
220
|
+
// Offline / on-prem no-egress / timeout / malformed — degrade to the stale slice or nothing.
|
|
221
|
+
displayDebug(`models.dev fetch failed: ${errMsg(e)}; using cached catalog if available.`);
|
|
222
|
+
return cached;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Enrich a provider's discovered model list with catalog metadata, cache-first.
|
|
227
|
+
*
|
|
228
|
+
* `/v1/models` stays authoritative: every input model is returned, in order, whether or not
|
|
229
|
+
* models.dev knows it. Matched ids gain an `enrichment`; unmatched ids (and every model when the
|
|
230
|
+
* catalog is unavailable or the provider is local) are returned untouched. NEVER throws.
|
|
231
|
+
*/
|
|
232
|
+
export async function enrichModels(providerId, models, options = {}) {
|
|
233
|
+
let catalog = null;
|
|
234
|
+
try {
|
|
235
|
+
catalog = await getProviderCatalog(providerId, options);
|
|
236
|
+
}
|
|
237
|
+
catch {
|
|
238
|
+
catalog = null; // defensive: getProviderCatalog already swallows, but never let enrichment throw.
|
|
239
|
+
}
|
|
240
|
+
return models.map((m) => {
|
|
241
|
+
const enrichment = catalog?.models[m.id];
|
|
242
|
+
return enrichment ? { ...m, enrichment } : { id: m.id, preferred: m.preferred };
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
//# sourceMappingURL=modelCatalog.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"modelCatalog.js","sourceRoot":"","sources":["../../src/providers/modelCatalog.ts"],"names":[],"mappings":"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"}
|
|
@@ -148,9 +148,83 @@ export declare function findApiKeyEnvVar(descriptor: ProviderDescriptor): string
|
|
|
148
148
|
* - When `discoveredModels` is omitted, the curated `preferredModels` are
|
|
149
149
|
* returned, all flagged ⭐ preferred.
|
|
150
150
|
* - When provided, every discovered model is listed; those that also appear in
|
|
151
|
-
* the curated `preferredModels` are flagged ⭐ preferred.
|
|
151
|
+
* the curated `preferredModels` are flagged ⭐ preferred. The list is ordered
|
|
152
|
+
* **preferred first** (in curated order, so the recommended default sits on top
|
|
153
|
+
* and is pre-selected), then every other discovered model **alphabetically** —
|
|
154
|
+
* so a large catalog (e.g. OpenRouter's 200+ models) is navigable instead of
|
|
155
|
+
* arriving in the endpoint's arbitrary order.
|
|
152
156
|
*/
|
|
153
157
|
export declare function buildModelList(descriptor: ProviderDescriptor, discoveredModels?: string[]): ModelInfo[];
|
|
158
|
+
/**
|
|
159
|
+
* The single fallback source of truth for a provider's default model id (CFG-14).
|
|
160
|
+
*
|
|
161
|
+
* Returns the top curated ⭐ `preferredModels` entry for the provider — the one
|
|
162
|
+
* id that provider factories fall back to when a config carries no `model`, and
|
|
163
|
+
* the id first-run writes when live discovery is impossible. Centralising it here
|
|
164
|
+
* means the fallback lives in exactly ONE place (the curated registry above)
|
|
165
|
+
* instead of being duplicated as a literal in every `providers/<id>.ts`.
|
|
166
|
+
*
|
|
167
|
+
* Every registered provider is required to carry at least one curated model, so
|
|
168
|
+
* this always resolves for a known id; it throws for an unknown provider or a
|
|
169
|
+
* provider whose `preferredModels` is empty (a developer error — the invariant
|
|
170
|
+
* that the fallback source is complete would otherwise fail silently).
|
|
171
|
+
*
|
|
172
|
+
* @returns the curated default model id (always defined for a known provider).
|
|
173
|
+
*/
|
|
174
|
+
export declare function getCuratedFallbackModel(providerId: ProviderId): string;
|
|
175
|
+
/**
|
|
176
|
+
* Build the minimal `.gsloth.config.json` body an `init` template writes for a
|
|
177
|
+
* provider (CFG-14). When `model` is omitted, the `model` key is left OUT of the
|
|
178
|
+
* config entirely, so the provider factory resolves it at run time from
|
|
179
|
+
* {@link getCuratedFallbackModel} — the single curated source that tracks the
|
|
180
|
+
* installed version, rather than a literal frozen into the user's file that 404s
|
|
181
|
+
* once the model is retired.
|
|
182
|
+
*
|
|
183
|
+
* A `$schema` pointer ({@link CONFIG_SCHEMA_POINTER}) is written first so editors offer
|
|
184
|
+
* autocomplete/validation against the shipped JSON Schema (GS2-1). `$schema` is a known,
|
|
185
|
+
* runtime-ignored config field (see the zod schema), so it never affects loading.
|
|
186
|
+
*/
|
|
187
|
+
export declare function buildInitConfigContent(providerId: ProviderId, model?: string): string;
|
|
188
|
+
/**
|
|
189
|
+
* CFG-21 — the timeout for the *interactive* first-run model fetch. The dialog is
|
|
190
|
+
* blocking on a human who just picked a provider, so it can afford to wait a lot
|
|
191
|
+
* longer than the background probes for a large cloud catalog (OpenRouter ~300
|
|
192
|
+
* models) to arrive over a cold connection, rather than losing the 2s race and
|
|
193
|
+
* silently dropping to a 3-item curated stub. Threaded per-call so ONLY the dialog
|
|
194
|
+
* uses it — `detectProviders` / `resolveInitModel` stay on the short
|
|
195
|
+
* {@link DISCOVERY_TIMEOUT_MS} probe (a long global would hang the *provider* step
|
|
196
|
+
* whenever the local Ollama daemon is down).
|
|
197
|
+
*/
|
|
198
|
+
export declare const INTERACTIVE_MODEL_FETCH_TIMEOUT_MS = 12000;
|
|
199
|
+
/**
|
|
200
|
+
* Provenance of a returned model list (CFG-21). Distinguishes the three outcomes so
|
|
201
|
+
* an interactive caller can tell an honest "couldn't reach the provider" degrade
|
|
202
|
+
* apart from a by-design curated list:
|
|
203
|
+
*
|
|
204
|
+
* - `live` — a successful, non-empty live `/v1/models` query.
|
|
205
|
+
* - `fallback` — a live query WAS attempted (key present / ollama probed) but
|
|
206
|
+
* failed: network error/timeout, non-2xx, or an empty/malformed payload. THIS is
|
|
207
|
+
* the "couldn't reach" case the first-run dialog surfaces.
|
|
208
|
+
* - `curated` — no live query was attempted, by design: a `kind:'none'` provider
|
|
209
|
+
* (google-genai, vertexai) or a cloud provider with no API key present. The
|
|
210
|
+
* curated catalog is the expected result here, NOT a degrade, so the dialog must
|
|
211
|
+
* NOT show a "couldn't reach" notice for it.
|
|
212
|
+
*/
|
|
213
|
+
export type ModelDiscoveryStatus = 'live' | 'fallback' | 'curated';
|
|
214
|
+
/** Per-call discovery options (CFG-21). */
|
|
215
|
+
export interface ModelDiscoveryOptions {
|
|
216
|
+
/**
|
|
217
|
+
* Live-fetch timeout in ms. Defaults to the short {@link DISCOVERY_TIMEOUT_MS} so
|
|
218
|
+
* every existing caller is unchanged; the interactive first-run dialog passes the
|
|
219
|
+
* generous {@link INTERACTIVE_MODEL_FETCH_TIMEOUT_MS}.
|
|
220
|
+
*/
|
|
221
|
+
timeoutMs?: number;
|
|
222
|
+
}
|
|
223
|
+
/** A model list plus its {@link ModelDiscoveryStatus} provenance (CFG-21). */
|
|
224
|
+
export interface ModelDiscoveryResult {
|
|
225
|
+
models: ModelInfo[];
|
|
226
|
+
status: ModelDiscoveryStatus;
|
|
227
|
+
}
|
|
154
228
|
/**
|
|
155
229
|
* Discover the models for a single provider.
|
|
156
230
|
*
|
|
@@ -167,15 +241,49 @@ export declare function buildModelList(descriptor: ProviderDescriptor, discovere
|
|
|
167
241
|
* the curated catalog is returned directly. Ollama (no key, local daemon) is
|
|
168
242
|
* always probed.
|
|
169
243
|
*/
|
|
170
|
-
export declare function discoverModels(providerId: ProviderId): Promise<ModelInfo[]>;
|
|
244
|
+
export declare function discoverModels(providerId: ProviderId, options?: ModelDiscoveryOptions): Promise<ModelInfo[]>;
|
|
171
245
|
/**
|
|
172
246
|
* List the models for a single provider.
|
|
173
247
|
*
|
|
174
248
|
* Live-discovers from the provider's models endpoint where possible (with a
|
|
175
249
|
* curated fallback); returns the curated set for `kind: 'none'` providers. Does
|
|
176
250
|
* not require the provider to be "available".
|
|
251
|
+
*
|
|
252
|
+
* `options.timeoutMs` (CFG-21) overrides the default short live-fetch timeout; the
|
|
253
|
+
* bare `listModels(providerId)` call is unchanged.
|
|
254
|
+
*/
|
|
255
|
+
export declare function listModels(providerId: ProviderId, options?: ModelDiscoveryOptions): Promise<ModelInfo[]>;
|
|
256
|
+
/**
|
|
257
|
+
* CFG-21 — like {@link discoverModels} but also returns the model list's
|
|
258
|
+
* {@link ModelDiscoveryStatus} provenance, so an interactive caller (the first-run
|
|
259
|
+
* dialog) can tell an honest "couldn't reach the provider" degrade (`fallback`)
|
|
260
|
+
* apart from a by-design curated list (`curated`) and a real live catalog (`live`).
|
|
261
|
+
* Kept as a separate export so `listModels` / `discoverModels` keep their existing
|
|
262
|
+
* `(providerId) => ModelInfo[]` signatures other callers rely on.
|
|
263
|
+
*
|
|
264
|
+
* Never throws for a discovery failure (that degrades to `fallback`); it only
|
|
265
|
+
* throws for an unknown provider id, mirroring {@link discoverModels}.
|
|
266
|
+
*/
|
|
267
|
+
export declare function discoverModelsWithProvenance(providerId: ProviderId, options?: ModelDiscoveryOptions): Promise<ModelDiscoveryResult>;
|
|
268
|
+
/**
|
|
269
|
+
* Resolve the model id that `init` should bake into a generated config for a
|
|
270
|
+
* provider (CFG-14) — the safeguard against writing a speculative id that 404s
|
|
271
|
+
* once the model is retired.
|
|
272
|
+
*
|
|
273
|
+
* - **Live discovery possible** (a key / running daemon and a responding
|
|
274
|
+
* `/v1/models` endpoint): returns a *verified-present* id — the highest-ranked
|
|
275
|
+
* curated ⭐ id that is actually in the live list (matched tolerant of an
|
|
276
|
+
* `:latest` suffix), or, when no curated id is live, the first live id.
|
|
277
|
+
* - **Live discovery impossible** (`kind: 'none'`, no key, offline, empty
|
|
278
|
+
* catalog): returns `undefined`. This is the ONLY speculative path, and it
|
|
279
|
+
* deliberately declines to invent a literal: the caller OMITS `model` so the
|
|
280
|
+
* provider factory falls back to {@link getCuratedFallbackModel} at run time
|
|
281
|
+
* (a single curated source that upgrades with the installed version).
|
|
282
|
+
*
|
|
283
|
+
* Never throws — a bad key or network error degrades to `undefined`, mirroring
|
|
284
|
+
* {@link discoverModels}.
|
|
177
285
|
*/
|
|
178
|
-
export declare function
|
|
286
|
+
export declare function resolveInitModel(providerId: ProviderId): Promise<string | undefined>;
|
|
179
287
|
/**
|
|
180
288
|
* Detect every known provider on this machine and list its models.
|
|
181
289
|
*
|