@sembl/core 0.2.1 → 0.4.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/dist/index.d.ts CHANGED
@@ -1,3 +1,37 @@
1
+ /**
2
+ * Named string formats a field can be constrained to.
3
+ *
4
+ * Each one is validated locally, described in the prompt, and — where JSON
5
+ * Schema has a matching keyword — emitted in the schema dialects that honour
6
+ * it. They exist for the fields every pipeline ends up normalising by hand:
7
+ * a country that came back as "United States", a state as "Calif.", a
8
+ * currency as "dollars".
9
+ *
10
+ * - `"url"` — an absolute http(s) URL.
11
+ * - `"email"` — one address, no display name.
12
+ * - `"date"` — a calendar date as `YYYY-MM-DD`.
13
+ * - `"datetime"` — an ISO 8601 timestamp, e.g. `2026-09-05T14:30:00Z`.
14
+ * - `"iso-country"` — an ISO 3166-1 alpha-2 code: `US`, `DE`, `PT`.
15
+ * - `"us-state"` — a two-letter USPS state code: `CA`, `NY`, `DC`, `PR`.
16
+ * - `"us-state-name"` — a state's full name: `California`, `New York`.
17
+ * - `"currency"` — an ISO 4217 code: `USD`, `EUR`, `GBP`.
18
+ */
19
+ type FieldFormat = "url" | "email" | "date" | "datetime" | "iso-country" | "us-state" | "us-state-name" | "currency";
20
+ declare const FIELD_FORMATS: readonly FieldFormat[];
21
+ /**
22
+ * Check a string against a format. Returns a message describing what was
23
+ * expected when the value does not conform, undefined when it does.
24
+ */
25
+ declare function validateFormat(value: string, format: FieldFormat): string | undefined;
26
+ /** The phrase the prompt uses to state a format. */
27
+ declare function describeFormat(format: FieldFormat): string;
28
+ /**
29
+ * JSON Schema keywords for a format, in the standard dialect. The four with
30
+ * a JSON Schema `format` of their own use it; the code formats state their
31
+ * shape as a pattern, since a `format` a validator does not know is ignored.
32
+ */
33
+ declare function formatToJsonSchema(format: FieldFormat): Record<string, unknown>;
34
+
1
35
  /**
2
36
  * Bounds a field's value beyond its type.
3
37
  *
@@ -7,6 +41,8 @@
7
41
  * each element, so `string[]` can carry both `maxItems` and `maxLength`.
8
42
  */
9
43
  interface FieldConstraints {
44
+ /** A named string format: `"url"`, `"date"`, `"iso-country"`, `"currency"`, … */
45
+ format?: FieldFormat;
10
46
  /** Maximum string length, inclusive */
11
47
  maxLength?: number;
12
48
  /** Minimum string length, inclusive */
@@ -571,6 +607,12 @@ interface Source {
571
607
  label?: string;
572
608
  /** The text itself. */
573
609
  text: string;
610
+ /**
611
+ * A cap on this source's own characters, applied before the coercion's
612
+ * total `maxInputChars`, so one huge page cannot starve the others. Cut
613
+ * with the coercion's `truncate` policy.
614
+ */
615
+ maxChars?: number;
574
616
  }
575
617
  /**
576
618
  * What a coercion accepts as input: a plain string, one labelled source, or
@@ -637,7 +679,9 @@ interface BudgetResult {
637
679
  /**
638
680
  * Fit a set of sources into a character budget.
639
681
  *
640
- * The budget covers the sources' text as a whole. When they exceed it, it is
682
+ * A source's own `maxChars` is applied first, on its own, so a page known to
683
+ * be huge can be capped without starving the sources beside it. Then the
684
+ * total budget, when there is one, covers the sources' text as a whole. When they exceed it, it is
641
685
  * shared out so that every source that fits within an equal share keeps all
642
686
  * of its text, and what those leave unused goes to the longer ones. A short
643
687
  * email next to a long scraped page is therefore never touched; the page
@@ -645,7 +689,7 @@ interface BudgetResult {
645
689
  * so the model knows the text is incomplete rather than reading a
646
690
  * mid-sentence stop as the end.
647
691
  */
648
- declare function budgetSources(sources: readonly Source[], maxChars: number, policy?: TruncatePolicy): BudgetResult;
692
+ declare function budgetSources(sources: readonly Source[], maxChars: number | undefined, policy?: TruncatePolicy): BudgetResult;
649
693
 
650
694
  /**
651
695
  * What to do with a present field that fails validation.
@@ -750,6 +794,8 @@ interface ProvenanceResult<T> {
750
794
  * default `"throw"` policy, or when the response validated cleanly.
751
795
  */
752
796
  issues: ResolvedIssue[];
797
+ /** Token usage summed over every call the coercion made. */
798
+ usage: CoerceUsage;
753
799
  }
754
800
  /**
755
801
  * Extra prompt guidance for a provenance run.
@@ -765,6 +811,13 @@ interface ProvenanceOptions {
765
811
  * Each annotation then also asks which source the value was read from.
766
812
  */
767
813
  sourceLabels?: readonly string[];
814
+ /**
815
+ * Only these top-level fields are wrapped; every other field comes back
816
+ * as a plain value with no provenance. Halves the output on schemas where
817
+ * a human reviews a handful of fields and code checks the rest. All
818
+ * fields when absent.
819
+ */
820
+ fields?: readonly string[];
768
821
  }
769
822
  /**
770
823
  * The provenance guidance for a run, extended with the source rule when the
@@ -836,6 +889,33 @@ interface CoerceOptions {
836
889
  * event.
837
890
  */
838
891
  onInvalidField?: InvalidFieldPolicy;
892
+ /**
893
+ * With `coerceWithProvenance` / `partialCoerceWithProvenance`: annotate only
894
+ * these top-level fields. The rest come back plain, which roughly halves
895
+ * the output when a human reviews a few fields and code checks the others.
896
+ * Ignored by the plain coercions.
897
+ */
898
+ provenanceFields?: readonly string[];
899
+ /**
900
+ * How many times to ask again when a non-empty input yields no fields at
901
+ * all. Default 0. A model occasionally answers `{}` for a page it could
902
+ * read; the retry tells it so and asks for every stated value. Counts
903
+ * separately from `maxRepairAttempts`.
904
+ */
905
+ retryOnEmpty?: number;
906
+ /**
907
+ * Extra guidance for this extraction that is not part of the schema: facts
908
+ * about the source ("prices on this site are in cents"), context the model
909
+ * cannot see ("the property is in Portugal, so assume EUR"), or judgement
910
+ * calls ("guest counts exclude infants"). Rendered as its own section at
911
+ * the end of the system prompt, so it stays on the instruction side of the
912
+ * data boundary that source blocks are excluded from — a hint placed inside
913
+ * a source would be ignored by design.
914
+ *
915
+ * Reaches every call of the run, repairs included, and is part of what a
916
+ * recording is keyed on.
917
+ */
918
+ instructions?: string | readonly string[];
839
919
  /**
840
920
  * Cap on the total characters of source text sent to the model, applied
841
921
  * after `preprocess`. Sources over the cap are cut per `truncate`, each
@@ -856,6 +936,27 @@ interface CoerceOptions {
856
936
  }
857
937
  /** A hook applied to each source before it is budgeted and rendered. */
858
938
  type PreprocessSource = (source: Source, index: number) => Source | string | Promise<Source | string>;
939
+ /**
940
+ * Token accounting for a whole coercion, summed over every provider call it
941
+ * made — the first attempt, repairs, and empty-result retries.
942
+ */
943
+ interface CoerceUsage {
944
+ /** Provider calls made. */
945
+ calls: number;
946
+ promptTokens: number;
947
+ completionTokens: number;
948
+ totalTokens: number;
949
+ cacheReadTokens: number;
950
+ cacheWriteTokens: number;
951
+ }
952
+ /** The result of a detailed coercion: the data plus what it cost to get it. */
953
+ interface CoerceDetails<T> {
954
+ data: T;
955
+ /** Issues the `onInvalidField` policy absorbed. Empty under `"throw"`. */
956
+ issues: ResolvedIssue[];
957
+ /** Token usage summed over every call the coercion made. */
958
+ usage: CoerceUsage;
959
+ }
859
960
  /**
860
961
  * Coerce user input into a fully validated instance of the target schema.
861
962
  * Throws CoerceError if validation fails (required fields missing, type
@@ -871,6 +972,44 @@ declare function coerce<T>(input: CoerceInput, options: CoerceOptions): Promise<
871
972
  * cannot be resolved.
872
973
  */
873
974
  declare function partialCoerce<T>(input: CoerceInput, options: CoerceOptions): Promise<Partial<T>>;
975
+ /**
976
+ * Like {@link coerce}, but also returns the issues the `onInvalidField` policy
977
+ * absorbed and the token usage of every call made — without the cost of
978
+ * provenance. The one to use when a pipeline accounts for spend or shows
979
+ * dropped fields but never needs per-field confidence.
980
+ */
981
+ declare function coerceDetailed<T>(input: CoerceInput, options: CoerceOptions): Promise<CoerceDetails<T>>;
982
+ /** Like {@link partialCoerce}, with the issues and usage of {@link coerceDetailed}. */
983
+ declare function partialCoerceDetailed<T>(input: CoerceInput, options: CoerceOptions): Promise<CoerceDetails<Partial<T>>>;
984
+ /** What {@link primeCache} produced: a record that the prefix was sent once. */
985
+ interface PrimedPrefix {
986
+ schemaId: string;
987
+ mode: "coerce" | "partialCoerce";
988
+ provenance: boolean;
989
+ /** Usage of the warm-up call. `cacheWriteTokens` shows the prefix landed. */
990
+ usage: CoerceUsage;
991
+ primedAt: string;
992
+ }
993
+ /** Options for {@link primeCache}: the coercion options the batch will use. */
994
+ interface PrimeCacheOptions extends CoerceOptions {
995
+ mode?: "coerce" | "partialCoerce";
996
+ provenance?: boolean;
997
+ }
998
+ /**
999
+ * Send the stable prefix — system prompt and schema — once, ahead of a batch,
1000
+ * so a provider that caches it writes the cache before the batch starts.
1001
+ *
1002
+ * Meant to overlap the caller's own preparation: start it while fetching
1003
+ * pages, then pass the promise to `coerceMany` as `primed`. The batch then
1004
+ * fans out at once instead of running its first item alone. The warm-up
1005
+ * costs one call with a trivial input and a near-empty answer; the answer is
1006
+ * not validated and never returned.
1007
+ *
1008
+ * Only the prefix matters, so the same options the batch will use must be
1009
+ * passed — a different schema, mode, provenance setting or instructions is
1010
+ * a different prefix and warms nothing.
1011
+ */
1012
+ declare function primeCache(options: PrimeCacheOptions): Promise<PrimedPrefix>;
874
1013
  /**
875
1014
  * Like {@link coerce}, but each field also comes back with how well the input
876
1015
  * supported it and the text it was read from.
@@ -890,25 +1029,44 @@ declare function coerceWithProvenance<T>(input: CoerceInput, options: CoerceOpti
890
1029
  */
891
1030
  declare function partialCoerceWithProvenance<T>(input: CoerceInput, options: CoerceOptions): Promise<ProvenanceResult<Partial<T>>>;
892
1031
 
1032
+ /** What a batch accepts: an array, or anything that can be iterated, lazily or not. */
1033
+ type CoerceManyInputs = Iterable<CoerceInput> | AsyncIterable<CoerceInput>;
893
1034
  /** Options for {@link coerceMany}. Everything in `CoerceOptions` applies to each item. */
894
- interface CoerceManyOptions extends CoerceOptions {
1035
+ interface CoerceManyOptions<T = unknown> extends CoerceOptions {
895
1036
  /** How many items may be in flight at once. Default 4. */
896
1037
  concurrency?: number;
897
1038
  /** Which coercion to run per item. Default `"coerce"`. */
898
1039
  mode?: "coerce" | "partialCoerce";
899
- /** Ask for per-field provenance on every item. Default false. */
900
- provenance?: boolean;
901
1040
  /**
902
- * Run the first item alone before fanning out, so a provider that caches
903
- * the prompt prefix writes it once and every later item reads it. Costs
904
- * one item's latency up front; saves a cache write per concurrent worker.
905
- * Default true.
1041
+ * Ask for provenance on every item: `true` for every field, or the names
1042
+ * of the top-level fields to annotate. Default false.
1043
+ */
1044
+ provenance?: boolean | readonly string[];
1045
+ /**
1046
+ * How to warm a provider's prompt cache before fanning out, so the stable
1047
+ * prefix is written once and every item reads it.
1048
+ *
1049
+ * - `true` (default): run the first item alone, then fan out. No extra
1050
+ * call, but the batch waits for one full item.
1051
+ * - `"eager"`: send a warm-up call the moment the batch starts and fan out
1052
+ * as soon as it lands. One extra small call; no item waits on another.
1053
+ * The right choice when inputs stream in from an async iterable.
1054
+ * - `false`: fan out immediately.
1055
+ *
1056
+ * Ignored when `primed` is given.
1057
+ */
1058
+ primeCache?: boolean | "eager";
1059
+ /**
1060
+ * A prefix already warmed with {@link primeCache}, or the promise of one.
1061
+ * Start it while fetching inputs and hand it over here: the batch waits
1062
+ * for it (not for an item) and then fans out. A warm-up that fails is
1063
+ * traced and otherwise ignored — the batch runs, only colder.
906
1064
  */
907
- primeCache?: boolean;
1065
+ primed?: PrimedPrefix | Promise<PrimedPrefix>;
908
1066
  /** How the batch backs off when the provider pushes back. */
909
1067
  retry?: RetryOptions;
910
1068
  /** Called as each item settles, in completion order, for progress. */
911
- onItem?: (result: CoerceManyResult<unknown>) => void;
1069
+ onItem?: (result: CoerceManyResult<T>) => void;
912
1070
  /** Stop starting new items; those not yet started fail with the reason. */
913
1071
  signal?: AbortSignal;
914
1072
  }
@@ -930,7 +1088,7 @@ interface RetryOptions {
930
1088
  /** Longest pause, in milliseconds. Default 30000. */
931
1089
  maxDelayMs?: number;
932
1090
  }
933
- /** One item's outcome. `index` is its position in the input list. */
1091
+ /** One item's outcome. `index` is its position in the input sequence. */
934
1092
  type CoerceManyResult<T> = {
935
1093
  ok: true;
936
1094
  index: number;
@@ -939,12 +1097,16 @@ type CoerceManyResult<T> = {
939
1097
  provenance: Record<string, FieldProvenance>;
940
1098
  /** Issues the `onInvalidField` policy absorbed for this item. */
941
1099
  issues: ResolvedIssue[];
942
- /** How many provider calls it took, repairs excluded. */
1100
+ /** Token usage over every call this item made, repairs included. */
1101
+ usage: CoerceUsage;
1102
+ /** How many times the item was started, retries included. */
943
1103
  attempts: number;
944
1104
  } | {
945
1105
  ok: false;
946
1106
  index: number;
947
1107
  error: unknown;
1108
+ /** Usage of the calls made before giving up. */
1109
+ usage: CoerceUsage;
948
1110
  attempts: number;
949
1111
  };
950
1112
  /**
@@ -955,8 +1117,13 @@ type CoerceManyResult<T> = {
955
1117
  * its own, so one bad listing cannot take down an import. Retryable provider
956
1118
  * errors pause the whole batch and try the item again; anything else, a
957
1119
  * `CoerceError` included, is that item's final answer.
1120
+ *
1121
+ * Inputs may be an array or any iterable, including an async one, so a
1122
+ * batch can start while its inputs are still being fetched. Every span an
1123
+ * item emits carries `itemIndex` (and `itemLabel` when the input is
1124
+ * labelled), so a trace sink can attribute usage under concurrency.
958
1125
  */
959
- declare function coerceMany<T>(inputs: readonly CoerceInput[], options: CoerceManyOptions): Promise<CoerceManyResult<T>[]>;
1126
+ declare function coerceMany<T>(inputs: CoerceManyInputs, options: CoerceManyOptions<T>): Promise<CoerceManyResult<T>[]>;
960
1127
 
961
1128
  /**
962
1129
  * Build the input for a repair attempt: the original input (already rendered
@@ -988,6 +1155,10 @@ interface SemblGlobalConfig {
988
1155
  maxRepairAttempts?: number;
989
1156
  /** What to do with a present field that fails validation. Default "throw". */
990
1157
  onInvalidField?: InvalidFieldPolicy;
1158
+ /** Extra guidance rendered into every system prompt. */
1159
+ instructions?: string | readonly string[];
1160
+ /** Retries when a non-empty input yields no fields. Default 0. */
1161
+ retryOnEmpty?: number;
991
1162
  /** Cap on total source characters sent to the model. Unbounded by default. */
992
1163
  maxInputChars?: number;
993
1164
  /** Which part of an over-budget source to cut. Default "tail". */
@@ -1011,6 +1182,10 @@ interface SemblCallConfig {
1011
1182
  maxRepairAttempts?: number;
1012
1183
  /** Override the invalid-field policy for this call */
1013
1184
  onInvalidField?: InvalidFieldPolicy;
1185
+ /** Guidance for this call. Replaces, rather than extends, the global list. */
1186
+ instructions?: string | readonly string[];
1187
+ /** Override the empty-result retry budget for this call */
1188
+ retryOnEmpty?: number;
1014
1189
  /** Override the input character budget for this call */
1015
1190
  maxInputChars?: number;
1016
1191
  /** Override the truncation policy for this call */
@@ -1028,6 +1203,8 @@ interface ResolvedConfig {
1028
1203
  traceSinks?: TraceSink[];
1029
1204
  maxRepairAttempts?: number;
1030
1205
  onInvalidField?: InvalidFieldPolicy;
1206
+ instructions?: string | readonly string[];
1207
+ retryOnEmpty?: number;
1031
1208
  maxInputChars?: number;
1032
1209
  truncate?: TruncatePolicy;
1033
1210
  preprocess?: PreprocessSource;
@@ -1116,7 +1293,18 @@ declare function sembl(input: CoerceInput | Record<string, unknown>, config?: Se
1116
1293
  interface PromptOptions {
1117
1294
  /** Legal values for dynamic enum sources, from `resolveEnumSources` */
1118
1295
  resolvedEnums?: ResolvedEnums;
1296
+ /**
1297
+ * Caller-supplied guidance for this extraction, rendered as its own section
1298
+ * of the system prompt. Blank entries are dropped.
1299
+ */
1300
+ instructions?: string | readonly string[];
1119
1301
  }
1302
+ /**
1303
+ * Normalise the `instructions` option to a list of non-empty lines. Throws
1304
+ * for anything that is not a string or a list of strings, since a hint that
1305
+ * silently rendered as "[object Object]" would be worse than none.
1306
+ */
1307
+ declare function normalizeInstructions(instructions: string | readonly string[] | undefined): string[];
1120
1308
  /**
1121
1309
  * Build a system prompt that provides semantic context for the target schema.
1122
1310
  * This assembles the semantic hierarchy so the LLM understands the meaning
@@ -1149,7 +1337,13 @@ declare function validatePartial(data: Record<string, unknown>, schema: RuntimeS
1149
1337
  */
1150
1338
  declare class Tracer implements TraceContext {
1151
1339
  private sinks;
1152
- constructor(sinks?: TraceSink[]);
1340
+ private readonly baseAttributes;
1341
+ /**
1342
+ * `baseAttributes` are merged into every span this tracer opens — how a
1343
+ * batch stamps `itemIndex` on the spans of each item, so a sink can tell
1344
+ * whose `llmCall` it is looking at under concurrency.
1345
+ */
1346
+ constructor(sinks?: TraceSink[], baseAttributes?: Record<string, unknown>);
1153
1347
  startSpan(name: string, attributes?: Record<string, unknown>, parent?: TraceSpan): TraceSpan;
1154
1348
  endSpan(span: TraceSpan): void;
1155
1349
  addEvent(span: TraceSpan, name: string, attributes?: Record<string, unknown>): void;
@@ -1162,4 +1356,4 @@ declare class ConsoleSink implements TraceSink {
1162
1356
  write(span: TraceSpan): void;
1163
1357
  }
1164
1358
 
1165
- export { type BudgetResult, CoerceError, type CoerceInput, type CoerceManyOptions, type CoerceManyResult, type CoerceOptions, Coercible, ConsoleSink, Constrain, type DeepPartial, type DefinedSchema, Describe, type EnumResolution, EnumResolutionError, type EnumResolver, type EnumSourceFailure, type EnumSourceUsage, type FieldBuilder, type FieldConfidence, type FieldConstraints, type FieldDescriptor, type FieldProvenance, type FieldType, type FieldValidationIssue, type Infer, type InferFields, type InvalidFieldPolicy, type IssueResolution, type JsonSchemaDialect, type JsonSchemaOptions, PROVENANCE_INSTRUCTIONS, type PreprocessSource, type PromptOptions, type ProvenanceOptions, type ProvenanceResult, type Provider, type ProviderConfig, type ProviderRequest, type ProviderResponse, type ProviderUsage, type ResolveIssuesOptions, type ResolveIssuesResult, type ResolvedEnums, type ResolvedIssue, type RetryOptions, type RuntimeSchema, SOURCE_INSTRUCTIONS, Schema, type SchemaBundle, SchemaRegistry, type SemblCallConfig, SemblConfig, type SemblGlobalConfig, type Source, type TraceContext, type TraceEvent, type TraceSink, type TraceSpan, Tracer, type TruncatePolicy, type TruncationRecord, type ValidationOptions, ValuesFrom, budgetSources, buildPrompt, buildRepairInput, bundleOf, coerce, coerceMany, coerceWithProvenance, collectEnumSources, defineSchema, field, isCoerceInput, isSource, partialCoerce, partialCoerceWithProvenance, provenanceInstructions, renderSources, resolveEnumSources, resolveIssues, runtimeSchemaToJsonSchema, sembl, splitProvenance, toOpenAIJsonSchema, toProvenanceSchema, toSources, validatePartial, validateStrict };
1359
+ export { type BudgetResult, type CoerceDetails, CoerceError, type CoerceInput, type CoerceManyOptions, type CoerceManyResult, type CoerceOptions, type CoerceUsage, Coercible, ConsoleSink, Constrain, type DeepPartial, type DefinedSchema, Describe, type EnumResolution, EnumResolutionError, type EnumResolver, type EnumSourceFailure, type EnumSourceUsage, FIELD_FORMATS, type FieldBuilder, type FieldConfidence, type FieldConstraints, type FieldDescriptor, type FieldFormat, type FieldProvenance, type FieldType, type FieldValidationIssue, type Infer, type InferFields, type InvalidFieldPolicy, type IssueResolution, type JsonSchemaDialect, type JsonSchemaOptions, PROVENANCE_INSTRUCTIONS, type PreprocessSource, type PrimeCacheOptions, type PrimedPrefix, type PromptOptions, type ProvenanceOptions, type ProvenanceResult, type Provider, type ProviderConfig, type ProviderRequest, type ProviderResponse, type ProviderUsage, type ResolveIssuesOptions, type ResolveIssuesResult, type ResolvedEnums, type ResolvedIssue, type RetryOptions, type RuntimeSchema, SOURCE_INSTRUCTIONS, Schema, type SchemaBundle, SchemaRegistry, type SemblCallConfig, SemblConfig, type SemblGlobalConfig, type Source, type TraceContext, type TraceEvent, type TraceSink, type TraceSpan, Tracer, type TruncatePolicy, type TruncationRecord, type ValidationOptions, ValuesFrom, budgetSources, buildPrompt, buildRepairInput, bundleOf, coerce, coerceDetailed, coerceMany, coerceWithProvenance, collectEnumSources, defineSchema, describeFormat, field, formatToJsonSchema, isCoerceInput, isSource, normalizeInstructions, partialCoerce, partialCoerceDetailed, partialCoerceWithProvenance, primeCache, provenanceInstructions, renderSources, resolveEnumSources, resolveIssues, runtimeSchemaToJsonSchema, sembl, splitProvenance, toOpenAIJsonSchema, toProvenanceSchema, toSources, validateFormat, validatePartial, validateStrict };