@combycode/llm-sdk 2.2.2 → 2.3.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 +182 -0
- package/dist/agent/loop-internals.d.ts +4 -0
- package/dist/agent/loop.d.ts +35 -0
- package/dist/{llm/providers → catalog}/builtin-tools.d.ts +1 -1
- package/dist/{plugins/model-catalog → catalog}/catalog.d.ts +34 -0
- package/dist/helpers/client-pool.d.ts +1 -1
- package/dist/helpers/client-resolver.d.ts +1 -1
- package/dist/helpers/engine.d.ts +12 -1
- package/dist/helpers/mcp.d.ts +6 -1
- package/dist/helpers/models.d.ts +1 -1
- package/dist/helpers/one-shot.d.ts +2 -2
- package/dist/helpers/select-model.d.ts +1 -1
- package/dist/index.browser.js +1414 -890
- package/dist/index.d.ts +4 -4
- package/dist/index.js +1414 -890
- package/dist/llm/client-config.d.ts +1 -1
- package/dist/llm/client-internal.d.ts +11 -0
- package/dist/llm/client.d.ts +4 -0
- package/dist/llm/providers/_shared/sse.d.ts +19 -0
- package/dist/llm/providers/google/files.d.ts +15 -0
- package/dist/llm/providers/google/media.d.ts +22 -4
- package/dist/llm/providers/google/realtime.d.ts +15 -2
- package/dist/llm/providers/openai/media.d.ts +10 -1
- package/dist/llm/providers/openai/realtime.d.ts +15 -2
- package/dist/llm/server-state.d.ts +1 -1
- package/dist/llm/types/options.d.ts +2 -2
- package/dist/llm/types/request.d.ts +50 -1
- package/dist/plugins/context-measurer/counter/count-api.d.ts +1 -1
- package/dist/plugins/context-measurer/counter/heuristic.d.ts +1 -1
- package/dist/plugins/context-measurer/counter/hybrid.d.ts +1 -1
- package/dist/plugins/context-measurer/measurer.d.ts +1 -1
- package/dist/plugins/cost-collector/collector.d.ts +1 -1
- package/dist/plugins/cost-collector/cost-collector-internal.d.ts +1 -1
- package/dist/plugins/cost-collector/cost-collector-types.d.ts +1 -1
- package/dist/plugins/files/registry.d.ts +1 -1
- package/dist/plugins/files/strategy.d.ts +1 -1
- package/dist/plugins/internal-tools/registry.d.ts +1 -1
- package/dist/plugins/internal-tools/runner/types.d.ts +1 -1
- package/dist/plugins/mcp/sampling.d.ts +23 -1
- package/dist/plugins/media/output.d.ts +1 -1
- package/dist/plugins/telemetry/telemetry.d.ts +2 -133
- package/dist/plugins/telemetry/types.d.ts +139 -0
- package/dist/util/hash.d.ts +8 -0
- package/dist/{plugins/media → util}/source-image.d.ts +1 -1
- package/dist/wire/inherit.d.ts +47 -0
- package/dist/wire/interpreter.d.ts +236 -0
- package/dist/wire/registry.d.ts +18 -0
- package/dist/wire/transforms.d.ts +22 -0
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,188 @@ All notable changes to `@combycode/llm-sdk` are documented here. The format foll
|
|
|
4
4
|
[Keep a Changelog](https://keepachangelog.com/) and the project adheres to
|
|
5
5
|
[Semantic Versioning](https://semver.org/).
|
|
6
6
|
|
|
7
|
+
## [2.3.0] — 2026-08-23
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- **Every catalogued chat model is pinned to a wire spec.** `ModelInfo.wireSpec` names the spec
|
|
12
|
+
that builds that model's requests — `anthropic/messages@4.7`, `google/generate@2.5`,
|
|
13
|
+
`openai/responses`, and so on. All 289 chat models across five providers carry one.
|
|
14
|
+
|
|
15
|
+
The pin is what lets this SDK and the Python and Rust ports agree on a model without each
|
|
16
|
+
re-deriving its wire shape from the model id — the derivation that produced the 2.2.1 and 2.2.2
|
|
17
|
+
bugs.
|
|
18
|
+
|
|
19
|
+
Carried and validated, not yet authoritative: the hand-written adapters still build requests
|
|
20
|
+
from `wire` traits. Because that is two representations of one fact, and two representations
|
|
21
|
+
drift, a test drives the PINNED SPEC and asserts the request it produces matches what the traits
|
|
22
|
+
say the model takes. When the adapters become spec-driven in 3.0.0, `wire` goes away and that
|
|
23
|
+
test is what makes the swap safe.
|
|
24
|
+
|
|
25
|
+
- **Wire specs ship in the repo** (`src/wire/`). 71 JSON specs describe how to talk to each
|
|
26
|
+
provider API — field names, enum values, defaults, versioned tool-type strings, which shape a
|
|
27
|
+
model version takes — covering every adapter the SDK has: chat, interactions, media, realtime,
|
|
28
|
+
embeddings, files and batch.
|
|
29
|
+
|
|
30
|
+
They exist so the Python and Rust ports consume one artifact instead of re-deriving the same
|
|
31
|
+
knowledge three times, and so a provider change is one reviewable diff rather than three code
|
|
32
|
+
changes. This is the knowledge that, living in regexes, produced the 2.2.1 and 2.2.2 bugs.
|
|
33
|
+
|
|
34
|
+
**Oracle, not yet authority:** the adapters remain hand-written, and a test
|
|
35
|
+
(`tests/unit/wire`) proves the specs and the adapters agree on every CI run — data that is
|
|
36
|
+
never executed rots. Making the specs authoritative is the 3.0.0 step.
|
|
37
|
+
|
|
38
|
+
They are not exported from `index.ts` and are tree-shaken out of `dist`, so they add **no bytes**
|
|
39
|
+
to the published package (verified: package size unchanged).
|
|
40
|
+
|
|
41
|
+
- **Every catalogued chat model is checked against its pinned spec on every run.** The previous
|
|
42
|
+
pin test drove Anthropic and Google models only — 26 of 289. The other 263, 224 of them
|
|
43
|
+
OpenRouter, were covered by nothing stronger than "the pin names a spec that resolves", which a
|
|
44
|
+
typo satisfies. `tests/unit/wire/every-model-reproduces-its-adapter.test.ts` now builds 17
|
|
45
|
+
request shapes for every chat model, through the spec the CATALOG pins it to, and requires the
|
|
46
|
+
payload to equal the one the real adapter produces — ~4,900 comparisons, under a second.
|
|
47
|
+
|
|
48
|
+
The adapter is chosen from `preferredApi`, never from the pin. Choosing it from the pin is
|
|
49
|
+
circular and silently so: mis-pin an OpenRouter model to `openai/responses` and the adapter
|
|
50
|
+
moves with it, both sides agree, and the sweep stays green on a broken pin. It did exactly that
|
|
51
|
+
until a deliberate corruption caught it. Mis-pinning any single model in any of the five
|
|
52
|
+
providers now fails, as does a provider losing its pins entirely.
|
|
53
|
+
|
|
54
|
+
- **A consumer example for traces** (`telemetry-traces`, in the examples corpus): subscribing with
|
|
55
|
+
`onTrace`, filtering by span type at the subscription rather than in the handler, head sampling,
|
|
56
|
+
keeping prompt content out, joining an inbound `traceparent`, naming a run with
|
|
57
|
+
`label`/`source`/`attributes`, and reading `client.routing`. The trace feed had no example at
|
|
58
|
+
all, which the quality gate's example-first check was reporting.
|
|
59
|
+
|
|
60
|
+
- **Request builders on the media and realtime adapters.** These adapters used to assemble each
|
|
61
|
+
request *inside* the method that also fetched and parsed it, so the only way to see what the SDK
|
|
62
|
+
would send was to intercept the network. Construction is now separated:
|
|
63
|
+
|
|
64
|
+
- `GoogleMediaAdapter`: `buildImageRequest`, `buildEditImageRequest`, `buildAudioRequest`,
|
|
65
|
+
`buildVideoRequest`, plus the lower-level `buildImagenRequest` / `buildGenerateContentRequest`.
|
|
66
|
+
- `OpenAIMediaAdapter`: `buildGenerateImageRequest`, `buildEditImageRequest`, `buildAudioRequest`,
|
|
67
|
+
`buildVideoRequest`.
|
|
68
|
+
- Realtime: `buildConnectRequest` on both adapters, and free functions
|
|
69
|
+
`buildOpenAISessionUpdate` / `buildOpenAITurnFrames` and `buildGoogleSetupFrame` /
|
|
70
|
+
`buildGoogleTurnFrames` for the handshake and per-turn frames.
|
|
71
|
+
|
|
72
|
+
The public methods now call these, so the two cannot drift — and a test
|
|
73
|
+
(`media-request-builders.test.ts`) asserts that what a builder returns is byte-identical to what
|
|
74
|
+
its method actually sends.
|
|
75
|
+
|
|
76
|
+
Additive: no existing signature changed.
|
|
77
|
+
|
|
78
|
+
- **The catalog now knows how to TALK to a model, not just what it can do.** `ModelInfo` gains a
|
|
79
|
+
`wire` block carrying per-model wire traits — which `thinking` shape the model accepts, whether
|
|
80
|
+
it takes `top_k` — and `LLMClient` resolves it onto every request as `NormalizedRequest.wire`.
|
|
81
|
+
Adapters read that instead of parsing the model id.
|
|
82
|
+
|
|
83
|
+
This is the gap behind two shipped bugs. The catalog already recorded that a model supported
|
|
84
|
+
reasoning; nothing recorded which of two incompatible `thinking` shapes it accepted, so adapters
|
|
85
|
+
matched on the id and got it wrong twice — in 2.2.1, and again in the 4.0 date-suffix defect
|
|
86
|
+
fixed this release. Wire knowledge is now reviewable data that can be diffed and generated,
|
|
87
|
+
rather than a regex nobody re-reads.
|
|
88
|
+
|
|
89
|
+
The bundled catalog carries `wire` for all 14 Anthropic and 12 Google chat models, generated
|
|
90
|
+
from the existing rules so behaviour is unchanged on day one. When the catalog is silent —
|
|
91
|
+
an engine running without one, or an uncatalogued model — adapters fall back to parsing the id
|
|
92
|
+
exactly as before, so nothing breaks. Removing that fallback is a later step, once every model
|
|
93
|
+
is pinned.
|
|
94
|
+
|
|
95
|
+
- `EngineHandle.createClient(options)` — build an `LLMClient` bound to that engine. Added so
|
|
96
|
+
`plugins/internal-tools` can obtain a client without importing `createLLM` from the helpers
|
|
97
|
+
layer. Additive on a handle callers receive rather than implement.
|
|
98
|
+
|
|
99
|
+
- `util/hash` — FNV-1a 32-bit, deterministic and dependency-free, for deriving stable short ids
|
|
100
|
+
from content instead of from a clock.
|
|
101
|
+
|
|
102
|
+
### Changed
|
|
103
|
+
|
|
104
|
+
- **The module graph is now a DAG.** Two dependency cycles between top-level layers were closed:
|
|
105
|
+
`llm <-> plugins` and `helpers <-> plugins`. They were harmless in TypeScript and are not
|
|
106
|
+
harmless in Rust, where crates cannot express a cycle, so they blocked the port.
|
|
107
|
+
|
|
108
|
+
Shared code moved DOWN rather than sideways: the model catalog and its bundled data now live in
|
|
109
|
+
`src/catalog/` instead of `src/plugins/model-catalog/` + `src/llm/providers/*/catalog.json`, and
|
|
110
|
+
image-source normalisation moved from `src/plugins/media/source-image` to `src/util/source-image`.
|
|
111
|
+
Where a lower layer genuinely needs a capability from a higher one it is now passed down instead
|
|
112
|
+
of imported up.
|
|
113
|
+
|
|
114
|
+
**No public API changed** — the package has a single root export and every moved symbol is
|
|
115
|
+
re-exported from the same place as before. A new test (`tests/unit/architecture/layers.test.ts`)
|
|
116
|
+
fails if any cycle returns.
|
|
117
|
+
|
|
118
|
+
- **Internal cleanups carried over from the 1.0 backlog.**
|
|
119
|
+
- `buildContext` read `LLMClient`'s private `queueName` / `configName` / `cacheName` through
|
|
120
|
+
`as unknown as` casts, which compile happily and would silently yield `undefined` the day a
|
|
121
|
+
field is renamed. The client now exposes them deliberately as `client.routing`, so a rename is
|
|
122
|
+
a type error.
|
|
123
|
+
- Telemetry's ten exported types moved from the 1,225-line `telemetry.ts` to
|
|
124
|
+
`plugins/telemetry/types.ts`, matching the rest of the codebase. Re-exported from the old path,
|
|
125
|
+
so no import — public or internal — changed.
|
|
126
|
+
- `sseJson()` in `providers/_shared` replaces the one line every provider's stream parser
|
|
127
|
+
repeated verbatim.
|
|
128
|
+
|
|
129
|
+
- **`providerOptions` is typed.** It was `Record<string, unknown>` — the one untyped hole in the
|
|
130
|
+
request, and so the one place a typo produced silence rather than an error:
|
|
131
|
+
`promtCacheOptions` type-checked and was simply never sent.
|
|
132
|
+
|
|
133
|
+
The new `ProviderOptions` interface documents every key an adapter actually reads
|
|
134
|
+
(`userProfileId`; `moderationPolicy`, `promptCacheOptions`, `reasoningMode`;
|
|
135
|
+
`responseModalities`, `speechConfig`, `imageConfig`, `translationConfig`, `cachedContent`;
|
|
136
|
+
`openrouter`), derived from the read sites rather than invented. Two `as` casts at those sites
|
|
137
|
+
became unnecessary and were removed.
|
|
138
|
+
|
|
139
|
+
**Not breaking:** the index signature stays, so an unmodelled key is still accepted — providers
|
|
140
|
+
ship parameters before the SDK models them, and refusing those would make the escape hatch
|
|
141
|
+
useless. What changed is that the keys we do know are checked and discoverable.
|
|
142
|
+
|
|
143
|
+
- **`AgentLoop.complete()` and `stream()` no longer duplicate their scaffolding.** The two are the
|
|
144
|
+
same loop with different plumbing, and they had drifted into near-duplicates — 167 identical
|
|
145
|
+
lines across ~560. Five shared pieces are now extracted: `recordRunError`, `resolveFinalText`,
|
|
146
|
+
`buildFinalResponse`, `settleRun`, and `buildStepOptions`.
|
|
147
|
+
|
|
148
|
+
This is not tidying. The `ctx` block in `buildStepOptions` is what stops one conversation
|
|
149
|
+
arriving at a collector as several unrelated traces; duplicated, a fix to one path would have
|
|
150
|
+
left the other silently splitting. The same applies to the run's final-text rules and its
|
|
151
|
+
error reporting.
|
|
152
|
+
|
|
153
|
+
`complete()` 303 -> 234 lines, `stream()` 261 -> 209, identical shared lines 167 -> ~118.
|
|
154
|
+
Behaviour unchanged: all 1,953 tests pass untouched.
|
|
155
|
+
|
|
156
|
+
### Fixed
|
|
157
|
+
|
|
158
|
+
- **Anthropic 4.0 models were handed the 4.6+ `thinking` shape.** `anthropicThinkingShape()`
|
|
159
|
+
parsed a model id with `/^claude-[a-z]+-(\d+)(?:[-.](\d+))?/`, so an id carrying a release date
|
|
160
|
+
but no minor version read the date AS the minor: `claude-opus-4-20250514` became major 4 /
|
|
161
|
+
minor 20250514, cleared the `>= 4.6` test, and was sent `thinking: {type:'adaptive'}` — the
|
|
162
|
+
opposite of what the file's own `ANTHROPIC_ADAPTIVE_THINKING_MIN = {major:4, minor:6}` says.
|
|
163
|
+
|
|
164
|
+
This never shipped a failure: `claude-opus-4-20250514` and `claude-sonnet-4-20250514` are the
|
|
165
|
+
only affected ids and both are `active: false` in the catalog (deprecated 2026-05-14). It is
|
|
166
|
+
nonetheless the 2.2.1 regression in mirror image, so it is fixed rather than left latent. The
|
|
167
|
+
minor is now bounded to one or two digits and must not be followed by another digit. Checked
|
|
168
|
+
against every id in the catalog plus aliases and future-shaped ids (`claude-opus-4-10`,
|
|
169
|
+
`claude-sonnet-6-1-20270101`): exactly those two change classification, the other 17 are
|
|
170
|
+
untouched.
|
|
171
|
+
|
|
172
|
+
- **xAI batch creation was not reproducible.** The create call named the batch
|
|
173
|
+
`` `batch_${Date.now()}` ``, which made it the only request in the provider surface that was not
|
|
174
|
+
a pure function of its input: it could not be asserted in a test or reproduced from a log, and a
|
|
175
|
+
retried create produced a second batch under a different name that nothing could deduplicate.
|
|
176
|
+
The name is now derived from the batch contents, so identical submissions produce identical
|
|
177
|
+
requests and differing ones still differ.
|
|
178
|
+
|
|
179
|
+
- **`google/files` rejected Google's own resource-name format.** `delete()` and `getInfo()`
|
|
180
|
+
normalised the file id only when it contained `/files/` *with* a leading slash — true of the
|
|
181
|
+
full `uri` this adapter returns from `upload()` and `list()`, and false of `files/abc`, the
|
|
182
|
+
canonical `name` the Google API itself returns. Passing that back produced
|
|
183
|
+
`/v1beta/files/files/abc` and a 404.
|
|
184
|
+
|
|
185
|
+
It never broke the library's own round-trip, which is why it survived: the only way to reach it
|
|
186
|
+
was to use the provider's own id format. All three forms — full uri, `files/abc`, and a bare
|
|
187
|
+
name — now normalise to the same request.
|
|
188
|
+
|
|
7
189
|
## [2.2.2] — 2026-08-17
|
|
8
190
|
|
|
9
191
|
### Fixed
|
|
@@ -20,6 +20,10 @@ export type RunTrace = TraceContext & {
|
|
|
20
20
|
sessionId: string;
|
|
21
21
|
requestId: string;
|
|
22
22
|
};
|
|
23
|
+
/** Why a run ended. Written inline in both `complete()` and `stream()` and in
|
|
24
|
+
* `finalizeRun`'s argument list, which is three places to keep in step; named
|
|
25
|
+
* so adding a reason is one edit. */
|
|
26
|
+
export type RunEndReason = 'done' | 'stopped' | 'error' | 'guardrail' | 'max_steps';
|
|
23
27
|
/** Create a fresh StepState for the start of a streaming step. */
|
|
24
28
|
export declare function makeStepState(): StepState;
|
|
25
29
|
/** Accumulate one SSE StreamEvent into StepState.
|
package/dist/agent/loop.d.ts
CHANGED
|
@@ -94,6 +94,41 @@ export declare class AgentLoop {
|
|
|
94
94
|
removeTool(name: string): void;
|
|
95
95
|
toolNames(): string[];
|
|
96
96
|
stop(): void;
|
|
97
|
+
/** Record a thrown error onto the run: emit `onRunError` and return the parts
|
|
98
|
+
* the caller assigns. Both paths report `phase: 'llm_call'`. */
|
|
99
|
+
private recordRunError;
|
|
100
|
+
/** The run's final text.
|
|
101
|
+
*
|
|
102
|
+
* `finalAnswerText` strips `phase: 'commentary'` parts: a codex-family model
|
|
103
|
+
* narrates before it answers and `response.text` concatenates both, so an
|
|
104
|
+
* agent's final output used to include its own thinking-out-loud. Falls back
|
|
105
|
+
* to `.text` when the content carries no text parts, and is identical to
|
|
106
|
+
* `.text` for every model that reports no phase.
|
|
107
|
+
*
|
|
108
|
+
* The streaming path passes what it accumulated; the non-streaming path lets
|
|
109
|
+
* the last response decide. Both share the guardrail / max-steps overrides. */
|
|
110
|
+
private resolveFinalText;
|
|
111
|
+
/** Compose the run's CompletionResponse. `media` and `raw` are parameters
|
|
112
|
+
* because the two paths genuinely differ: the streaming path has already
|
|
113
|
+
* emitted media as events and never holds a raw provider payload. */
|
|
114
|
+
private buildFinalResponse;
|
|
115
|
+
/** Close out a run: write the report, then surface a failure.
|
|
116
|
+
*
|
|
117
|
+
* The order matters and is the reason this is one function rather than two
|
|
118
|
+
* calls at each site — a failed run must still emit its metrics and hooks
|
|
119
|
+
* before the error propagates, or a crash silently loses the run's telemetry.
|
|
120
|
+
* A failed run throws rather than returning empty text, matching the raw
|
|
121
|
+
* client. */
|
|
122
|
+
private settleRun;
|
|
123
|
+
/** Per-step options for the underlying LLM call: loop defaults, then the
|
|
124
|
+
* caller's overrides, then the run's trace.
|
|
125
|
+
*
|
|
126
|
+
* `complete()` and `stream()` duplicated this verbatim, differing only in the
|
|
127
|
+
* name of the local holding the composed system prompt. That is a bad place
|
|
128
|
+
* for a copy: the ctx block below is what stops one conversation arriving at
|
|
129
|
+
* the collector as several unrelated traces, and a fix applied to one path
|
|
130
|
+
* would have left the other silently splitting. */
|
|
131
|
+
private buildStepOptions;
|
|
97
132
|
complete(input: string | ContentPart[] | Message[], options?: ExecuteOptions): Promise<CompletionResponse>;
|
|
98
133
|
/** Run `complete` with a JSON Schema enforced via `structured`, then
|
|
99
134
|
* JSON.parse the response text. Tool calls are still allowed within the
|
|
@@ -14,5 +14,5 @@
|
|
|
14
14
|
* - web_fetch → anthropic (web_fetch_20260318), google (urlContext).
|
|
15
15
|
* OpenAI has no separate fetch tool (its web_search does page-open); xAI /
|
|
16
16
|
* openrouter expose none. */
|
|
17
|
-
import type { ProviderName } from '../types/provider';
|
|
17
|
+
import type { ProviderName } from '../llm/types/provider';
|
|
18
18
|
export declare const PROVIDER_BUILTIN_TOOLS: Record<ProviderName, readonly string[]>;
|
|
@@ -73,6 +73,27 @@ 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
|
+
}
|
|
76
97
|
export interface ModelInfo {
|
|
77
98
|
provider: string;
|
|
78
99
|
/** Catalog key — our canonical (normalised) slug, e.g. `claude-opus-4.8`. */
|
|
@@ -94,6 +115,19 @@ export interface ModelInfo {
|
|
|
94
115
|
/** Other callable ids that resolve to this model (dated snapshots, the bare
|
|
95
116
|
* callable form). Indexed for lookup + accepted as model strings. */
|
|
96
117
|
aliases?: string[];
|
|
118
|
+
/** Per-model wire traits — see ModelWire. */
|
|
119
|
+
wire?: ModelWire;
|
|
120
|
+
/** Which wire spec builds this model's requests, e.g. `anthropic/messages@4.7`.
|
|
121
|
+
*
|
|
122
|
+
* The specs live in `src/wire/specs` and are the artifact the Python and Rust
|
|
123
|
+
* ports consume, so the pin is what lets all three agree on a model without
|
|
124
|
+
* each re-deriving it from the model id.
|
|
125
|
+
*
|
|
126
|
+
* Today the pin is carried and validated but the hand-written adapters still
|
|
127
|
+
* build requests, reading `wire` above. Driving them from the pinned spec is
|
|
128
|
+
* the 3.0.0 step, at which point `wire` goes away — the spec already encodes
|
|
129
|
+
* the same knowledge, which is why a test asserts the two agree. */
|
|
130
|
+
wireSpec?: string;
|
|
97
131
|
/** Model role/modality: chat | code | image | video | tts | stt | embedding | … */
|
|
98
132
|
type?: string;
|
|
99
133
|
/** Content kinds the model ACCEPTS as input: text | image | audio | video |
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* ClientResolver and (in the legacy SDK) by InternalToolRunner. */
|
|
4
4
|
import { LLMClient } from '../llm/client';
|
|
5
5
|
import type { LLMClientConfig } from '../llm/client-config';
|
|
6
|
-
import type { ModelCatalog } from '../
|
|
6
|
+
import type { ModelCatalog } from '../catalog/catalog';
|
|
7
7
|
export declare class ClientPool {
|
|
8
8
|
private readonly catalog?;
|
|
9
9
|
private clients;
|
|
@@ -9,7 +9,7 @@ import type { LLMClientConfig } from '../llm/client-config';
|
|
|
9
9
|
import type { HookBus } from '../bus/hook-bus';
|
|
10
10
|
import type { ProviderName } from '../llm/types/provider';
|
|
11
11
|
import type { ServiceTier } from '../llm/types/tiers';
|
|
12
|
-
import type { ModelCatalog } from '../
|
|
12
|
+
import type { ModelCatalog } from '../catalog/catalog';
|
|
13
13
|
import type { EngineFetch, EngineFetchStream } from '../network/types';
|
|
14
14
|
export interface ClientResolverConfig {
|
|
15
15
|
/** provider → API key. Providers absent from this map can't be resolved. */
|
package/dist/helpers/engine.d.ts
CHANGED
|
@@ -17,13 +17,15 @@
|
|
|
17
17
|
* // llm.client uses engine.fetch + engine.hooks automatically. */
|
|
18
18
|
import { AgentBus } from '../bus/agent-bus';
|
|
19
19
|
import { HookBus } from '../bus/hook-bus';
|
|
20
|
+
import type { LLMClient } from '../llm/client';
|
|
21
|
+
import { type CreateLLMOptions } from './llm';
|
|
20
22
|
import type { ProviderName } from '../llm/types/provider';
|
|
21
23
|
import { NetworkEngine, type QueueSettings } from '../network/engine';
|
|
22
24
|
import type { RetryPolicyOverride } from '../network/queue-state-config';
|
|
23
25
|
import type { EngineConnect, EngineFetch, EngineFetchStream, FetchFn } from '../network/types';
|
|
24
26
|
import { Cache } from '../plugins/cache/cache';
|
|
25
27
|
import { CostCollector } from '../plugins/cost-collector/collector';
|
|
26
|
-
import { ModelCatalog } from '../
|
|
28
|
+
import { ModelCatalog } from '../catalog/catalog';
|
|
27
29
|
import { TelemetryAdapter, type TelemetryAdapterOptions } from '../plugins/telemetry/telemetry';
|
|
28
30
|
import type { Persistence } from '../plugins/persistence/types';
|
|
29
31
|
export interface EngineHandle {
|
|
@@ -63,6 +65,15 @@ export interface EngineHandle {
|
|
|
63
65
|
* createMediaOutput, complete) read these to wire LLM clients without
|
|
64
66
|
* the caller passing apiKey explicitly. */
|
|
65
67
|
apiKeys: Partial<Record<ProviderName, string>>;
|
|
68
|
+
/** Build an LLMClient bound to this engine.
|
|
69
|
+
*
|
|
70
|
+
* Exists so lower layers can obtain a client without importing the helpers
|
|
71
|
+
* layer: `plugins/internal-tools` needs one for LLM-backed tools, and
|
|
72
|
+
* importing `createLLM` directly made `plugins` depend on `helpers` while
|
|
73
|
+
* `helpers` already depended on `plugins` — a cycle that a Rust crate split
|
|
74
|
+
* cannot express. The engine is something those plugins already hold, so it
|
|
75
|
+
* is the natural place to hand the capability down. */
|
|
76
|
+
createClient(options: Omit<CreateLLMOptions, 'engine'>): LLMClient;
|
|
66
77
|
/** Tear down all owned plugins. */
|
|
67
78
|
destroy(): void;
|
|
68
79
|
}
|
package/dist/helpers/mcp.d.ts
CHANGED
|
@@ -11,7 +11,12 @@ import type { AgentTool } from '../agent/types';
|
|
|
11
11
|
import { McpClient } from '../plugins/mcp/client';
|
|
12
12
|
import { type McpAuthProvider } from '../plugins/mcp/oauth';
|
|
13
13
|
import type { SsrfGuardOptions } from '../plugins/mcp/url-guard';
|
|
14
|
-
import { type McpSamplingConfig } from '../plugins/mcp/sampling';
|
|
14
|
+
import { type McpSamplingConfig, type McpSamplingHandler } from '../plugins/mcp/sampling';
|
|
15
|
+
/** Build a sampling handler that fulfils an MCP server's `sampling/createMessage`
|
|
16
|
+
* with our own engine. Thin wiring: the MCP-shape mapping lives in
|
|
17
|
+
* `plugins/mcp/sampling`, which cannot import this layer without closing a
|
|
18
|
+
* dependency cycle, so the completion function is passed down instead. */
|
|
19
|
+
export declare function samplingHandler(config: McpSamplingConfig): McpSamplingHandler;
|
|
15
20
|
import { type McpElicitRequestParams, type McpElicitResult, type McpInitializeResult, type McpRoot, type McpServerConfig } from '../plugins/mcp/types';
|
|
16
21
|
import { type EngineHandle } from './engine';
|
|
17
22
|
export interface ConnectMcpOptions {
|
package/dist/helpers/models.d.ts
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* `{ raw: true }` for bare id strings. Results are cached IN MEMORY for 24h
|
|
10
10
|
* (override with `refresh: true`). */
|
|
11
11
|
import type { ProviderName } from '../llm/types/provider';
|
|
12
|
-
import type { ModelInfo } from '../
|
|
12
|
+
import type { ModelInfo } from '../catalog/catalog';
|
|
13
13
|
import { type EngineHandle } from './engine';
|
|
14
14
|
/** Curated local catalog (the main answer). */
|
|
15
15
|
export declare function listModels(opts?: {
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
* LLMClient.complete. Either way the helper destroys its created client
|
|
16
16
|
* before returning so callers don't leak. */
|
|
17
17
|
import type { AgentTool } from '../agent/types';
|
|
18
|
-
import type { CacheConfig, ThinkingConfig } from '../llm/types/request';
|
|
18
|
+
import type { CacheConfig, ProviderOptions, ThinkingConfig } from '../llm/types/request';
|
|
19
19
|
import type { LLMClientConfig } from '../llm/client-config';
|
|
20
20
|
import type { AudioOptions } from '../llm/types/audio';
|
|
21
21
|
import type { ContentPart, Message } from '../llm/types/messages';
|
|
@@ -84,7 +84,7 @@ export interface CompleteOptions {
|
|
|
84
84
|
/** Optional engine to use. Falls back to coreRegistry default. */
|
|
85
85
|
engine?: EngineHandle;
|
|
86
86
|
/** Provider-specific request options (e.g. `{ openrouter: { models: [...] } }`). */
|
|
87
|
-
providerOptions?:
|
|
87
|
+
providerOptions?: ProviderOptions;
|
|
88
88
|
/** Extra LLMClient options. */
|
|
89
89
|
client?: Partial<Omit<LLMClientConfig, 'provider' | 'model' | 'apiKey'>>;
|
|
90
90
|
/** When set, `estimate()` runs BEFORE the request is sent. If the cost for
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
* Availability-aware: only considers providers with a configured API key.
|
|
12
12
|
* Ranks cheapest-first (tiebreak: newest version); select() returns the single
|
|
13
13
|
* best, selectModels() the ranked list. Thresholds + custom tags are overridable. */
|
|
14
|
-
import type { ModelInfo } from '../
|
|
14
|
+
import type { ModelInfo } from '../catalog/catalog';
|
|
15
15
|
import type { ProviderName } from '../llm/types/provider';
|
|
16
16
|
import { type EngineHandle } from './engine';
|
|
17
17
|
export interface SelectPrefs {
|