@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,349 @@
1
+ /**
2
+ * Canonical provider-neutral message and streaming vocabulary for the loop,
3
+ * session log, and plugins. Adapters alone translate provider wire messages;
4
+ * mapped interfaces make the content, source, and finish unions extensible.
5
+ */
6
+ import type { Branded } from '@stackstackstack/dsh-brand';
7
+ import type { ImageAttachmentRef } from '@stackstackstack/dsh-attachment';
8
+ import type { CallId, ProviderRequestId, ReasoningEffortId } from './brand.ts';
9
+ import type { Message } from './message.ts';
10
+ declare module '@deepseek-ai/cordis' {
11
+ interface Events {
12
+ /**
13
+ * The provider topology changed: an adapter registered or unregistered
14
+ * routes, or the configurable-provider directory gained or lost entries.
15
+ * This payload-free registry notification fires at each commit point
16
+ * (including registration disposal); consumers re-read `listProviders()`,
17
+ * `listModels()`, or `listConfigurableProviders()` for the new state.
18
+ * Observer failures are contained and cannot veto the registry mutation.
19
+ * @mode emit
20
+ */
21
+ 'llm/adapters-updated'(): void;
22
+ }
23
+ }
24
+ export type { AssistantMessage, AssistantProvenance, Message, MessageSource, MessageSourceMap, ModelMessageSource, ToolMessageSource, ToolResultMessage, UserMessage, } from './message.ts';
25
+ /** Serializable provider or transport failure facts; policy decides whether they are retryable. */
26
+ export interface LlmFailure {
27
+ /** Human-readable provider or transport failure. */
28
+ readonly message: string;
29
+ /** Stable provider-neutral machine-routing code. */
30
+ readonly code: string;
31
+ /** HTTP status returned by the provider, when available. */
32
+ readonly status?: number;
33
+ /** Provider-requested delay in milliseconds, when valid and available. */
34
+ readonly providerRetryAfterMs?: number;
35
+ /** Opaque provider-issued request identifier for diagnostics. */
36
+ readonly requestId?: ProviderRequestId;
37
+ }
38
+ /** Plain text visible to the end user. */
39
+ export interface TextBlock {
40
+ type: 'text';
41
+ text: string;
42
+ }
43
+ /** Reasoning / thinking content, distinct from visible text. */
44
+ export interface ReasoningBlock {
45
+ type: 'reasoning';
46
+ text: string;
47
+ }
48
+ /**
49
+ * A durable raster image reference, valid in user or assistant content. The
50
+ * block is deliberately role-neutral; assistant-side rendering is forward
51
+ * compatibility — the current production adapters declare text-only output,
52
+ * so only user content carries images today.
53
+ */
54
+ export interface ImageBlock {
55
+ type: 'image';
56
+ /** Immutable bytes and intrinsic display metadata owned by the attachment service. */
57
+ attachment: ImageAttachmentRef;
58
+ }
59
+ /** A tool invocation requested by the model. */
60
+ export interface ToolCallBlock {
61
+ type: 'tool-call';
62
+ /** Provider-issued call id; correlates with the matching tool result. */
63
+ id: CallId;
64
+ name: string;
65
+ /** Raw JSON string as produced by the model. */
66
+ arguments: string;
67
+ }
68
+ /** The result of a tool invocation, sent back to the model. */
69
+ export interface ToolResultBlock {
70
+ type: 'tool-result';
71
+ toolCallId: CallId;
72
+ content: ContentBlock[];
73
+ isError?: boolean;
74
+ }
75
+ /**
76
+ * Merge-extensible content blocks keyed by `type`. New core blocks must land
77
+ * with adapter, UI, and compaction support.
78
+ */
79
+ export interface ContentBlockMap {
80
+ 'text': TextBlock;
81
+ 'reasoning': ReasoningBlock;
82
+ 'image': ImageBlock;
83
+ 'tool-call': ToolCallBlock;
84
+ 'tool-result': ToolResultBlock;
85
+ }
86
+ /** The block `type` tag vocabulary; widens as plugins add entries to {@link ContentBlockMap}. */
87
+ export type ContentBlockType = keyof ContentBlockMap;
88
+ /** Any known content block, derived from {@link ContentBlockMap}; switch on `type` and fall through unknowns (merge-extensible). */
89
+ export type ContentBlock = ContentBlockMap[ContentBlockType];
90
+ /**
91
+ * Why a model response stopped.
92
+ * Merge-extensible so adapters can surface provider-specific reasons.
93
+ */
94
+ export interface FinishReasonMap {
95
+ 'stop': {
96
+ kind: 'stop';
97
+ };
98
+ 'tool-calls': {
99
+ kind: 'tool-calls';
100
+ };
101
+ 'max-tokens': {
102
+ kind: 'max-tokens';
103
+ };
104
+ 'aborted': {
105
+ kind: 'aborted';
106
+ failure: LlmFailure;
107
+ };
108
+ 'error': {
109
+ kind: 'error';
110
+ failure: LlmFailure;
111
+ };
112
+ }
113
+ /** Any known finish reason, derived from {@link FinishReasonMap}; switch on `kind` and fall through unknowns (merge-extensible). */
114
+ export type FinishReason = FinishReasonMap[keyof FinishReasonMap];
115
+ /**
116
+ * Token accounting for one model call (cache fields are optional).
117
+ *
118
+ * Counts are DISJOINT: `inputTokens` is uncached input only; cached input is
119
+ * reported separately as `cacheReadTokens`/`cacheWriteTokens` (billed input =
120
+ * sum of the three). Adapters whose providers fold cache hits into a total
121
+ * prompt count (DeepSeek's `prompt_tokens`) subtract them out.
122
+ */
123
+ export interface TokenUsage {
124
+ inputTokens: number;
125
+ outputTokens: number;
126
+ cacheReadTokens?: number;
127
+ cacheWriteTokens?: number;
128
+ reasoningTokens?: number;
129
+ }
130
+ /** Display metadata for one registered provider route. */
131
+ export interface LlmProviderInfo {
132
+ /** Provider route key used by {@link GenerateOptions.provider}. */
133
+ id: string;
134
+ /** Human-readable provider name for selectors and diagnostics. */
135
+ name: string;
136
+ }
137
+ /** Merge-extensible provider model modality vocabulary. */
138
+ export interface ModelModalityMap {
139
+ text: 'text';
140
+ image: 'image';
141
+ }
142
+ /** Any declared provider model modality. */
143
+ export type ModelModality = ModelModalityMap[keyof ModelModalityMap];
144
+ /**
145
+ * One provider route an adapter plugin can activate through configuration,
146
+ * whether or not the route is currently registered. Configuration surfaces
147
+ * merge this directory with `listProviders()` to offer every configurable
148
+ * provider alongside its live/dormant state.
149
+ */
150
+ export interface LlmConfigurableProvider {
151
+ /** Provider route key this entry activates when configured. */
152
+ provider: string;
153
+ /** Human-readable provider name for configuration surfaces. */
154
+ displayName: string;
155
+ /** User-settings namespace whose section configures this provider. */
156
+ settingsNs: string;
157
+ /**
158
+ * Path from that namespace's section root to this provider's profile
159
+ * object; empty when the whole section is the profile.
160
+ */
161
+ settingsPath: readonly string[];
162
+ /**
163
+ * Whether the owning adapter knows this route only because configuration
164
+ * declared it — a gateway or self-hosted server it ships nothing about.
165
+ * Absent means the adapter draws no such distinction; false means it does
166
+ * and this route is one of its own. Only the adapter can answer: a stored
167
+ * profile is how a user-added route AND a corrected shipped one both look
168
+ * from outside.
169
+ */
170
+ declared?: boolean;
171
+ }
172
+ /**
173
+ * One interrogation of a provider endpoint that configuration has not stored
174
+ * yet. Configuration surfaces send the draft a user is still editing, so the
175
+ * request carries the endpoint and credential directly instead of naming a
176
+ * route: a provider being added has no route to name.
177
+ */
178
+ export interface LlmModelDiscoveryRequest {
179
+ /**
180
+ * Route the draft is editing, when it edits an existing one. A route whose
181
+ * adapter already knows its models answers from that knowledge instead of
182
+ * asking the endpoint — the adapter's own registry is the better answer, and
183
+ * it costs no network call.
184
+ */
185
+ provider?: string;
186
+ /**
187
+ * Endpoint to interrogate. Optional because a route the adapter already
188
+ * describes needs none; a route it does not must supply one.
189
+ */
190
+ baseURL?: string;
191
+ /** Wire protocol the endpoint speaks, when the draft names one. */
192
+ api?: string;
193
+ /** Credential for this interrogation alone; the harness never stores it. */
194
+ apiKey?: string;
195
+ /** Caller cancellation; implementations must settle promptly after it aborts. */
196
+ signal?: AbortSignal;
197
+ }
198
+ /**
199
+ * One model an endpoint reports about itself. Every field but the id is
200
+ * optional because most provider listings disclose an id and nothing else;
201
+ * a surface adopting one of these still owes the capacities its adapter needs.
202
+ */
203
+ export interface LlmDiscoveredModel {
204
+ /** Model id the endpoint accepts. */
205
+ id: string;
206
+ /** Human-readable name when the endpoint supplies one. */
207
+ name?: string;
208
+ /** Maximum combined request and response context, when disclosed. */
209
+ contextWindow?: number;
210
+ /** Maximum output tokens, when disclosed. */
211
+ maxTokens?: number;
212
+ }
213
+ /** One adapter-discovered model; catalog membership is advisory, not request validation. */
214
+ export interface LlmModelInfo {
215
+ /** Provider route that owns this model entry. */
216
+ provider: string;
217
+ /** Model id passed to {@link GenerateOptions.model}. */
218
+ id: string;
219
+ /** Human-readable model name for selectors. */
220
+ name: string;
221
+ /** Optional user-facing distinction from otherwise similar models. */
222
+ description?: string;
223
+ /** Accepted request modalities; absent means unknown, while an explicit omission is negative capability. */
224
+ inputModalities?: readonly ModelModality[];
225
+ }
226
+ /** Provider-owned context capacity for one exact provider/model route. */
227
+ export interface LlmModelContext {
228
+ /** Maximum combined request and response context in tokens. */
229
+ contextWindow: number;
230
+ }
231
+ /** Display metadata for one adapter-owned reasoning effort. */
232
+ export interface LlmReasoningEffortInfo {
233
+ /** Opaque stable value accepted by {@link GenerateOptions.reasoningEffort}. */
234
+ id: ReasoningEffortId;
235
+ /** Human-readable effort name for selectors and diagnostics. */
236
+ name: string;
237
+ /** Optional user-facing distinction from otherwise similar efforts. */
238
+ description?: string;
239
+ }
240
+ /** Selectable reasoning efforts for one exact provider/model route. */
241
+ export interface LlmModelReasoningInfo {
242
+ /** Supported efforts in adapter-preferred display order. */
243
+ efforts: readonly LlmReasoningEffortInfo[];
244
+ /**
245
+ * Adapter-configured default materialized into requests when callers omit
246
+ * an effort. Absence preserves the provider's own default.
247
+ */
248
+ defaultEffort?: ReasoningEffortId;
249
+ }
250
+ /** Exact-route model metadata resolved by its owning adapter. */
251
+ export interface LlmResolvedModelInfo extends LlmModelInfo {
252
+ /** Provider-owned context capacity when known. */
253
+ context?: LlmModelContext;
254
+ /** Adapter-configured per-request output cap materialized when callers omit one. */
255
+ defaultMaxTokens?: number;
256
+ /** Adapter-owned selectable reasoning levels when exposed. */
257
+ reasoning?: LlmModelReasoningInfo;
258
+ }
259
+ /**
260
+ * Raw streaming protocol emitted by adapters.
261
+ * Block indexes correlate interleaved deltas, and `block-end` carries the
262
+ * assembled block. Adapters emit usage before the terminal finish and nothing
263
+ * afterward; tool arguments remain raw JSON strings. An adapter implementation
264
+ * may throw, but `LlmRuntime.stream()` normalizes that failure to a terminal
265
+ * `error` or `aborted` finish before exposing it to consumers.
266
+ */
267
+ export type StreamChunk = {
268
+ type: 'block-start';
269
+ index: number;
270
+ blockType: ContentBlockType;
271
+ } | {
272
+ type: 'text-delta';
273
+ index: number;
274
+ text: string;
275
+ } | {
276
+ type: 'reasoning-delta';
277
+ index: number;
278
+ text: string;
279
+ } | {
280
+ type: 'tool-call-delta';
281
+ index: number;
282
+ id: CallId;
283
+ name?: string;
284
+ argumentsDelta: string;
285
+ } | {
286
+ type: 'block-end';
287
+ index: number;
288
+ block: ContentBlock;
289
+ } | {
290
+ type: 'usage';
291
+ usage: TokenUsage;
292
+ } | {
293
+ type: 'finish';
294
+ reason: FinishReason;
295
+ /** Adapter-private lossless-JSON state for replaying a successful response. */
296
+ replayState?: unknown;
297
+ };
298
+ /**
299
+ * JSON-schema description of a tool, as sent to the model.
300
+ *
301
+ * Declared here (not in dsh-tools) because it is part of {@link GenerateOptions};
302
+ * dsh-tools' ToolDefinition and dsh-system-prompt's PromptAssembly both import
303
+ * it from this package.
304
+ */
305
+ export interface ToolSchema {
306
+ name: string;
307
+ description: string;
308
+ /** JSON Schema object for the arguments. */
309
+ parameters: Record<string, unknown>;
310
+ }
311
+ /** A single model request, fully assembled. */
312
+ export interface GenerateOptions {
313
+ /** Registered provider route selecting the adapter instance. */
314
+ provider: string;
315
+ model: string;
316
+ /** Adapter-owned reasoning effort selected for this exact model. */
317
+ reasoningEffort?: ReasoningEffortId;
318
+ /**
319
+ * Ordered conversation messages, exactly as the provider sees them (after
320
+ * the `system` slot). A loop-built request assembles them as
321
+ * the derived history (dsh-agent-loop); a hand-built one-shot passes any list.
322
+ */
323
+ messages: Message[];
324
+ /** System prompt text (adapters map to the provider's system slot). */
325
+ system?: string;
326
+ /** Tool schemas (adapters map to the provider's `tools` field). */
327
+ tools?: ToolSchema[];
328
+ temperature?: number;
329
+ maxTokens?: number;
330
+ /**
331
+ * Stop sequences: generation halts as soon as the model produces any one of
332
+ * these strings (adapters map to the provider's stop field, e.g. OpenAI
333
+ * `stop`). The stop string itself is not included in the output.
334
+ */
335
+ stop?: string[];
336
+ signal?: AbortSignal;
337
+ /**
338
+ * Session identity stamped by the loop for request routing. Replay uses it
339
+ * to separate cursors; adapters may map it to model-hidden transport metadata.
340
+ */
341
+ sessionId?: Branded<'SessionId'>;
342
+ /**
343
+ * Provider-neutral classification for an auxiliary model call. Adapters may
344
+ * map the purpose to model-hidden transport metadata or purpose-specific
345
+ * generation policy. Ordinary conversation requests leave it unset.
346
+ */
347
+ purpose?: 'compaction' | 'session-title';
348
+ }
349
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Canonical provider-neutral message and streaming vocabulary for the loop,
3
+ * session log, and plugins. Adapters alone translate provider wire messages;
4
+ * mapped interfaces make the content, source, and finish unions extensible.
5
+ */
6
+ export {};
7
+ //# sourceMappingURL=types.js.map
package/package.json ADDED
@@ -0,0 +1,64 @@
1
+ {
2
+ "name": "@stackstackstack/dsh-llm",
3
+ "description": "Provider-neutral LLM service interface for the DeepSeek Harness",
4
+ "version": "0.1.5",
5
+ "publishConfig": {
6
+ "access": "public"
7
+ },
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/deepseek-ai/deepseek-harness.git",
11
+ "directory": "packages/llm/llm"
12
+ },
13
+ "type": "module",
14
+ "main": "lib/index.js",
15
+ "types": "lib/types/index.d.ts",
16
+ "exports": {
17
+ ".": {
18
+ "types": "./lib/types/index.d.ts",
19
+ "default": "./lib/index.js"
20
+ },
21
+ "./invariant": {
22
+ "types": "./lib/types/invariant.d.ts",
23
+ "default": "./lib/invariant.js"
24
+ },
25
+ "./types": {
26
+ "types": "./lib/types/types.d.ts",
27
+ "default": "./lib/types/types.js"
28
+ },
29
+ "./brand": {
30
+ "types": "./lib/types/brand.d.ts",
31
+ "default": "./lib/types/brand.js"
32
+ },
33
+ "./message": {
34
+ "types": "./lib/types/message.d.ts",
35
+ "default": "./lib/types/message.js"
36
+ },
37
+ "./src/*": "./src/*",
38
+ "./package.json": "./package.json"
39
+ },
40
+ "files": [
41
+ "lib/index.js",
42
+ "lib/invariant.js",
43
+ "lib/types/**/*.js",
44
+ "lib/types/**/*.d.ts"
45
+ ],
46
+ "license": "MIT",
47
+ "peerDependencies": {
48
+ "@stackstackstack/dsh-attachment": "^0.1.5",
49
+ "@stackstackstack/dsh-brand": "^0.1.5",
50
+ "@stackstackstack/dsh-invariants": "^0.1.5",
51
+ "@deepseek-ai/cordis": "^4.0.1",
52
+ "@stackstackstack/dsh-timeout": "^0.1.5"
53
+ },
54
+ "dependencies": {
55
+ "@deepseek-ai/schemastery": "^3.18.1"
56
+ },
57
+ "devDependencies": {
58
+ "@stackstackstack/dsh-attachment": "^0.1.5",
59
+ "@stackstackstack/dsh-brand": "^0.1.5",
60
+ "@stackstackstack/dsh-timeout": "^0.1.5",
61
+ "@deepseek-ai/cordis": "^4.0.1",
62
+ "@stackstackstack/dsh-invariants": "^0.1.5"
63
+ }
64
+ }