@sembl/core 0.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/LICENSE +21 -0
- package/README.md +103 -0
- package/dist/index.d.ts +742 -0
- package/dist/index.js +1059 -0
- package/dist/index.js.map +1 -0
- package/package.json +57 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,742 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bounds a field's value beyond its type.
|
|
3
|
+
*
|
|
4
|
+
* Which keys apply depends on the value being checked, not on the declared
|
|
5
|
+
* field type: string bounds apply to strings, numeric bounds to numbers, and
|
|
6
|
+
* item bounds to arrays. On an array field the string/number bounds apply to
|
|
7
|
+
* each element, so `string[]` can carry both `maxItems` and `maxLength`.
|
|
8
|
+
*/
|
|
9
|
+
interface FieldConstraints {
|
|
10
|
+
/** Maximum string length, inclusive */
|
|
11
|
+
maxLength?: number;
|
|
12
|
+
/** Minimum string length, inclusive */
|
|
13
|
+
minLength?: number;
|
|
14
|
+
/** Minimum numeric value, inclusive */
|
|
15
|
+
minimum?: number;
|
|
16
|
+
/** Maximum numeric value, inclusive */
|
|
17
|
+
maximum?: number;
|
|
18
|
+
/** Minimum number of array entries, inclusive */
|
|
19
|
+
minItems?: number;
|
|
20
|
+
/** Maximum number of array entries, inclusive */
|
|
21
|
+
maxItems?: number;
|
|
22
|
+
/** Regular expression source a string value must match */
|
|
23
|
+
pattern?: string;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Describes the type of a schema field.
|
|
27
|
+
*
|
|
28
|
+
* `enum` values are fixed when the schema is compiled. `dynamicEnum` defers
|
|
29
|
+
* them to coercion time, naming a source the caller resolves — for value sets
|
|
30
|
+
* that live in a database or CMS rather than in the source tree.
|
|
31
|
+
*/
|
|
32
|
+
type FieldType = {
|
|
33
|
+
kind: "string";
|
|
34
|
+
} | {
|
|
35
|
+
kind: "number";
|
|
36
|
+
} | {
|
|
37
|
+
kind: "boolean";
|
|
38
|
+
} | {
|
|
39
|
+
kind: "array";
|
|
40
|
+
items: FieldType;
|
|
41
|
+
} | {
|
|
42
|
+
kind: "object";
|
|
43
|
+
nestedSchemaId: string;
|
|
44
|
+
} | {
|
|
45
|
+
kind: "enum";
|
|
46
|
+
values: string[];
|
|
47
|
+
} | {
|
|
48
|
+
kind: "dynamicEnum";
|
|
49
|
+
sourceId: string;
|
|
50
|
+
};
|
|
51
|
+
/**
|
|
52
|
+
* Describes a single field in a schema.
|
|
53
|
+
*/
|
|
54
|
+
interface FieldDescriptor {
|
|
55
|
+
/** Property name as it appears in the class */
|
|
56
|
+
name: string;
|
|
57
|
+
/** Semantic description from @Describe decorator */
|
|
58
|
+
description: string;
|
|
59
|
+
/** The resolved type of this field */
|
|
60
|
+
type: FieldType;
|
|
61
|
+
/** Whether this field is required (definite assignment) */
|
|
62
|
+
required: boolean;
|
|
63
|
+
/** Value bounds from the @Constrain decorator, if any */
|
|
64
|
+
constraints?: FieldConstraints;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* A compiled runtime schema representing a decorated class.
|
|
68
|
+
*/
|
|
69
|
+
interface RuntimeSchema {
|
|
70
|
+
/** Unique identifier, typically the class name */
|
|
71
|
+
id: string;
|
|
72
|
+
/** Semantic description from @Schema decorator */
|
|
73
|
+
description: string;
|
|
74
|
+
/** All fields extracted from the class */
|
|
75
|
+
fields: FieldDescriptor[];
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* A bundle of all schemas extracted from a set of source files.
|
|
79
|
+
*/
|
|
80
|
+
interface SchemaBundle {
|
|
81
|
+
/** Map of schema ID to RuntimeSchema */
|
|
82
|
+
schemas: Record<string, RuntimeSchema>;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Supplies the legal values for a `dynamicEnum` source at coercion time.
|
|
87
|
+
*
|
|
88
|
+
* Sources are opaque to SEMBL: the id is whatever the schema author wrote in
|
|
89
|
+
* `@ValuesFrom("...")`, and the caller decides how to turn it into values (a
|
|
90
|
+
* CMS fetch, a database query, a static map). Called at most once per distinct
|
|
91
|
+
* source id per coercion; the caller owns any caching across coercions.
|
|
92
|
+
*
|
|
93
|
+
* ```ts
|
|
94
|
+
* const enumResolver: EnumResolver = async (sourceId) => {
|
|
95
|
+
* const docs = await cms.taxonomy(sourceId);
|
|
96
|
+
* return docs.map((d) => d.slug);
|
|
97
|
+
* };
|
|
98
|
+
* ```
|
|
99
|
+
*/
|
|
100
|
+
type EnumResolver = (sourceId: string) => readonly string[] | Promise<readonly string[]>;
|
|
101
|
+
/**
|
|
102
|
+
* The legal values for every enum source that resolved successfully,
|
|
103
|
+
* keyed by source id.
|
|
104
|
+
*
|
|
105
|
+
* A source id absent from this map is *unresolved* — the field it backs falls
|
|
106
|
+
* back to a free-form string. Successful resolution always yields a non-empty
|
|
107
|
+
* array; an empty result is treated as a failure, not as "no legal values",
|
|
108
|
+
* because a field with zero legal values is unsatisfiable.
|
|
109
|
+
*/
|
|
110
|
+
type ResolvedEnums = Readonly<Record<string, readonly string[]>>;
|
|
111
|
+
|
|
112
|
+
type JsonSchema = Record<string, unknown>;
|
|
113
|
+
/**
|
|
114
|
+
* Which flavour of JSON Schema to emit.
|
|
115
|
+
*
|
|
116
|
+
* - `"openai-strict"` — the subset OpenAI structured outputs accepts.
|
|
117
|
+
* - `"standard"` — ordinary JSON Schema, for validators and other providers.
|
|
118
|
+
*/
|
|
119
|
+
type JsonSchemaDialect = "openai-strict" | "standard";
|
|
120
|
+
/**
|
|
121
|
+
* Options for JSON Schema generation.
|
|
122
|
+
*/
|
|
123
|
+
interface JsonSchemaOptions {
|
|
124
|
+
/** Legal values for dynamic enum sources, from `resolveEnumSources` */
|
|
125
|
+
resolvedEnums?: ResolvedEnums;
|
|
126
|
+
/** Target dialect. Defaults to `"openai-strict"`. */
|
|
127
|
+
dialect?: JsonSchemaDialect;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Convert a RuntimeSchema to a JSON Schema object.
|
|
131
|
+
*
|
|
132
|
+
* Under `"openai-strict"`, every property is listed in `required` and an
|
|
133
|
+
* optional field becomes `anyOf: [T, null]`, as structured outputs demand.
|
|
134
|
+
* Under `"standard"`, only genuinely required fields are listed and optional
|
|
135
|
+
* ones are simply absent — which is what keeps an unmentioned field
|
|
136
|
+
* distinguishable from one the model explicitly nulled.
|
|
137
|
+
*
|
|
138
|
+
* Both dialects inline nested schemas (no `$ref`) and set
|
|
139
|
+
* `additionalProperties: false`. Dynamic enum fields become a string `enum`
|
|
140
|
+
* when their source resolved, and a plain string otherwise. FieldConstraints
|
|
141
|
+
* are emitted only in `"standard"` — see {@link CONSTRAINT_KEYWORDS}.
|
|
142
|
+
*/
|
|
143
|
+
declare function runtimeSchemaToJsonSchema(schema: RuntimeSchema, bundle?: SchemaBundle, options?: JsonSchemaOptions): JsonSchema;
|
|
144
|
+
/**
|
|
145
|
+
* Wrap a RuntimeSchema as a top-level JSON Schema suitable for
|
|
146
|
+
* OpenAI's response_format.json_schema.schema parameter.
|
|
147
|
+
*/
|
|
148
|
+
declare function toOpenAIJsonSchema(schema: RuntimeSchema, bundle?: SchemaBundle, options?: Omit<JsonSchemaOptions, "dialect">): JsonSchema;
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* An enum source that could not be turned into a usable set of legal values.
|
|
152
|
+
*/
|
|
153
|
+
interface EnumSourceFailure {
|
|
154
|
+
/** The source id that failed */
|
|
155
|
+
sourceId: string;
|
|
156
|
+
/** Why it failed: the resolver threw, or it produced no values */
|
|
157
|
+
reason: "threw" | "empty";
|
|
158
|
+
/** The thrown value, when `reason` is "threw" */
|
|
159
|
+
cause?: unknown;
|
|
160
|
+
/**
|
|
161
|
+
* Whether a required field depends on this source. A field counts as
|
|
162
|
+
* required only if every object on the path to it is also required — an
|
|
163
|
+
* unreachable field cannot make a coercion fail.
|
|
164
|
+
*/
|
|
165
|
+
required: boolean;
|
|
166
|
+
/** Field paths that reference this source, for error messages */
|
|
167
|
+
paths: string[];
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* The outcome of resolving every enum source a schema reaches.
|
|
171
|
+
*/
|
|
172
|
+
interface EnumResolution {
|
|
173
|
+
/** Legal values for each source that resolved successfully */
|
|
174
|
+
enums: ResolvedEnums;
|
|
175
|
+
/** Sources that threw or produced nothing */
|
|
176
|
+
failures: EnumSourceFailure[];
|
|
177
|
+
}
|
|
178
|
+
/** How a single enum source is reached from the root schema. */
|
|
179
|
+
interface EnumSourceUsage {
|
|
180
|
+
/** Whether the source is reachable through an unbroken chain of required fields */
|
|
181
|
+
required: boolean;
|
|
182
|
+
/** Field paths that reference this source */
|
|
183
|
+
paths: string[];
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Collect the distinct dynamic enum source ids a schema reaches, directly or
|
|
187
|
+
* through its bundle, along with whether each is reachable through a chain of
|
|
188
|
+
* required fields.
|
|
189
|
+
*/
|
|
190
|
+
declare function collectEnumSources(schema: RuntimeSchema, bundle?: SchemaBundle): Map<string, EnumSourceUsage>;
|
|
191
|
+
/**
|
|
192
|
+
* Resolve every dynamic enum source a schema reaches, calling `resolver` once
|
|
193
|
+
* per distinct source id and awaiting all of them concurrently.
|
|
194
|
+
*
|
|
195
|
+
* Resolution never throws on the caller's behalf. A source whose resolver
|
|
196
|
+
* throws, or which yields no values, is reported in `failures` and left out of
|
|
197
|
+
* `enums` — downstream that means the field widens to a free-form string.
|
|
198
|
+
* Widening a *required* field is a silent correctness hole, so callers are
|
|
199
|
+
* expected to treat a failure with `required: true` as fatal; `coerce` and
|
|
200
|
+
* `partialCoerce` do exactly that.
|
|
201
|
+
*/
|
|
202
|
+
declare function resolveEnumSources(schema: RuntimeSchema, resolver: EnumResolver, bundle?: SchemaBundle): Promise<EnumResolution>;
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Registry for looking up compiled RuntimeSchemas by ID.
|
|
206
|
+
*/
|
|
207
|
+
declare class SchemaRegistry {
|
|
208
|
+
private schemas;
|
|
209
|
+
/**
|
|
210
|
+
* Register a single schema.
|
|
211
|
+
*/
|
|
212
|
+
register(schema: RuntimeSchema): void;
|
|
213
|
+
/**
|
|
214
|
+
* Register all schemas from a bundle.
|
|
215
|
+
*/
|
|
216
|
+
registerBundle(bundle: SchemaBundle): void;
|
|
217
|
+
/**
|
|
218
|
+
* Look up a schema by ID.
|
|
219
|
+
*/
|
|
220
|
+
get(id: string): RuntimeSchema | undefined;
|
|
221
|
+
/**
|
|
222
|
+
* Get a schema by ID, throwing if not found.
|
|
223
|
+
*/
|
|
224
|
+
require(id: string): RuntimeSchema;
|
|
225
|
+
/**
|
|
226
|
+
* Get a SchemaBundle of all registered schemas.
|
|
227
|
+
*/
|
|
228
|
+
toBundle(): SchemaBundle;
|
|
229
|
+
/**
|
|
230
|
+
* Get all registered schema IDs.
|
|
231
|
+
*/
|
|
232
|
+
ids(): string[];
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Class decorator marking a schema class with a semantic description.
|
|
237
|
+
* No-op at runtime — parsed by the compiler from source AST.
|
|
238
|
+
*/
|
|
239
|
+
declare function Schema(description: string): ClassDecorator;
|
|
240
|
+
/**
|
|
241
|
+
* Property decorator providing a field-level semantic description.
|
|
242
|
+
* No-op at runtime — parsed by the compiler from source AST.
|
|
243
|
+
*/
|
|
244
|
+
declare function Describe(description: string): PropertyDecorator;
|
|
245
|
+
/**
|
|
246
|
+
* Property decorator bounding a field's legal values — lengths, numeric
|
|
247
|
+
* ranges, array sizes, a pattern.
|
|
248
|
+
*
|
|
249
|
+
* Takes an object literal of compile-time constants; the compiler reads it
|
|
250
|
+
* from source, so computed expressions are not supported.
|
|
251
|
+
*
|
|
252
|
+
* ```ts
|
|
253
|
+
* @Describe("Display name for the listing.")
|
|
254
|
+
* @Constrain({ maxLength: 40 })
|
|
255
|
+
* name!: string;
|
|
256
|
+
* ```
|
|
257
|
+
*
|
|
258
|
+
* No-op at runtime — parsed by the compiler from source AST.
|
|
259
|
+
*/
|
|
260
|
+
declare function Constrain(constraints: FieldConstraints): PropertyDecorator;
|
|
261
|
+
/**
|
|
262
|
+
* Property decorator declaring that a field's legal values come from a named
|
|
263
|
+
* source resolved at coercion time rather than from the source tree — a CMS
|
|
264
|
+
* taxonomy, a database enum table.
|
|
265
|
+
*
|
|
266
|
+
* Applies to a string field or to the element type of a string array. The
|
|
267
|
+
* caller supplies an `EnumResolver` that maps `sourceId` to the legal values.
|
|
268
|
+
*
|
|
269
|
+
* ```ts
|
|
270
|
+
* @Describe("Amenities the property offers.")
|
|
271
|
+
* @ValuesFrom("amenities")
|
|
272
|
+
* amenities!: string[];
|
|
273
|
+
* ```
|
|
274
|
+
*
|
|
275
|
+
* No-op at runtime — parsed by the compiler from source AST.
|
|
276
|
+
*/
|
|
277
|
+
declare function ValuesFrom(sourceId: string): PropertyDecorator;
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* Recursively makes all properties optional, including nested objects.
|
|
281
|
+
*/
|
|
282
|
+
type DeepPartial<T> = {
|
|
283
|
+
[P in keyof T]?: T[P] extends (infer U)[] ? DeepPartial<U>[] : T[P] extends object ? DeepPartial<T[P]> : T[P];
|
|
284
|
+
};
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* Describes a single validation issue on a field.
|
|
288
|
+
*/
|
|
289
|
+
interface FieldValidationIssue {
|
|
290
|
+
/** Dot-separated path to the field, e.g. "address.city" */
|
|
291
|
+
path: string;
|
|
292
|
+
/** What went wrong */
|
|
293
|
+
message: string;
|
|
294
|
+
/** The value that was received, if any */
|
|
295
|
+
received?: unknown;
|
|
296
|
+
}
|
|
297
|
+
/**
|
|
298
|
+
* Error thrown when coercion validation fails.
|
|
299
|
+
*/
|
|
300
|
+
declare class CoerceError extends Error {
|
|
301
|
+
readonly issues: FieldValidationIssue[];
|
|
302
|
+
constructor(issues: FieldValidationIssue[]);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* Error thrown when an enum source backing a required field could not be
|
|
307
|
+
* resolved.
|
|
308
|
+
*
|
|
309
|
+
* Falling back to a free-form string here would let the model invent values
|
|
310
|
+
* that pass coercion and fail downstream, so a required field with a dead
|
|
311
|
+
* taxonomy is a hard failure rather than a widening.
|
|
312
|
+
*/
|
|
313
|
+
declare class EnumResolutionError extends Error {
|
|
314
|
+
readonly failures: EnumSourceFailure[];
|
|
315
|
+
constructor(failures: EnumSourceFailure[]);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* Configuration for a provider.
|
|
320
|
+
*/
|
|
321
|
+
interface ProviderConfig {
|
|
322
|
+
/** Model identifier, e.g. "gpt-4o" */
|
|
323
|
+
model: string;
|
|
324
|
+
/** Optional temperature override (0-2) */
|
|
325
|
+
temperature?: number;
|
|
326
|
+
/** Optional max tokens for the response */
|
|
327
|
+
maxTokens?: number;
|
|
328
|
+
}
|
|
329
|
+
/**
|
|
330
|
+
* Request sent to a provider for structured output.
|
|
331
|
+
*/
|
|
332
|
+
interface ProviderRequest {
|
|
333
|
+
/** System prompt with semantic context */
|
|
334
|
+
systemPrompt: string;
|
|
335
|
+
/** User input to coerce */
|
|
336
|
+
userInput: string;
|
|
337
|
+
/** JSON Schema for structured output */
|
|
338
|
+
jsonSchema: Record<string, unknown>;
|
|
339
|
+
/** The runtime schema being targeted */
|
|
340
|
+
schema: RuntimeSchema;
|
|
341
|
+
/**
|
|
342
|
+
* Bundle used to resolve nested schemas, when one was supplied.
|
|
343
|
+
*
|
|
344
|
+
* `jsonSchema` above is already built against this bundle in the
|
|
345
|
+
* OpenAI-strict dialect. Providers whose API wants a different dialect
|
|
346
|
+
* should re-derive from `schema` + `bundle` rather than reaching for
|
|
347
|
+
* `schema` alone — dropping the bundle silently emits nested objects with
|
|
348
|
+
* no properties.
|
|
349
|
+
*/
|
|
350
|
+
bundle?: SchemaBundle;
|
|
351
|
+
/**
|
|
352
|
+
* Legal values for the schema's `dynamicEnum` sources, already resolved.
|
|
353
|
+
* A provider that re-derives its own JSON Schema must pass these along —
|
|
354
|
+
* dropping them silently widens those fields back to free-form strings.
|
|
355
|
+
*/
|
|
356
|
+
resolvedEnums?: ResolvedEnums;
|
|
357
|
+
}
|
|
358
|
+
/**
|
|
359
|
+
* Token accounting for a single provider call.
|
|
360
|
+
*
|
|
361
|
+
* The cache fields are reported as the provider reports them, and providers
|
|
362
|
+
* disagree about whether cached tokens also appear in `promptTokens` — so read
|
|
363
|
+
* them as an effectiveness signal, not as terms to add up. Each provider's
|
|
364
|
+
* README states which convention it follows.
|
|
365
|
+
*/
|
|
366
|
+
interface ProviderUsage {
|
|
367
|
+
/** Input tokens, as the provider counts them. */
|
|
368
|
+
promptTokens: number;
|
|
369
|
+
/** Output tokens generated. */
|
|
370
|
+
completionTokens: number;
|
|
371
|
+
/** `promptTokens + completionTokens`. */
|
|
372
|
+
totalTokens: number;
|
|
373
|
+
/**
|
|
374
|
+
* Input tokens served from a prompt cache, when the provider reports it.
|
|
375
|
+
* Growing to cover the stable prefix across a batch is the signal that
|
|
376
|
+
* caching is working; a run of zeroes means it is not.
|
|
377
|
+
*/
|
|
378
|
+
cacheReadTokens?: number;
|
|
379
|
+
/**
|
|
380
|
+
* Input tokens written to a prompt cache, when the provider reports it.
|
|
381
|
+
* Expect this on the first call of a batch and near zero afterwards; a
|
|
382
|
+
* write on every call means the cached prefix is being invalidated.
|
|
383
|
+
*/
|
|
384
|
+
cacheWriteTokens?: number;
|
|
385
|
+
}
|
|
386
|
+
/**
|
|
387
|
+
* Response from a provider.
|
|
388
|
+
*/
|
|
389
|
+
interface ProviderResponse {
|
|
390
|
+
/** The parsed structured output */
|
|
391
|
+
data: Record<string, unknown>;
|
|
392
|
+
/** Raw response metadata for tracing */
|
|
393
|
+
usage?: ProviderUsage;
|
|
394
|
+
}
|
|
395
|
+
/**
|
|
396
|
+
* Interface that LLM providers must implement.
|
|
397
|
+
*/
|
|
398
|
+
interface Provider {
|
|
399
|
+
/**
|
|
400
|
+
* Send a structured output request to the LLM.
|
|
401
|
+
*/
|
|
402
|
+
complete(request: ProviderRequest): Promise<ProviderResponse>;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
/**
|
|
406
|
+
* A discrete event within a trace span.
|
|
407
|
+
*/
|
|
408
|
+
interface TraceEvent {
|
|
409
|
+
/** Event name */
|
|
410
|
+
name: string;
|
|
411
|
+
/** Timestamp */
|
|
412
|
+
timestamp: number;
|
|
413
|
+
/** Arbitrary attributes */
|
|
414
|
+
attributes?: Record<string, unknown>;
|
|
415
|
+
}
|
|
416
|
+
/**
|
|
417
|
+
* A span representing a unit of work in the coercion pipeline.
|
|
418
|
+
*/
|
|
419
|
+
interface TraceSpan {
|
|
420
|
+
/** Unique span ID */
|
|
421
|
+
id: string;
|
|
422
|
+
/** Human-readable name */
|
|
423
|
+
name: string;
|
|
424
|
+
/** Start timestamp */
|
|
425
|
+
startTime: number;
|
|
426
|
+
/** End timestamp (set when span completes) */
|
|
427
|
+
endTime?: number;
|
|
428
|
+
/** Events recorded during this span */
|
|
429
|
+
events: TraceEvent[];
|
|
430
|
+
/** Arbitrary attributes */
|
|
431
|
+
attributes?: Record<string, unknown>;
|
|
432
|
+
/** Parent span ID, if nested */
|
|
433
|
+
parentId?: string;
|
|
434
|
+
}
|
|
435
|
+
/**
|
|
436
|
+
* Sink that receives completed trace spans.
|
|
437
|
+
*/
|
|
438
|
+
interface TraceSink {
|
|
439
|
+
/** Called when a span is completed */
|
|
440
|
+
write(span: TraceSpan): void;
|
|
441
|
+
}
|
|
442
|
+
/**
|
|
443
|
+
* Context for tracing within a coercion call.
|
|
444
|
+
*/
|
|
445
|
+
interface TraceContext {
|
|
446
|
+
/** Start a new span */
|
|
447
|
+
startSpan(name: string, attributes?: Record<string, unknown>): TraceSpan;
|
|
448
|
+
/** End a span and dispatch to sinks */
|
|
449
|
+
endSpan(span: TraceSpan): void;
|
|
450
|
+
/** Add an event to the current span */
|
|
451
|
+
addEvent(span: TraceSpan, name: string, attributes?: Record<string, unknown>): void;
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
/**
|
|
455
|
+
* How well the input supported a value.
|
|
456
|
+
*
|
|
457
|
+
* A three-level scale rather than a number: models are poorly calibrated at
|
|
458
|
+
* producing a 0–1 score, and a review UI only ever needs to decide whether to
|
|
459
|
+
* flag a field for a human anyway.
|
|
460
|
+
*/
|
|
461
|
+
type FieldConfidence = "high" | "medium" | "low";
|
|
462
|
+
/** Where a coerced field's value came from. */
|
|
463
|
+
interface FieldProvenance {
|
|
464
|
+
/** How well the input supported this value. */
|
|
465
|
+
confidence: FieldConfidence;
|
|
466
|
+
/**
|
|
467
|
+
* The span of input the value was read from, quoted. Absent when the value
|
|
468
|
+
* was inferred rather than read — which is itself the signal worth showing.
|
|
469
|
+
*/
|
|
470
|
+
evidence?: string;
|
|
471
|
+
}
|
|
472
|
+
/** A coercion result paired with per-field provenance. */
|
|
473
|
+
interface ProvenanceResult<T> {
|
|
474
|
+
/** The coerced data, in the shape of the target schema. */
|
|
475
|
+
data: T;
|
|
476
|
+
/** Provenance for each top-level field the model returned, keyed by name. */
|
|
477
|
+
provenance: Record<string, FieldProvenance>;
|
|
478
|
+
}
|
|
479
|
+
/**
|
|
480
|
+
* Extra prompt guidance for a provenance run.
|
|
481
|
+
*
|
|
482
|
+
* The JSON Schema already forces the shape; what it cannot convey is how to
|
|
483
|
+
* judge confidence, which is the whole point of asking.
|
|
484
|
+
*/
|
|
485
|
+
declare const PROVENANCE_INSTRUCTIONS: string;
|
|
486
|
+
/**
|
|
487
|
+
* Derive the schema to actually request when provenance is wanted: the same
|
|
488
|
+
* fields, each wrapped in `{ value, confidence, evidence }`.
|
|
489
|
+
*
|
|
490
|
+
* Only top-level fields are annotated. A nested object keeps its ordinary
|
|
491
|
+
* shape inside `value`, so provenance is reported for the object as a whole
|
|
492
|
+
* rather than per leaf — annotating every leaf multiplies both the schema and
|
|
493
|
+
* the output for detail a review UI rarely acts on.
|
|
494
|
+
*
|
|
495
|
+
* Returns a bundle carrying the wrapper, the per-field annotation schemas, and
|
|
496
|
+
* everything the original bundle held, so nested types still inline.
|
|
497
|
+
*/
|
|
498
|
+
declare function toProvenanceSchema(schema: RuntimeSchema, bundle?: SchemaBundle): {
|
|
499
|
+
schema: RuntimeSchema;
|
|
500
|
+
bundle: SchemaBundle;
|
|
501
|
+
};
|
|
502
|
+
/**
|
|
503
|
+
* Split a provenance-shaped response back into plain data and per-field
|
|
504
|
+
* provenance.
|
|
505
|
+
*
|
|
506
|
+
* A field the model returned unwrapped — or wrapped without a usable
|
|
507
|
+
* `confidence` — still yields its value; the provenance is simply not
|
|
508
|
+
* recorded. Losing an annotation is not a reason to lose the extraction, and
|
|
509
|
+
* the missing key is visible to the caller.
|
|
510
|
+
*/
|
|
511
|
+
declare function splitProvenance(response: Record<string, unknown>, schema: RuntimeSchema): {
|
|
512
|
+
data: Record<string, unknown>;
|
|
513
|
+
provenance: Record<string, FieldProvenance>;
|
|
514
|
+
};
|
|
515
|
+
|
|
516
|
+
/**
|
|
517
|
+
* Options for coerce and partialCoerce.
|
|
518
|
+
*/
|
|
519
|
+
interface CoerceOptions {
|
|
520
|
+
/** The LLM provider to use */
|
|
521
|
+
provider: Provider;
|
|
522
|
+
/** The target schema */
|
|
523
|
+
schema: RuntimeSchema;
|
|
524
|
+
/** Optional bundle for resolving nested schemas */
|
|
525
|
+
bundle?: SchemaBundle;
|
|
526
|
+
/** Optional resolver for @ValuesFrom enum sources */
|
|
527
|
+
enumResolver?: EnumResolver;
|
|
528
|
+
/** Optional trace sinks */
|
|
529
|
+
traceSinks?: TraceSink[];
|
|
530
|
+
/**
|
|
531
|
+
* How many times to send validation failures back to the model for
|
|
532
|
+
* correction before giving up. Defaults to 0 — no repair.
|
|
533
|
+
*
|
|
534
|
+
* A repair costs an extra call only when validation actually failed, so the
|
|
535
|
+
* happy path is unaffected. It is off by default because it also multiplies
|
|
536
|
+
* worst-case latency, which a caller should opt into knowingly. For
|
|
537
|
+
* extraction from messy input — scraped HTML, third-party payloads — 1 is
|
|
538
|
+
* usually the right setting.
|
|
539
|
+
*/
|
|
540
|
+
maxRepairAttempts?: number;
|
|
541
|
+
}
|
|
542
|
+
/**
|
|
543
|
+
* Coerce user input into a fully validated instance of the target schema.
|
|
544
|
+
* Throws CoerceError if validation fails (required fields missing, type
|
|
545
|
+
* mismatches, constraint violations, values outside a resolved taxonomy).
|
|
546
|
+
* Throws EnumResolutionError if a required field's enum source cannot be resolved.
|
|
547
|
+
*/
|
|
548
|
+
declare function coerce<T>(input: string, options: CoerceOptions): Promise<T>;
|
|
549
|
+
/**
|
|
550
|
+
* Coerce user input into a partial instance of the target schema.
|
|
551
|
+
* Only validates types of fields that are present; never throws for missing fields.
|
|
552
|
+
* Throws CoerceError only if present fields have type mismatches or violate
|
|
553
|
+
* their constraints, and EnumResolutionError if a required field's enum source
|
|
554
|
+
* cannot be resolved.
|
|
555
|
+
*/
|
|
556
|
+
declare function partialCoerce<T>(input: string, options: CoerceOptions): Promise<Partial<T>>;
|
|
557
|
+
/**
|
|
558
|
+
* Like {@link coerce}, but each field also comes back with how well the input
|
|
559
|
+
* supported it and the text it was read from.
|
|
560
|
+
*
|
|
561
|
+
* Costs a larger schema and a longer response, so reach for it where a human
|
|
562
|
+
* reviews the result — a pre-filled form that should flag its guesses — rather
|
|
563
|
+
* than on a hot path.
|
|
564
|
+
*/
|
|
565
|
+
declare function coerceWithProvenance<T>(input: string, options: CoerceOptions): Promise<ProvenanceResult<T>>;
|
|
566
|
+
/**
|
|
567
|
+
* Like {@link partialCoerce}, but each field also comes back with how well the
|
|
568
|
+
* input supported it and the text it was read from.
|
|
569
|
+
*
|
|
570
|
+
* This is the one a form pre-fill usually wants: fields the input never
|
|
571
|
+
* mentioned are simply absent, and the ones that are present say how much to
|
|
572
|
+
* trust them.
|
|
573
|
+
*/
|
|
574
|
+
declare function partialCoerceWithProvenance<T>(input: string, options: CoerceOptions): Promise<ProvenanceResult<Partial<T>>>;
|
|
575
|
+
|
|
576
|
+
/**
|
|
577
|
+
* Build the input for a repair attempt: the original input, the output that
|
|
578
|
+
* was rejected, and what was wrong with it.
|
|
579
|
+
*
|
|
580
|
+
* The `Provider` interface is single-turn, so the correction has to travel as
|
|
581
|
+
* user text rather than as a real assistant turn. In practice that reads to
|
|
582
|
+
* the model the same way, and it keeps repair working on every provider
|
|
583
|
+
* without widening the provider contract. Exported so a caller who wants
|
|
584
|
+
* different wording can build their own and call the provider directly.
|
|
585
|
+
*/
|
|
586
|
+
declare function buildRepairInput(originalInput: string, rejected: Record<string, unknown>, issues: FieldValidationIssue[]): string;
|
|
587
|
+
|
|
588
|
+
/**
|
|
589
|
+
* Configuration options shared by global and per-call config.
|
|
590
|
+
*/
|
|
591
|
+
interface SemblGlobalConfig {
|
|
592
|
+
/** The LLM provider to use */
|
|
593
|
+
provider?: Provider;
|
|
594
|
+
/** Optional bundle for resolving nested schemas */
|
|
595
|
+
bundle?: SchemaBundle;
|
|
596
|
+
/** Optional resolver for @ValuesFrom enum sources */
|
|
597
|
+
enumResolver?: EnumResolver;
|
|
598
|
+
/** Optional trace sinks */
|
|
599
|
+
traceSinks?: TraceSink[];
|
|
600
|
+
/** How many times to send validation failures back for correction. Default 0. */
|
|
601
|
+
maxRepairAttempts?: number;
|
|
602
|
+
}
|
|
603
|
+
/**
|
|
604
|
+
* Per-call configuration overrides passed to `sembl()`.
|
|
605
|
+
*/
|
|
606
|
+
interface SemblCallConfig {
|
|
607
|
+
/** Override the LLM provider for this call */
|
|
608
|
+
provider?: Provider;
|
|
609
|
+
/** Override the bundle for this call */
|
|
610
|
+
bundle?: SchemaBundle;
|
|
611
|
+
/** Override the enum source resolver for this call */
|
|
612
|
+
enumResolver?: EnumResolver;
|
|
613
|
+
/** Override trace sinks for this call */
|
|
614
|
+
traceSinks?: TraceSink[];
|
|
615
|
+
/** Override the repair attempt budget for this call */
|
|
616
|
+
maxRepairAttempts?: number;
|
|
617
|
+
}
|
|
618
|
+
/**
|
|
619
|
+
* Resolved configuration with a guaranteed provider.
|
|
620
|
+
*/
|
|
621
|
+
interface ResolvedConfig {
|
|
622
|
+
provider: Provider;
|
|
623
|
+
bundle?: SchemaBundle;
|
|
624
|
+
enumResolver?: EnumResolver;
|
|
625
|
+
traceSinks?: TraceSink[];
|
|
626
|
+
maxRepairAttempts?: number;
|
|
627
|
+
}
|
|
628
|
+
/**
|
|
629
|
+
* Global configuration singleton for SEMBL.
|
|
630
|
+
*/
|
|
631
|
+
declare class SemblConfig {
|
|
632
|
+
private static _config;
|
|
633
|
+
/** Set global defaults. */
|
|
634
|
+
static configure(config: SemblGlobalConfig): void;
|
|
635
|
+
/** Reset global config to empty (useful in tests). */
|
|
636
|
+
static reset(): void;
|
|
637
|
+
/** Read-only access to the current global config. */
|
|
638
|
+
static get current(): Readonly<SemblGlobalConfig>;
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
/**
|
|
642
|
+
* A chainable, thenable wrapper around coercion results.
|
|
643
|
+
*
|
|
644
|
+
* Each `.coerceTo()` / `.partialCoerceTo()` call eagerly triggers an LLM call,
|
|
645
|
+
* serializing the previous result as the input string for the next step.
|
|
646
|
+
*
|
|
647
|
+
* Implements `PromiseLike<T>` so it can be `await`ed directly.
|
|
648
|
+
*
|
|
649
|
+
* There is no provenance variant here: an intermediate link's annotations
|
|
650
|
+
* would be serialized into the next call's input and lost, and a terminal one
|
|
651
|
+
* would have to return a different shape than every other link. Use
|
|
652
|
+
* `coerceWithProvenance` / `partialCoerceWithProvenance` directly instead.
|
|
653
|
+
*/
|
|
654
|
+
declare class Coercible<T> implements PromiseLike<T> {
|
|
655
|
+
private readonly _promise;
|
|
656
|
+
private readonly _config;
|
|
657
|
+
constructor(_promise: Promise<T>, _config: ResolvedConfig);
|
|
658
|
+
/** The per-call options every link in the chain shares. */
|
|
659
|
+
private _optionsFor;
|
|
660
|
+
/**
|
|
661
|
+
* Chain a full coercion to a new schema.
|
|
662
|
+
* The current value is serialized and used as input for the next LLM call.
|
|
663
|
+
*/
|
|
664
|
+
coerceTo<U>(schema: RuntimeSchema): Coercible<U>;
|
|
665
|
+
/**
|
|
666
|
+
* Chain a partial coercion to a new schema.
|
|
667
|
+
* The current value is serialized and used as input for the next LLM call.
|
|
668
|
+
*/
|
|
669
|
+
partialCoerceTo<U>(schema: RuntimeSchema): Coercible<Partial<U>>;
|
|
670
|
+
then<TResult1 = T, TResult2 = never>(onfulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | null, onrejected?: ((reason: unknown) => TResult2 | PromiseLike<TResult2>) | null): Promise<TResult1 | TResult2>;
|
|
671
|
+
catch<TResult = never>(onrejected?: ((reason: unknown) => TResult | PromiseLike<TResult>) | null): Promise<T | TResult>;
|
|
672
|
+
finally(onfinally?: (() => void) | null): Promise<T>;
|
|
673
|
+
}
|
|
674
|
+
/**
|
|
675
|
+
* Entry point for the fluent coercion API.
|
|
676
|
+
*
|
|
677
|
+
* Accepts a string or object as input. Objects are JSON-serialized.
|
|
678
|
+
* Returns a `Coercible<string>` that can be chained with `.coerceTo()` / `.partialCoerceTo()`.
|
|
679
|
+
*
|
|
680
|
+
* @example
|
|
681
|
+
* ```ts
|
|
682
|
+
* SemblConfig.configure({ provider, bundle });
|
|
683
|
+
* const result = await sembl("some user input")
|
|
684
|
+
* .partialCoerceTo(ProfileSchema)
|
|
685
|
+
* .coerceTo(IntentSchema);
|
|
686
|
+
* ```
|
|
687
|
+
*/
|
|
688
|
+
declare function sembl(input: string | Record<string, unknown>, config?: SemblCallConfig): Coercible<string>;
|
|
689
|
+
|
|
690
|
+
/**
|
|
691
|
+
* Options for prompt generation.
|
|
692
|
+
*/
|
|
693
|
+
interface PromptOptions {
|
|
694
|
+
/** Legal values for dynamic enum sources, from `resolveEnumSources` */
|
|
695
|
+
resolvedEnums?: ResolvedEnums;
|
|
696
|
+
}
|
|
697
|
+
/**
|
|
698
|
+
* Build a system prompt that provides semantic context for the target schema.
|
|
699
|
+
* This assembles the semantic hierarchy so the LLM understands the meaning
|
|
700
|
+
* of each field in context.
|
|
701
|
+
*/
|
|
702
|
+
declare function buildPrompt(schema: RuntimeSchema, bundle?: SchemaBundle, options?: PromptOptions): string;
|
|
703
|
+
|
|
704
|
+
/**
|
|
705
|
+
* Options shared by both validation modes.
|
|
706
|
+
*/
|
|
707
|
+
interface ValidationOptions {
|
|
708
|
+
/** Legal values for dynamic enum sources, from `resolveEnumSources` */
|
|
709
|
+
resolvedEnums?: ResolvedEnums;
|
|
710
|
+
}
|
|
711
|
+
/**
|
|
712
|
+
* Validate data against a RuntimeSchema in strict mode.
|
|
713
|
+
* All required fields must be present and correctly typed.
|
|
714
|
+
* Returns validation issues (empty array means valid).
|
|
715
|
+
*/
|
|
716
|
+
declare function validateStrict(data: Record<string, unknown>, schema: RuntimeSchema, bundle?: SchemaBundle, options?: ValidationOptions): FieldValidationIssue[];
|
|
717
|
+
/**
|
|
718
|
+
* Validate data against a RuntimeSchema in partial mode.
|
|
719
|
+
* Only validates types of fields that ARE present; never fails for missing fields.
|
|
720
|
+
* Returns validation issues (empty array means valid).
|
|
721
|
+
*/
|
|
722
|
+
declare function validatePartial(data: Record<string, unknown>, schema: RuntimeSchema, bundle?: SchemaBundle, options?: ValidationOptions): FieldValidationIssue[];
|
|
723
|
+
|
|
724
|
+
/**
|
|
725
|
+
* Tracer implementation that creates spans, records events, and dispatches to sinks.
|
|
726
|
+
*/
|
|
727
|
+
declare class Tracer implements TraceContext {
|
|
728
|
+
private sinks;
|
|
729
|
+
constructor(sinks?: TraceSink[]);
|
|
730
|
+
startSpan(name: string, attributes?: Record<string, unknown>, parent?: TraceSpan): TraceSpan;
|
|
731
|
+
endSpan(span: TraceSpan): void;
|
|
732
|
+
addEvent(span: TraceSpan, name: string, attributes?: Record<string, unknown>): void;
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
/**
|
|
736
|
+
* Default trace sink that writes spans to the console.
|
|
737
|
+
*/
|
|
738
|
+
declare class ConsoleSink implements TraceSink {
|
|
739
|
+
write(span: TraceSpan): void;
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
export { CoerceError, type CoerceOptions, Coercible, ConsoleSink, Constrain, type DeepPartial, Describe, type EnumResolution, EnumResolutionError, type EnumResolver, type EnumSourceFailure, type EnumSourceUsage, type FieldConfidence, type FieldConstraints, type FieldDescriptor, type FieldProvenance, type FieldType, type FieldValidationIssue, type JsonSchemaDialect, type JsonSchemaOptions, PROVENANCE_INSTRUCTIONS, type PromptOptions, type ProvenanceResult, type Provider, type ProviderConfig, type ProviderRequest, type ProviderResponse, type ResolvedEnums, type RuntimeSchema, Schema, type SchemaBundle, SchemaRegistry, type SemblCallConfig, SemblConfig, type SemblGlobalConfig, type TraceContext, type TraceEvent, type TraceSink, type TraceSpan, Tracer, type ValidationOptions, ValuesFrom, buildPrompt, buildRepairInput, coerce, coerceWithProvenance, collectEnumSources, partialCoerce, partialCoerceWithProvenance, resolveEnumSources, runtimeSchemaToJsonSchema, sembl, splitProvenance, toOpenAIJsonSchema, toProvenanceSchema, validatePartial, validateStrict };
|