@combycode/llm-sdk 2.3.0 → 3.1.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/CHANGELOG.md +429 -0
- package/MIGRATION.md +130 -0
- package/dist/bus/hook-bus.d.ts +13 -3
- package/dist/bus/hook-map.d.ts +24 -0
- package/dist/catalog/catalog.d.ts +23 -26
- package/dist/helpers/client-resolver.d.ts +21 -2
- package/dist/helpers/count-tokens.d.ts +1 -1
- package/dist/helpers/engine.d.ts +27 -5
- package/dist/helpers/select-model.d.ts +35 -0
- package/dist/index.browser.js +17394 -4392
- package/dist/index.d.ts +3 -3
- package/dist/index.js +17394 -4392
- package/dist/llm/client-config.d.ts +8 -0
- package/dist/llm/client.d.ts +5 -0
- package/dist/llm/providers/anthropic/batch.d.ts +13 -2
- package/dist/llm/providers/anthropic/constants.d.ts +8 -43
- package/dist/llm/providers/anthropic/files.d.ts +13 -2
- package/dist/llm/providers/anthropic/messages.d.ts +18 -1
- package/dist/llm/providers/google/batch.d.ts +14 -1
- package/dist/llm/providers/google/constants.d.ts +6 -15
- package/dist/llm/providers/google/embeddings.d.ts +8 -1
- package/dist/llm/providers/google/files.d.ts +19 -1
- package/dist/llm/providers/google/generate.d.ts +15 -1
- package/dist/llm/providers/google/interactions.d.ts +5 -1
- package/dist/llm/providers/google/media.d.ts +26 -3
- package/dist/llm/providers/openai/batch.d.ts +19 -2
- package/dist/llm/providers/openai/completions.d.ts +13 -1
- package/dist/llm/providers/openai/embeddings.d.ts +13 -1
- package/dist/llm/providers/openai/files.d.ts +13 -2
- package/dist/llm/providers/openai/media.d.ts +21 -1
- package/dist/llm/providers/openai/moderations.d.ts +11 -1
- package/dist/llm/providers/openai/realtime.d.ts +3 -0
- package/dist/llm/providers/openai/responses.d.ts +10 -2
- package/dist/llm/providers/openai/transcription.d.ts +7 -1
- package/dist/llm/providers/openrouter/completions.d.ts +5 -3
- package/dist/llm/providers/openrouter/embeddings.d.ts +3 -0
- package/dist/llm/providers/openrouter/media.d.ts +14 -4
- package/dist/llm/providers/openrouter/responses.d.ts +5 -3
- package/dist/llm/providers/xai/batch.d.ts +13 -2
- package/dist/llm/providers/xai/completions.d.ts +5 -3
- package/dist/llm/providers/xai/files.d.ts +13 -2
- package/dist/llm/providers/xai/media.d.ts +28 -10
- package/dist/llm/providers/xai/responses.d.ts +5 -3
- package/dist/llm/response-shape.d.ts +96 -0
- package/dist/llm/types/provider.d.ts +11 -0
- package/dist/llm/types/request.d.ts +5 -6
- package/dist/llm/wire-multipart.d.ts +26 -0
- package/dist/{wire/transforms.d.ts → llm/wire-transforms.d.ts} +11 -5
- package/dist/plugins/context-measurer/counter/count-api.d.ts +39 -5
- package/dist/plugins/context-measurer/counter/hybrid.d.ts +33 -0
- package/dist/plugins/context-measurer/counter/tiktoken.d.ts +7 -0
- package/dist/plugins/mcp/transport-http.d.ts +13 -7
- package/dist/plugins/mcp/wire-rules.d.ts +21 -0
- package/dist/plugins/retrieval/document-file.d.ts +15 -0
- package/dist/plugins/retrieval/hosted-google.d.ts +8 -3
- package/dist/plugins/retrieval/hosted-openai.d.ts +9 -1
- package/dist/plugins/retrieval/hosted-xai.d.ts +8 -2
- package/dist/wire/chat-specs.d.ts +32 -0
- package/dist/wire/inherit.d.ts +0 -4
- package/dist/wire/interpreter.d.ts +84 -1
- package/dist/wire/mcp-specs.d.ts +13 -0
- package/dist/wire/media-specs.d.ts +20 -0
- package/dist/wire/pins.d.ts +34 -0
- package/dist/wire/registry.d.ts +6 -4
- package/dist/wire/retrieval-specs.d.ts +14 -0
- package/dist/wire/service-specs.d.ts +18 -0
- package/dist/wire/utility-specs.d.ts +10 -0
- package/package.json +4 -1
|
@@ -73,27 +73,6 @@ export interface TokenizerInfo {
|
|
|
73
73
|
countApiAvailable: boolean;
|
|
74
74
|
tiktokenEncoding?: string;
|
|
75
75
|
}
|
|
76
|
-
/** Per-model WIRE traits: how to say a thing to this model, as opposed to
|
|
77
|
-
* `capabilities` / `reasoning`, which say what the model can do.
|
|
78
|
-
*
|
|
79
|
-
* This is the gap that produced two shipped bugs. The catalog knew a model
|
|
80
|
-
* supported reasoning; nothing knew which of two incompatible `thinking` shapes
|
|
81
|
-
* it accepted, so adapters parsed the model id with regexes and got it wrong
|
|
82
|
-
* twice (2.2.1, and the 4.0 date-suffix defect fixed in 039 A1).
|
|
83
|
-
*
|
|
84
|
-
* Adapters read these through `NormalizedRequest.wire`, which `LLMClient`
|
|
85
|
-
* resolves from the catalog. When the catalog has no entry the adapters fall
|
|
86
|
-
* back to parsing the id, so a catalog-less engine still works — the fallback
|
|
87
|
-
* is what a future release removes once every model is pinned. */
|
|
88
|
-
export interface ModelWire {
|
|
89
|
-
/** Which `thinking` shape the wire accepts.
|
|
90
|
-
* Anthropic: `adaptive` (4.6+) vs `budgeted` (pre-4.6, needs budget_tokens).
|
|
91
|
-
* Google: `level` (3.x, thinkingLevel enum) vs `budget` (2.5, token count). */
|
|
92
|
-
thinking?: 'adaptive' | 'budgeted' | 'budget' | 'level';
|
|
93
|
-
/** Whether the model accepts a top-k sampling parameter. Anthropic retired it
|
|
94
|
-
* after Opus 4.6 and 400s on models that no longer take it. */
|
|
95
|
-
topK?: boolean;
|
|
96
|
-
}
|
|
97
76
|
export interface ModelInfo {
|
|
98
77
|
provider: string;
|
|
99
78
|
/** Catalog key — our canonical (normalised) slug, e.g. `claude-opus-4.8`. */
|
|
@@ -115,8 +94,6 @@ export interface ModelInfo {
|
|
|
115
94
|
/** Other callable ids that resolve to this model (dated snapshots, the bare
|
|
116
95
|
* callable form). Indexed for lookup + accepted as model strings. */
|
|
117
96
|
aliases?: string[];
|
|
118
|
-
/** Per-model wire traits — see ModelWire. */
|
|
119
|
-
wire?: ModelWire;
|
|
120
97
|
/** Which wire spec builds this model's requests, e.g. `anthropic/messages@4.7`.
|
|
121
98
|
*
|
|
122
99
|
* The specs live in `src/wire/specs` and are the artifact the Python and Rust
|
|
@@ -164,14 +141,26 @@ export declare class ModelCatalog {
|
|
|
164
141
|
/** `provider/alias` → `provider/canonical-slug`. Lets get()/resolveModelId
|
|
165
142
|
* accept any callable id (providerModelName, dated snapshot) AND the slug. */
|
|
166
143
|
private aliasIndex;
|
|
144
|
+
/** `provider/normalized-id` → `provider/canonical-slug`. The last resort, so a
|
|
145
|
+
* user's spelling of a version never decides whether the model is found. */
|
|
146
|
+
private normIndex;
|
|
167
147
|
private key;
|
|
148
|
+
private normKey;
|
|
168
149
|
set(provider: string, model: string, info: Partial<Omit<ModelInfo, 'provider' | 'model'>> & {
|
|
169
150
|
pricing: ModelPricing;
|
|
170
151
|
}): void;
|
|
171
152
|
get(provider: string, model: string): ModelInfo | null;
|
|
172
|
-
/** The exact id to SEND to the provider for a given model string.
|
|
173
|
-
*
|
|
174
|
-
*
|
|
153
|
+
/** The exact id to SEND to the provider for a given model string.
|
|
154
|
+
*
|
|
155
|
+
* Three cases, and the difference between them is what the caller ASKED for:
|
|
156
|
+
* - our slug → translate to providerModelName (the pinned snapshot)
|
|
157
|
+
* - an id the provider itself accepts (a listed alias, e.g. a dated snapshot
|
|
158
|
+
* or anthropic's undated name) → verbatim, because it is a deliberate choice
|
|
159
|
+
* and rewriting it would pin a caller who asked to float
|
|
160
|
+
* - a spelling variant that is NOT callable (`gemini-2-5-flash`) → the
|
|
161
|
+
* canonical entry's providerModelName, since forwarding it verbatim only
|
|
162
|
+
* produces a 404 with the user's typo in it
|
|
163
|
+
* - unknown → verbatim, so a model we have never heard of still works */
|
|
175
164
|
resolveModelId(provider: string, model: string): string;
|
|
176
165
|
getPricing(provider: string, model: string): ModelPricing | null;
|
|
177
166
|
getPreferredApi(provider: string, model: string): ApiType | null;
|
|
@@ -190,6 +179,14 @@ export declare class ModelCatalog {
|
|
|
190
179
|
isStateModelBound(provider: string, model: string): boolean;
|
|
191
180
|
list(provider?: string): ModelInfo[];
|
|
192
181
|
load(data: Record<string, unknown>): void;
|
|
182
|
+
/** A catalog with every bundled provider entry already loaded.
|
|
183
|
+
*
|
|
184
|
+
* This is what the engine and the client build when nobody says otherwise. A
|
|
185
|
+
* fresh instance each time rather than a shared one: the catalog is mutable
|
|
186
|
+
* (`set()` is public and examples use it), so sharing would let one engine's
|
|
187
|
+
* edit reach another's request. Indexing all 427 entries costs about a
|
|
188
|
+
* millisecond, against a network call. */
|
|
189
|
+
static withProviderDefaults(): ModelCatalog;
|
|
193
190
|
/** Load every provider's `catalog.json` shipped with the SDK. Synchronous —
|
|
194
191
|
* the JSON files are bundled via static import so no I/O at runtime. */
|
|
195
192
|
loadProviderDefaults(): void;
|
|
@@ -46,8 +46,27 @@ export declare class ClientResolver {
|
|
|
46
46
|
export declare function parseModelId(modelId: string): [ProviderName, string];
|
|
47
47
|
export declare function isNamespacedModelId(modelId: string): boolean;
|
|
48
48
|
/** Resolve a model + optional provider to a concrete { provider, model }.
|
|
49
|
-
*
|
|
50
|
-
*
|
|
49
|
+
*
|
|
50
|
+
* An EXPLICIT provider always wins. It used to lose to the model's prefix, and
|
|
51
|
+
* that is not a preference — every OpenRouter model id is `vendor/model`, so
|
|
52
|
+
* `createLLM({ provider: 'openrouter', model: 'openai/gpt-5.4-nano' })` resolved
|
|
53
|
+
* to the provider `openai` and sent the **OpenRouter key to api.openai.com**.
|
|
54
|
+
* Ids whose vendor is not one of our five (`qwen/qwen3`) fared differently and no
|
|
55
|
+
* better: the prefix was cast to a ProviderName and failed later as "no default
|
|
56
|
+
* adapter for provider 'qwen'".
|
|
57
|
+
*
|
|
58
|
+
* With a provider given, a leading `<provider>/` on the model is redundant and is
|
|
59
|
+
* stripped — `openrouter` + `openrouter/openai/gpt-5.4-nano` is the catalog's own
|
|
60
|
+
* slug form and means the OpenRouter model `openai/gpt-5.4-nano`.
|
|
61
|
+
*
|
|
62
|
+
* Without one, the `provider/model` prefix is still the documented sugar, and it
|
|
63
|
+
* stays permissive about the prefix on purpose: the pricing paths (`estimate`,
|
|
64
|
+
* `estimator`) resolve models that are catalogued under a provider nobody can
|
|
65
|
+
* CALL — a private deployment, a test fixture — and rejecting those would break
|
|
66
|
+
* costing a model you never send. A prefix that is not callable fails where it
|
|
67
|
+
* matters, in the adapter factory, naming the provider it could not build.
|
|
68
|
+
*
|
|
69
|
+
* `label` names the caller in the error. */
|
|
51
70
|
export declare function resolveModel(model: string, provider: ProviderName | undefined, label: string): {
|
|
52
71
|
provider: ProviderName;
|
|
53
72
|
model: string;
|
|
@@ -17,7 +17,7 @@ export interface CountTokensOptions {
|
|
|
17
17
|
provider?: ProviderName;
|
|
18
18
|
/** Text or messages to count. */
|
|
19
19
|
input: string | Message[];
|
|
20
|
-
/** Key for the exact count-API path (Anthropic
|
|
20
|
+
/** Key for the exact count-API path (Anthropic, Google, xAI). Falls back to engine.apiKeys. */
|
|
21
21
|
apiKey?: string;
|
|
22
22
|
/** Use the precise counter where available (default true). false = sync estimate. */
|
|
23
23
|
exact?: boolean;
|
package/dist/helpers/engine.d.ts
CHANGED
|
@@ -65,6 +65,9 @@ export interface EngineHandle {
|
|
|
65
65
|
* createMediaOutput, complete) read these to wire LLM clients without
|
|
66
66
|
* the caller passing apiKey explicitly. */
|
|
67
67
|
apiKeys: Partial<Record<ProviderName, string>>;
|
|
68
|
+
/** Whether clients built from this engine check response shapes. Read by
|
|
69
|
+
* `createLLM`; see `checkResponseShapes` on the options. */
|
|
70
|
+
checkResponseShapes: boolean;
|
|
68
71
|
/** Build an LLMClient bound to this engine.
|
|
69
72
|
*
|
|
70
73
|
* Exists so lower layers can obtain a client without importing the helpers
|
|
@@ -100,12 +103,31 @@ export interface EngineConfig {
|
|
|
100
103
|
/** Custom low-level fetch transport — forwarded to the NetworkEngine's queue
|
|
101
104
|
* (so retry/rate-limit/hooks still apply). Defaults to globalThis.fetch. */
|
|
102
105
|
fetch?: FetchFn;
|
|
103
|
-
/**
|
|
104
|
-
*
|
|
106
|
+
/** Warn when a provider's response stops looking like the one we learned to
|
|
107
|
+
* read: a field never seen before, a field that was always present and is now
|
|
108
|
+
* absent, or a discriminator carrying a value nothing branches on.
|
|
109
|
+
*
|
|
110
|
+
* OFF by default. It never changes what is parsed — it only emits `onWarning`,
|
|
111
|
+
* so subscribe with `hooks.on('onWarning', …)` and look for codes starting
|
|
112
|
+
* `response_shape_`. Each distinct finding is reported ONCE per client.
|
|
113
|
+
*
|
|
114
|
+
* Worth turning on in staging and in your test suite: response drift is the
|
|
115
|
+
* failure this library gives you the least warning about, because a renamed
|
|
116
|
+
* field still parses — into `undefined`. */
|
|
117
|
+
checkResponseShapes?: boolean;
|
|
118
|
+
/** Catalog wiring. **Defaults to the bundled provider catalogs.**
|
|
119
|
+
*
|
|
120
|
+
* The catalog is what the adapters read per model: which wire spec builds the
|
|
121
|
+
* request, what the model costs, which tokenizer counts it. Starting empty
|
|
122
|
+
* meant every one of those silently fell back — the id-derived spec, an
|
|
123
|
+
* unknown price, an estimated token count — and nothing said so. The data is
|
|
124
|
+
* statically imported either way, so leaving it unloaded saved no bytes.
|
|
125
|
+
*
|
|
126
|
+
* - undefined (default) / `true` / 'defaults' → every bundled catalog.json
|
|
105
127
|
* - existing ModelCatalog instance → use as-is
|
|
106
|
-
* - `{ entries: {...} }` →
|
|
107
|
-
* -
|
|
108
|
-
catalog?: ModelCatalog |
|
|
128
|
+
* - `{ entries: {...} }` → the given entries only
|
|
129
|
+
* - `false` / 'empty' → no entries. Everything falls back; say so on purpose. */
|
|
130
|
+
catalog?: ModelCatalog | boolean | 'defaults' | 'empty' | {
|
|
109
131
|
entries: Record<string, unknown>;
|
|
110
132
|
};
|
|
111
133
|
/** Per-provider API keys. Helpers consult this when no apiKey is passed
|
|
@@ -34,6 +34,41 @@ declare const DEFAULT_THRESHOLDS: {
|
|
|
34
34
|
'context.small': number;
|
|
35
35
|
'context.large': number;
|
|
36
36
|
};
|
|
37
|
+
/** One filter a UI can offer, and what it accepts. */
|
|
38
|
+
export interface FilterFacet {
|
|
39
|
+
/** The DSL key, e.g. `price`. Write it as `key:value`. */
|
|
40
|
+
key: string;
|
|
41
|
+
/** Group heading for a picker. */
|
|
42
|
+
category: 'what it is' | 'cost' | 'thinking' | 'inputs' | 'hosted tools' | 'availability';
|
|
43
|
+
/** Short human label. */
|
|
44
|
+
label: string;
|
|
45
|
+
/** The values this key accepts. Empty when the key is a bare flag (`vision`),
|
|
46
|
+
* which the parser reads as `key:yes`. */
|
|
47
|
+
values: string[];
|
|
48
|
+
/** True when the key also accepts `> N` / `< N`, so a picker can offer a number. */
|
|
49
|
+
numeric: boolean;
|
|
50
|
+
/** True when a bare `key` (no value) is meaningful — the parser expands it to
|
|
51
|
+
* `key:yes`. */
|
|
52
|
+
bare: boolean;
|
|
53
|
+
}
|
|
54
|
+
/** Every clause the query parser understands, as data.
|
|
55
|
+
*
|
|
56
|
+
* Exported because the alternative is a UI hand-listing the same tags: a second
|
|
57
|
+
* copy of this vocabulary, drifting from the parser the first time either moves,
|
|
58
|
+
* and drifting invisibly because a wrong tag reads as "no models matched" rather
|
|
59
|
+
* than as an error. Here the picker and the parser cannot disagree — both come
|
|
60
|
+
* from `KNOWN_KEYS`, `CAP_KEYS` and `BUILTIN_TOOL_KEYS` above.
|
|
61
|
+
*
|
|
62
|
+
* `type`, `provider`, `status` and `tier` take their values from the CATALOG
|
|
63
|
+
* when one is passed, because those are open sets: a provider ships a new model
|
|
64
|
+
* type and a hard-coded list is wrong that day. Without a catalog they come back
|
|
65
|
+
* empty rather than guessed — an empty list is honest, a stale list is not. */
|
|
66
|
+
export declare function filterFacets(catalog?: {
|
|
67
|
+
list(): ModelInfo[];
|
|
68
|
+
}): FilterFacet[];
|
|
69
|
+
/** The shorthand tags the parser expands before matching (`cheap` → `price:low`).
|
|
70
|
+
* A picker can show these as one-click presets. */
|
|
71
|
+
export declare function filterAliases(): Record<string, string>;
|
|
37
72
|
/** All matching models, ranked cheapest-first (tiebreak: newest version). */
|
|
38
73
|
export declare function selectModels(query: string | string[], opts?: SelectOptions): ModelInfo[];
|
|
39
74
|
/** The single best match as a `provider/slug` string (feedable to complete), or null. */
|