@stackstackstack/dsh-llm 0.1.5

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.
@@ -0,0 +1,341 @@
1
+ /**
2
+ * LLM service: adapter registry with a waterfall-interceptable streaming call
3
+ * API. Exports the `LlmRuntime` default, the abstract `LlmAdapter` for
4
+ * provider backends, and `BlockAssembler` for chunk assembly.
5
+ *
6
+ * @module @stackstackstack/dsh-llm
7
+ */
8
+ import { Context, Service } from '@deepseek-ai/cordis';
9
+ import type { GenerateOptions, LlmConfigurableProvider, LlmDiscoveredModel, LlmFailure, LlmModelContext, LlmModelDiscoveryRequest, LlmModelInfo, LlmResolvedModelInfo, LlmProviderInfo, StreamChunk } from './types.ts';
10
+ import type { ResolvedRetryPolicy } from './retry-policy.ts';
11
+ import type { ProviderRequestId } from './brand.ts';
12
+ import type { LlmCallConfig, LlmCallConfigAdapterDefaults } from './call-config.ts';
13
+ import { HarnessError } from './error.ts';
14
+ export * from './attribution.ts';
15
+ export * from './brand.ts';
16
+ export * from './never.ts';
17
+ export * from './error.ts';
18
+ export * from './api-key.ts';
19
+ export * from './types.ts';
20
+ export * from './content.ts';
21
+ export * from './message.ts';
22
+ export * from './retry-policy.ts';
23
+ export { BlockAssembler } from './assembler.ts';
24
+ export { callConfigEquals, deepFreeze, isAgentLoopRequest, markAgentLoopRequest } from './call-config.ts';
25
+ export type { LlmCallConfig, LlmCallConfigAdapterDefaults } from './call-config.ts';
26
+ declare module '@deepseek-ai/cordis' {
27
+ interface Context {
28
+ llm: LlmRuntime;
29
+ }
30
+ interface Events {
31
+ /**
32
+ * Waterfall around every streaming model call (retry, replay, routing).
33
+ * Bound to the {@link LlmRuntime}; call `next()` to reach the resolved
34
+ * adapter's stream, or yield your own chunks to short-circuit.
35
+ * @param options - the full request. A LOOP-built request carries the
36
+ * process-local {@link markAgentLoopRequest} identity and arrives deep-frozen
37
+ * (mutation throws): its content is a pure function of the session log (the
38
+ * reconstructability Agent Note), so listeners read it, never rewrite it.
39
+ * Hand-built calls do not carry that marker; their messages already obey
40
+ * the immutable creation contract.
41
+ * @mode waterfall
42
+ */
43
+ 'llm/stream'(this: LlmRuntime, options: GenerateOptions, next: () => AsyncIterable<StreamChunk>): AsyncIterable<StreamChunk>;
44
+ }
45
+ }
46
+ /** Structured provider facts and cause accepted by {@link LlmError}. */
47
+ export interface LlmErrorOptions extends ErrorOptions {
48
+ /** Valid HTTP status observed at the provider boundary. */
49
+ status?: number;
50
+ /** Positive finite provider-requested delay in milliseconds. */
51
+ providerRetryAfterMs?: number;
52
+ /** Non-empty opaque provider request id. */
53
+ requestId?: ProviderRequestId;
54
+ }
55
+ /**
56
+ * Typed error for LLM-related failures. Extends {@link HarnessError}, so the
57
+ * `code` string (e.g. `AUTH`, `RATE_LIMIT`, `NO_ADAPTER`) is shared taxonomy.
58
+ */
59
+ export declare class LlmError extends HarnessError {
60
+ /** Serializable facts retained beside this live Error. */
61
+ readonly failure: LlmFailure;
62
+ /**
63
+ * @param message - non-empty human-readable failure summary.
64
+ * @param code - non-empty stable provider-neutral machine code.
65
+ * @param options - optional cause and validated serializable provider facts.
66
+ */
67
+ constructor(message: string, code: string, options?: LlmErrorOptions);
68
+ }
69
+ /**
70
+ * Accept one supplied credential, or refuse it as unusable.
71
+ *
72
+ * A stored key arrives from the credentials seam, a `.env` line, or a shell
73
+ * export, all of which pick up surrounding whitespace, so trimming is silent.
74
+ * Anything else fails here rather than inside `fetch`, whose ByteString
75
+ * refusal names a UTF-16 code point instead of the setting to change. The key
76
+ * never enters the message: `ref` names where to fix it, and echoing any part
77
+ * of a secret into a log or a UI is the failure this diagnosis avoids.
78
+ *
79
+ * Lives beside {@link LlmError} rather than in `./api-key.ts` so the predicate
80
+ * module stays dependency-free; both adapters share this one diagnosis instead
81
+ * of keeping near-identical local copies.
82
+ * @param raw - the credential exactly as supplied.
83
+ * @param pkg - the refusing package name, prefixed to the diagnostic.
84
+ * @param ref - the credential reference the value resolved through.
85
+ * @returns the trimmed, usable key.
86
+ */
87
+ export declare function assertUsableApiKey(raw: string, pkg: string, ref: string): string;
88
+ /** One model call whose config and adapter registration were resolved together. */
89
+ export interface PreparedLlmCall {
90
+ /** Detached, deep-frozen config with any adapter-owned default materialized. */
91
+ readonly config: LlmCallConfig;
92
+ /** Immutable retry policy captured with the adapter registration. */
93
+ readonly retryPolicy: ResolvedRetryPolicy;
94
+ /** Detached context metadata resolved with the registration-bound call. */
95
+ readonly context?: LlmModelContext;
96
+ /** Config fields materialized by the captured adapter rather than proposed by the caller. */
97
+ readonly adapterDefaults: LlmCallConfigAdapterDefaults;
98
+ /**
99
+ * Dispatch this call once through the registration captured during
100
+ * preparation. The request's call-config fields must match {@link config};
101
+ * reuse or mismatch fails with `INVALID_PREPARED_CALL`.
102
+ * @param options - fully assembled request carrying the prepared config.
103
+ * @returns the chunk stream, including the `llm/stream` waterfall.
104
+ */
105
+ stream(options: GenerateOptions): AsyncIterable<StreamChunk>;
106
+ }
107
+ /**
108
+ * Provider-wire adapter for the harness message and stream vocabulary. Register implementations
109
+ * with `ctx.llm.registerAdapter(providers, adapter)`. Every provider HTTP request must include
110
+ * `attributionHeaders()`; prove the headers are added in the wire request or library header hook. The direct-fetch
111
+ * DeepSeek and library-backed pi-ai adapters meet this contract through different internals.
112
+ */
113
+ export declare abstract class LlmAdapter {
114
+ /**
115
+ * Describe one provider route owned by this adapter.
116
+ * @param provider - a route passed to `registerAdapter()` for this instance.
117
+ * @returns detached display metadata whose id must equal `provider`.
118
+ */
119
+ providerInfo(provider: string): LlmProviderInfo;
120
+ /**
121
+ * Return the provider-owned retry policy captured with this route.
122
+ * @param _provider - a route passed to `registerAdapter()` for this instance.
123
+ * @returns a resolved policy, or `undefined` to use the normal defaults.
124
+ */
125
+ providerRetryPolicy(_provider: string): ResolvedRetryPolicy | undefined;
126
+ /**
127
+ * List models this adapter can currently advertise for one owned provider.
128
+ * The result is advisory: an adapter may accept unlisted model ids, and
129
+ * consumers must not turn absence into request rejection.
130
+ * @param _provider - one provider route owned by this adapter.
131
+ * @returns discoverable models in adapter-preferred order.
132
+ */
133
+ listModels(_provider: string): Promise<readonly LlmModelInfo[]>;
134
+ /**
135
+ * Resolve all metadata available for one exact model. This query is
136
+ * independent of the advisory catalog and does not validate request routing.
137
+ * @param provider - one provider route owned by this adapter.
138
+ * @param model - exact model id passed to {@link GenerateOptions.model}.
139
+ * @param _signal - cancellation for this exact-model lookup; asynchronous
140
+ * implementations must settle promptly after it aborts.
141
+ * @returns provider/model identity plus any context, call-default, and reasoning metadata.
142
+ */
143
+ resolveModel(provider: string, model: string, _signal?: AbortSignal): Promise<LlmResolvedModelInfo>;
144
+ /**
145
+ * Stream one model call as raw chunks. The only required method.
146
+ * @param options - the fully-assembled request; implementations must honor `options.signal`.
147
+ * @returns the chunk stream, obeying the adapter contract documented on `StreamChunk`.
148
+ */
149
+ abstract stream(options: GenerateOptions): AsyncIterable<StreamChunk>;
150
+ }
151
+ /**
152
+ * What {@link LlmRuntime.registerAdapter} returns: the disposer, plus an
153
+ * atomic route replacement for the same adapter instance.
154
+ */
155
+ export interface AdapterRegistrationHandle {
156
+ /** Release every route this registration currently holds. */
157
+ (): void;
158
+ /**
159
+ * Replace this registration's routes with `providers`, keeping the same
160
+ * adapter instance. The candidate set is validated in full first — a
161
+ * conflict with another adapter, an invalid name, or bad provider metadata
162
+ * throws and leaves the current routes untouched — and the swap itself is
163
+ * one synchronous section, so no request can observe a gap. An empty array
164
+ * is legal here (a settings section that emptied holds zero routes while
165
+ * staying registered), unlike an empty initial registration.
166
+ *
167
+ * Throws `LlmError` with code `REGISTRATION_DISPOSED` once the registration
168
+ * has been released: its routes are gone and its disposer has already run,
169
+ * so anything registered afterwards would have no owner left to release it.
170
+ * @param providers - the complete next route set for this registration.
171
+ */
172
+ replace(providers: string[]): void;
173
+ }
174
+ /**
175
+ * A live configurable-provider registration, disposable and atomically
176
+ * replaceable — the directory counterpart of {@link AdapterRegistrationHandle}.
177
+ */
178
+ export interface DirectoryRegistrationHandle {
179
+ /** Withdraw every entry this registration currently holds. */
180
+ (): void;
181
+ /**
182
+ * Replace this registration's entries with `entries`. The candidate set is
183
+ * validated in full first — an entry another registration already declares,
184
+ * a duplicate within the set, or invalid metadata throws and leaves the
185
+ * current entries untouched — and the swap is one synchronous section, so no
186
+ * reader observes a gap. An empty array is legal here, unlike an empty
187
+ * initial registration.
188
+ *
189
+ * Throws `LlmError` with code `REGISTRATION_DISPOSED` once the registration
190
+ * has been disposed.
191
+ */
192
+ replace(entries: readonly LlmConfigurableProvider[]): void;
193
+ }
194
+ /**
195
+ * The abstract `llm` service: an adapter registry plus a streaming model-call
196
+ * API, interceptable via the `llm/stream` waterfall.
197
+ */
198
+ export declare class LlmRuntime extends Service {
199
+ private adapters;
200
+ private directory;
201
+ private discoveries;
202
+ constructor(ctx: Context);
203
+ /** Notify topology observers without letting one broken listener veto the commit. */
204
+ private emitAdaptersUpdated;
205
+ /** Contained-listener diagnostic shared by the sync and async failure paths. */
206
+ private warnAdaptersListenerFailure;
207
+ /**
208
+ * Register an adapter for the given provider routes. Throws `LlmError` with code
209
+ * `DUPLICATE_ADAPTER` if any provider already has an adapter (all-or-nothing).
210
+ * Disposed with the fiber.
211
+ * @param providers - every provider route this adapter should serve.
212
+ * @param adapter - the adapter that streams calls for those providers.
213
+ * @returns the disposer, carrying {@link AdapterRegistrationHandle.replace}.
214
+ */
215
+ registerAdapter(providers: string[], adapter: LlmAdapter): AdapterRegistrationHandle;
216
+ /**
217
+ * Validate one candidate route set for `adapter`, treating routes this
218
+ * registration already holds as available. Nothing is mutated: a rejected
219
+ * candidate leaves the registry exactly as it was.
220
+ */
221
+ private prepareRoutes;
222
+ /**
223
+ * Swap this registration's routes for the prepared ones in one synchronous
224
+ * section, so no observer can see the registry between the release and the
225
+ * re-registration. The route set's one mutation point is also where
226
+ * `llm/adapters-updated` is published, so a `replace` announces itself
227
+ * exactly like a first registration.
228
+ */
229
+ private commitRoutes;
230
+ /**
231
+ * Describe provider routes with a registered adapter.
232
+ * @returns detached provider metadata in registration order.
233
+ */
234
+ listProviders(): LlmProviderInfo[];
235
+ /**
236
+ * Declare provider routes an adapter plugin can activate through
237
+ * configuration. Registration is all-or-nothing: an empty list, invalid
238
+ * entry, or a provider already declared by any registration throws
239
+ * `LlmError` without registering the rest. Disposed with the fiber.
240
+ * @param entries - every configurable provider this plugin owns.
241
+ * @returns a handle that withdraws all of them, and can atomically replace them.
242
+ */
243
+ registerConfigurableProviders(entries: readonly LlmConfigurableProvider[]): DirectoryRegistrationHandle;
244
+ /**
245
+ * List every declared configurable provider, registered or dormant.
246
+ * @returns detached directory entries in declaration order.
247
+ */
248
+ listConfigurableProviders(): LlmConfigurableProvider[];
249
+ /**
250
+ * Offer to interrogate provider endpoints on behalf of the settings
251
+ * namespace this plugin owns. The namespace is the key because that is what
252
+ * a configuration surface already holds from the configurable-provider
253
+ * directory, and because a provider being *added* has no route to name yet.
254
+ * Disposed with the fiber.
255
+ * @param settingsNs - the namespace whose profiles this discovery serves.
256
+ * @param discover - interrogates one endpoint; must honor `request.signal`.
257
+ * @returns the disposer that withdraws the offer.
258
+ */
259
+ registerModelDiscovery(settingsNs: string, discover: (request: LlmModelDiscoveryRequest) => Promise<readonly LlmDiscoveredModel[]>): () => void;
260
+ /**
261
+ * Interrogate one provider endpoint for the models it advertises. The
262
+ * request describes a draft, not a stored route, so nothing here reads or
263
+ * writes settings or credentials — the caller owns both, and the reply is
264
+ * candidate metadata a surface may offer for adoption.
265
+ * @param settingsNs - namespace whose registered discovery serves this draft.
266
+ * @param request - the endpoint, protocol, and one-shot credential to use.
267
+ * @returns the advertised models, deduplicated in endpoint order.
268
+ */
269
+ discoverModels(settingsNs: string, request: LlmModelDiscoveryRequest): Promise<LlmDiscoveredModel[]>;
270
+ /**
271
+ * Resolve the retry policy captured when one provider route was registered.
272
+ * @param provider - registered provider route to inspect.
273
+ * @returns the provider-owned policy, with normal defaults already resolved.
274
+ */
275
+ providerRetryPolicy(provider: string): ResolvedRetryPolicy;
276
+ /** Detach typed adapter-owned modality metadata. */
277
+ private detachedModalities;
278
+ /**
279
+ * Discover models advertised by one registered provider. Catalog membership
280
+ * is advisory and never changes routing or request validation.
281
+ * @param provider - registered provider route to inspect.
282
+ * @returns detached model metadata in adapter-preferred order.
283
+ */
284
+ listModels(provider: string): Promise<LlmModelInfo[]>;
285
+ /**
286
+ * Resolve and validate all metadata from the adapter that owns one exact
287
+ * route. The result is detached from adapter-owned objects; catalog
288
+ * membership remains advisory and does not control request routing.
289
+ * @param provider - registered provider route to inspect.
290
+ * @param model - exact model id passed to the adapter.
291
+ * @param signal - optional cancellation for adapter-owned asynchronous lookup.
292
+ * @returns exact model identity plus available context and reasoning metadata.
293
+ */
294
+ resolveModelInfo(provider: string, model: string, signal?: AbortSignal): Promise<LlmResolvedModelInfo>;
295
+ private resolveModelInfoFor;
296
+ /**
297
+ * Validate a conversation call config against its exact model capability and
298
+ * materialize adapter-configured defaults. Unsupported explicit efforts
299
+ * reject before provider I/O; no clamping or aliasing is performed. This
300
+ * standalone query does not bind a later dispatch; use {@link prepareCall}
301
+ * when logging and streaming must share one adapter registration.
302
+ * @param config - provider/model route and optional request controls.
303
+ * @param signal - optional cancellation for adapter-owned capability lookup.
304
+ * @returns a detached config only when a default must be materialized.
305
+ */
306
+ resolveCallConfig(config: LlmCallConfig, signal?: AbortSignal): Promise<LlmCallConfig>;
307
+ private resolveCallFor;
308
+ /**
309
+ * Resolve one call under its current adapter registration. The returned
310
+ * one-shot handle keeps that registration across header logging and dispatch,
311
+ * so HMR cannot combine one adapter's capability result with another adapter.
312
+ * @param config - provider/model route and optional request controls.
313
+ * @param signal - optional cancellation for adapter-owned capability lookup.
314
+ * @returns a prepared config and its registration-bound stream entry point.
315
+ */
316
+ prepareCall(config: LlmCallConfig, signal?: AbortSignal): Promise<PreparedLlmCall>;
317
+ private registration;
318
+ /** Remove replay state whose historical route is owned by another adapter. */
319
+ private forAdapter;
320
+ /**
321
+ * Final adapter boundary. Adapter selection, dispatch, iterator construction,
322
+ * and iteration failures become one terminal failure chunk. Middleware and
323
+ * downstream consumer failures remain thrown plugin or consumer errors.
324
+ */
325
+ private adapterStream;
326
+ /**
327
+ * Stream one model call as raw chunks (token-level deltas). Replay state is
328
+ * retained only when the same adapter instance owns its historical provider
329
+ * and the target provider. Final adapter selection remains fixed through
330
+ * asynchronous exact-model resolution and dispatch. Adapter selection,
331
+ * dispatch, and iteration failures become terminal `error` or `aborted`
332
+ * finish chunks; middleware, nested-call, cleanup, and consumer failures
333
+ * remain thrown.
334
+ * @param options - the full request; `options.provider` selects the adapter.
335
+ * @returns the chunk stream, possibly wrapped by `llm/stream` listeners.
336
+ */
337
+ stream(options: GenerateOptions): AsyncIterable<StreamChunk>;
338
+ private streamWithRegistration;
339
+ }
340
+ export default LlmRuntime;
341
+ //# sourceMappingURL=index.d.ts.map