llmbic 1.1.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -5,6 +5,40 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [1.3.0] - 2026-04-18
9
+
10
+ Non-breaking. Rules can now read a caller-provided `context` object alongside `content`, so per-call metadata (locale, tenant configuration, feature flags) no longer has to be captured in rule closures at declaration time.
11
+
12
+ ### Added
13
+
14
+ - `ExtractionRule<TContext = unknown>` - optional generic type parameter describing the shape of a per-call context forwarded to `extract`. Defaults to `unknown`, so context-unaware rules and legacy code compile unchanged.
15
+ - `ExtractionRule.extract(content, context?)` - second optional argument, forwarded verbatim by `rule.apply` / `Extractor.extract` / `Extractor.extractSync`. Left `undefined` when the caller passes no context.
16
+ - `rule.create<TContext>(field, extract, options?)` - `create` is now generic over `TContext`, so typed contexts flow from the callback signature to the returned `ExtractionRule<TContext>`. `rule.regex` stays context-unaware and remains assignable to any `ExtractionRule<TContext>[]` via contextual parameter contravariance.
17
+ - `rule.apply<S, TContext>(content, rules, schema, logger?, context?)` - fifth optional argument passed through to every rule's `extract` callback.
18
+ - `ExtractorConfig<S, TContext = unknown>` and `Extractor<T, TContext = unknown>` - second optional generic parameter shared with the rules array. `Extractor.extract(content, context?)` and `Extractor.extractSync(content, context?)` forward `context` to `rule.apply`. `Extractor.merge` does not re-evaluate rules and accepts no `context`.
19
+
20
+ ### Docs
21
+
22
+ - `CONTRIBUTING.md` - documents the 5-step release procedure (tests, bump, doc, tag, publish) to keep future releases aligned with the SemVer + npm publish lifecycle.
23
+
24
+ ## [1.2.0] - 2026-04-16
25
+
26
+ Non-breaking. Production-readiness pass: per-field provenance, per-field merge policy, and pre/post LLM transformers. Token / cost tracking deliberately stays out of scope - `LlmProvider` keeps observability as a caller concern; wrap your `complete` for telemetry.
27
+
28
+ ### Added
29
+
30
+ - `ExtractionResult.sources` - per-field origin of the kept value, as a `FieldSource` discriminated union (`'rule' | 'llm' | 'agreement' | 'flag'`). Variants involving a rule carry the `ruleId` of the rule that produced the match. Use it to attribute extractions back to specific rules, monitor rule quality at scale, or filter results on agreement vs LLM-only fields.
31
+ - `ExtractionRule.id` and `rule.create` / `rule.regex` `options.id` - declare a stable identifier surfaced in `ExtractionResult.sources`. When omitted, `rule.apply` auto-generates `${field}#${declarationIndex}` based on the rule's position in the array.
32
+ - `MergeApplyOptions.policyByField` and `ExtractorConfig.policyByField` - per-field overrides of `FieldMergePolicy` (strategy, confidences, compare). Precedence: defaults < `policy` < `policyByField[field]`. TypeScript validates field names against the schema. Lets a single extractor flag conflicts on critical fields, prefer rules on parser-friendly fields, and prefer the LLM on free-form fields without writing custom merge code.
33
+ - `ExtractorLlmConfig.transformRequest` / `transformResponse` - async hooks called around `provider.complete`. `transformRequest` rewrites the built `LlmRequest` (PII redaction, locale tagging); `transformResponse` rewrites the parsed `LlmResult` before the merge step (PII restoration, post-processing). Errors propagate, no implicit catch.
34
+ - `examples/pii-redaction.ts` - runnable, offline demo of the redact-then-restore pattern using `transformRequest` + `transformResponse` (also wired as `npm run example:pii-redaction`).
35
+
36
+ ### Public types
37
+
38
+ - `FieldSource` exported from the package root.
39
+ - `RulesResult.sourceIds` (optional) - populated by `rule.apply`, consumed by `merge.apply` to compute `ExtractionResult.sources`. External callers building `RulesResult` by hand can omit it; provenance simply falls back to an empty `ruleId`.
40
+ - `ExtractionRule` gains optional `id`. `RuleMatch`, `FieldMergeResult` and `merge.field`'s signature are unchanged - provenance is computed from the merge outcome plus the policy, not stored on the per-field primitive.
41
+
8
42
  ## [1.1.0] - 2026-04-16
9
43
 
10
44
  Non-breaking. Unblocks hybrid workflows that rely on nested schemas, agreement/conflict detection, and extractor-level merge options.
@@ -28,18 +62,18 @@ Non-breaking. Unblocks hybrid workflows that rely on nested schemas, agreement/c
28
62
  - `ExtractorConfig<S>` gains optional `normalizers`, `validators`, `policy`, `logger`.
29
63
  - `ExtractorLlmConfig` gains optional `mode`, `crossCheckHints`.
30
64
 
31
- ## [1.0.0] 2026-04-15
65
+ ## [1.0.0] - 2026-04-15
32
66
 
33
67
  Initial public release.
34
68
 
35
69
  ### Added
36
70
 
37
- - `createExtractor(config)` factory binding a Zod schema, deterministic rules and an optional LLM fallback into an extractor with `extract`, `extractSync`, `prompt`, `parse` and `merge` methods. Covers both one-shot async extraction and 4-step batch flows (extractSync prompt external LLM call parse merge).
38
- - `rule` namespace `rule.create(field, extractFn)`, `rule.regex(field, pattern, score, transform?)`, `rule.confidence(value, score)`, `rule.apply(content, rules, schema, logger?)`. Deterministic rules are pure synchronous functions returning typed matches with a confidence score in `[0, 1]`.
39
- - `merge` namespace `merge.apply(schema, rulesResult, llmResult, content, options?)` fuses rules output with LLM output, detects per-field conflicts, runs normalizers, re-validates against the Zod schema, and runs custom validators. `merge.defaultFieldPolicy` exposes the built-in fusion rules.
40
- - `prompt` namespace `prompt.build(schema, partial, options?)` emits an `LlmRequest` (`systemPrompt`, `userContent`, `responseSchema`, `knownValues`) restricted to fields missing from the deterministic pass. `prompt.parse(raw, missing, schema)` is a permissive parser that validates each field individually via Zod, drops invalid or unexpected keys, and never throws.
41
- - `validator` namespace `validator.of<T>()` returns `{ field, crossField }` factories bound to the data shape `T`, so predicates are fully typed from the field name.
42
- - `LlmProvider` contract single-method interface (`complete(request) { values }`) consumers implement to wire any backend (OpenAI, Anthropic, Ollama, custom HTTP, ...). No vendor SDK is pulled into the import graph.
71
+ - `createExtractor(config)` - factory binding a Zod schema, deterministic rules and an optional LLM fallback into an extractor with `extract`, `extractSync`, `prompt`, `parse` and `merge` methods. Covers both one-shot async extraction and 4-step batch flows (extractSync -> prompt -> external LLM call -> parse -> merge).
72
+ - `rule` namespace - `rule.create(field, extractFn)`, `rule.regex(field, pattern, score, transform?)`, `rule.confidence(value, score)`, `rule.apply(content, rules, schema, logger?)`. Deterministic rules are pure synchronous functions returning typed matches with a confidence score in `[0, 1]`.
73
+ - `merge` namespace - `merge.apply(schema, rulesResult, llmResult, content, options?)` fuses rules output with LLM output, detects per-field conflicts, runs normalizers, re-validates against the Zod schema, and runs custom validators. `merge.defaultFieldPolicy` exposes the built-in fusion rules.
74
+ - `prompt` namespace - `prompt.build(schema, partial, options?)` emits an `LlmRequest` (`systemPrompt`, `userContent`, `responseSchema`, `knownValues`) restricted to fields missing from the deterministic pass. `prompt.parse(raw, missing, schema)` is a permissive parser that validates each field individually via Zod, drops invalid or unexpected keys, and never throws.
75
+ - `validator` namespace - `validator.of<T>()` returns `{ field, crossField }` factories bound to the data shape `T`, so predicates are fully typed from the field name.
76
+ - `LlmProvider` contract - single-method interface (`complete(request) -> { values }`) consumers implement to wire any backend (OpenAI, Anthropic, Ollama, custom HTTP, ...). No vendor SDK is pulled into the import graph.
43
77
  - Per-field confidence scoring, conflict detection (`flag` / `prefer-rule` / `prefer-llm` strategies), and extraction metadata (`durationMs`, rule/LLM field counts).
44
78
  - Full TypeScript `.d.ts` output with JSDoc on every public type, method and configuration field.
45
79
  - Example wiring a local Ollama runtime under `examples/ollama.ts`.
package/README.md CHANGED
@@ -5,13 +5,13 @@
5
5
  [![license](https://img.shields.io/npm/l/llmbic.svg)](./LICENSE)
6
6
  [![node](https://img.shields.io/node/v/llmbic.svg)](https://nodejs.org)
7
7
 
8
- Hybrid data extraction deterministic rules + LLM fallback, with per-field confidence scoring.
8
+ Hybrid data extraction - deterministic rules + LLM fallback, with per-field confidence scoring.
9
9
 
10
10
  The name folds **LLM** into [*lambic*](https://en.wikipedia.org/wiki/Lambic), the Belgian beer made by blending wild fermentation with a controlled process. Same idea here: LLMs are unpredictable, rules are rigid, and the mix produces something reliable.
11
11
 
12
12
  ## Why
13
13
 
14
- Extracting structured data from unstructured text is a solved problem until you need it to be *reliable*. Rules (regex, parsers) are deterministic but brittle. LLMs understand context but hallucinate. Neither is enough alone.
14
+ Extracting structured data from unstructured text is a solved problem - until you need it to be *reliable*. Rules (regex, parsers) are deterministic but brittle. LLMs understand context but hallucinate. Neither is enough alone.
15
15
 
16
16
  Llmbic combines both: deterministic rules extract what they can with full confidence, the LLM fills in the gaps, and a merge layer detects conflicts between the two. Every field carries a confidence score. You know exactly what's trustworthy and what needs review.
17
17
 
@@ -21,7 +21,7 @@ Llmbic combines both: deterministic rules extract what they can with full confid
21
21
  npm install llmbic
22
22
  ```
23
23
 
24
- Llmbic has a single dependency: [Zod](https://zod.dev). No vendor SDK is pulled in you bring your own LLM provider via the 1-method `LlmProvider` interface (see "Writing a provider" below).
24
+ Llmbic has a single dependency: [Zod](https://zod.dev). No vendor SDK is pulled in - you bring your own LLM provider via the 1-method `LlmProvider` interface (see "Writing a provider" below).
25
25
 
26
26
  ## Quick start
27
27
 
@@ -111,7 +111,7 @@ console.log(result.confidence);
111
111
  // { total: 1.0, currency: 1.0, vendor: 0.7, date: 0.7 }
112
112
 
113
113
  console.log(result.conflicts);
114
- // [] no disagreement between rules and LLM
114
+ // [] - no disagreement between rules and LLM
115
115
  ```
116
116
 
117
117
  ### Batch / async mode (for OpenAI Batch API, job queues, etc.)
@@ -119,19 +119,19 @@ console.log(result.conflicts);
119
119
  When you manage the LLM call yourself (batching, polling, custom transport), use the 4-step API:
120
120
 
121
121
  ```typescript
122
- // Step 1 Deterministic extraction (sync, instant)
122
+ // Step 1 - Deterministic extraction (sync, instant)
123
123
  const partial = extractor.extractSync(markdown);
124
124
 
125
- // Step 2 Build the LLM request (you send it however you want)
125
+ // Step 2 - Build the LLM request (you send it however you want)
126
126
  const llmRequest = extractor.prompt(markdown, partial);
127
- // { systemPrompt, userContent, responseSchema, knownValues }
127
+ // -> { systemPrompt, userContent, responseSchema, knownValues }
128
128
 
129
129
  // ... submit to OpenAI Batch API, poll later, get the response ...
130
130
 
131
- // Step 3 Parse the raw LLM response
131
+ // Step 3 - Parse the raw LLM response
132
132
  const llmResult = extractor.parse(rawJsonResponse);
133
133
 
134
- // Step 4 Merge everything (fusion + conflict detection + validation)
134
+ // Step 4 - Merge everything (fusion + conflict detection + validation)
135
135
  const result = extractor.merge(partial, llmResult, markdown);
136
136
  ```
137
137
 
@@ -181,17 +181,39 @@ End-to-end runnable example (upload + poll + download + merge): [`examples/opena
181
181
 
182
182
  ### Per-field confidence scoring
183
183
 
184
- Every field in the result carries a confidence score (0.01.0):
184
+ Every field in the result carries a confidence score (0.0-1.0):
185
185
 
186
186
  | Source | Confidence |
187
187
  |--------|-----------|
188
188
  | Deterministic rule, exact match | 1.0 |
189
- | Deterministic rule, partial match | 0.70.9 (you decide) |
189
+ | Deterministic rule, partial match | 0.7-0.9 (you decide) |
190
190
  | LLM only | configurable default (0.7) |
191
191
  | Rule + LLM agree | 1.0 |
192
192
  | Rule + LLM disagree | 0.3 (flagged as conflict) |
193
193
  | No source | `null` |
194
194
 
195
+ ### Per-field provenance
196
+
197
+ Alongside `confidence`, every field carries a `source` describing where the kept value came from. Useful for attributing extractions back to the rule that produced them, monitoring rule quality at scale, or filtering on agreement vs LLM-only fields:
198
+
199
+ ```typescript
200
+ result.sources;
201
+ // {
202
+ // total: { kind: 'agreement', ruleId: 'total-eur' }, // rule + LLM agreed
203
+ // currency: { kind: 'rule', ruleId: 'currency#1' }, // only the rule produced a value
204
+ // vendor: { kind: 'llm' }, // only the LLM produced a value
205
+ // date: { kind: 'flag', ruleId: 'date-iso' }, // rule and LLM disagreed under flag strategy
206
+ // notes: null, // missing
207
+ // }
208
+ ```
209
+
210
+ `ruleId` defaults to `${field}#${declarationIndex}` based on the rule's position in the array - stable as long as you don't reorder. For long-lived production code, declare ids explicitly so refactors don't break observability:
211
+
212
+ ```typescript
213
+ rule.create('total', extractTotal, { id: 'total-eur' });
214
+ rule.regex('date', /(\d{4}-\d{2}-\d{2})/, 0.95, undefined, { id: 'date-iso' });
215
+ ```
216
+
195
217
  ### Conflict detection
196
218
 
197
219
  When a rule and the LLM extract different values for the same field, Llmbic flags it:
@@ -201,10 +223,28 @@ result.conflicts;
201
223
  // [{ field: 'total', ruleValue: 1250, ruleConfidence: 1.0, llmValue: 1520 }]
202
224
  ```
203
225
 
204
- Three conflict strategies: `'flag'` (default keep rule value, record conflict), `'prefer-rule'`, or `'prefer-llm'`.
226
+ Three conflict strategies: `'flag'` (default - keep rule value, record conflict), `'prefer-rule'`, or `'prefer-llm'`.
205
227
 
206
228
  In the default `'fill-gaps'` mode the LLM is only asked about fields the rules could not resolve, so conflicts are impossible. To actually trigger conflict detection, opt into cross-check (see below).
207
229
 
230
+ #### Per-field strategies
231
+
232
+ `policy` is a single strategy applied to every field. When fields have different criticality (a `price` you want to flag vs a `postal_code` your regex always nails vs a free-form `description` you'd rather defer to the LLM), use `policyByField` to override per field. Precedence: library defaults < `policy` < `policyByField[field]`.
233
+
234
+ ```typescript
235
+ const extractor = createExtractor({
236
+ schema: ListingSchema,
237
+ rules: [...],
238
+ policy: { strategy: 'flag' }, // default for every field
239
+ policyByField: {
240
+ postal_code: { strategy: 'prefer-rule' },
241
+ description: { strategy: 'prefer-llm' },
242
+ },
243
+ });
244
+ ```
245
+
246
+ You can override any subset of `FieldMergePolicy` per field - strategy, confidences, even the `compare` callback (e.g. fuzzy equality for free-form strings). TypeScript validates field names against your schema, so typos surface at compile time.
247
+
208
248
  ### Cross-check mode
209
249
 
210
250
  Switch the LLM call from fill-gaps (ask only about missing fields) to cross-check (ask about every schema field, whether the rules resolved it or not):
@@ -223,6 +263,35 @@ const extractor = createExtractor({
223
263
 
224
264
  The merge step now sees two candidates per field and surfaces real disagreements through `result.conflicts`. `crossCheckHints: 'bias'` re-exposes the rule values as hints to save tokens, at the cost of confirmation bias (the LLM tends to agree with what it was shown).
225
265
 
266
+ ### Per-call context
267
+
268
+ Rules can accept a second, caller-provided argument alongside `content` so they can read per-call metadata (locale, tenant configuration, feature flags) without having to capture it in a closure at declaration time:
269
+
270
+ ```typescript
271
+ import { createExtractor, rule } from 'llmbic';
272
+ import { z } from 'zod';
273
+
274
+ type Region = { region: 'us' | 'uk' };
275
+
276
+ const schema = z.object({ price: z.string() });
277
+
278
+ const priceRule = rule.create<Region>('price', (content, context) => {
279
+ const pattern = context?.region === 'uk' ? /£\s*(\d+)/ : /\$\s*(\d+)/;
280
+ const match = content.match(pattern);
281
+ return match ? rule.confidence(match[1]!, 1) : null;
282
+ });
283
+
284
+ const extractor = createExtractor<typeof schema, Region>({
285
+ schema,
286
+ rules: [priceRule],
287
+ });
288
+
289
+ await extractor.extract('Listed at £42', { region: 'uk' });
290
+ // -> { price: '42' }
291
+ ```
292
+
293
+ The type parameter on `rule.create<TContext>` flows to the generic on `createExtractor<Schema, TContext>` and is enforced at every call site. `context` is forwarded verbatim to every rule's `extract` callback; rules that ignore the argument still compile and work. `extractSync(content, context?)` behaves the same way for batch workflows. `Extractor.merge` reuses the partial's values and does not re-evaluate rules, so it takes no `context`.
294
+
226
295
  ### Rich schemas
227
296
 
228
297
  The JSON Schema handed to the LLM supports the Zod constructs that show up in real-world extraction targets:
@@ -273,9 +342,33 @@ result.validation;
273
342
  // or { valid: false, violations: [{ field: 'price', rule: 'price_positive', message: '...', severity: 'error' }] }
274
343
  ```
275
344
 
345
+ ### Request / response transformers
346
+
347
+ Two optional hooks let you intercept the LLM exchange without wrapping the provider yourself: `transformRequest` runs after `prompt.build` and before `provider.complete`; `transformResponse` runs after `prompt.parse` and before the merge. Both can be async; errors propagate.
348
+
349
+ ```typescript
350
+ const extractor = createExtractor({
351
+ schema: ContactSchema,
352
+ rules: [...],
353
+ llm: {
354
+ provider,
355
+ transformRequest: (request, content) => ({
356
+ ...request,
357
+ systemPrompt: `Language: ${detectLocale(content)}\n${request.systemPrompt}`,
358
+ }),
359
+ },
360
+ });
361
+ ```
362
+
363
+ Common patterns:
364
+
365
+ - **PII redaction (RGPD)**: replace emails / phones / IDs with placeholders in `userContent`, stash the originals in `knownValues` under a private key, restore them in `transformResponse`. Worked example: [`examples/pii-redaction.ts`](./examples/pii-redaction.ts).
366
+ - **Locale tagging**: prepend `Language: ...` to `systemPrompt` after caller-side detection.
367
+ - **Caching**: wrap your `LlmProvider.complete` directly - cleaner than short-circuiting in a hook, since it sits at the actual transport boundary.
368
+
276
369
  ## Writing a provider
277
370
 
278
- Llmbic does not ship vendor-specific adapters. The `LlmProvider` contract is a single method wiring to any backend (OpenAI, Anthropic, Ollama, vLLM, Gemini, custom HTTP, ...) is ~10 lines you write and own.
371
+ Llmbic does not ship vendor-specific adapters. The `LlmProvider` contract is a single method - wiring to any backend (OpenAI, Anthropic, Ollama, vLLM, Gemini, custom HTTP, ...) is ~10 lines you write and own.
279
372
 
280
373
  ```typescript
281
374
  import type { LlmProvider } from 'llmbic';
@@ -340,7 +433,7 @@ const provider: LlmProvider = {
340
433
  };
341
434
  ```
342
435
 
343
- **Ollama** (native `format` JSON Schema, requires Ollama 0.5+):
436
+ **Ollama** (native `format` - JSON Schema, requires Ollama 0.5+):
344
437
 
345
438
  ```typescript
346
439
  const client = new Ollama();
@@ -359,31 +452,32 @@ const provider: LlmProvider = {
359
452
  };
360
453
  ```
361
454
 
362
- Observability (token usage, latency, cost accounting) is out of scope wrap the `complete` call in whatever telemetry you already use.
455
+ Observability (token usage, latency, cost accounting) is out of scope - wrap the `complete` call in whatever telemetry you already use.
363
456
 
364
457
  ## Design decisions
365
458
 
366
- - **One dependency** Zod only. No vendor SDK ever enters the import graph; you bring your own LLM provider (see "Writing a provider").
367
- - **No retry** If the LLM returns invalid data, `parse()` does best-effort parsing (valid fields kept, invalid ignored). Retry is an orchestration concern.
368
- - **No streaming** Llmbic works with complete results. Streaming is a transport concern.
369
- - **No chunking** One content = one extraction. If your content is too long, split it before calling Llmbic.
370
- - **Normalizers mutate** For pragmatic reasons, normalizers receive and return the same object. The `merge()` function copies the data first, so the original is never modified.
371
- - **Rules are sync** Extraction rules are pure synchronous functions. If you need async lookups, do them before creating the rule.
459
+ - **One dependency** - Zod only. No vendor SDK ever enters the import graph; you bring your own LLM provider (see "Writing a provider").
460
+ - **No retry** - If the LLM returns invalid data, `parse()` does best-effort parsing (valid fields kept, invalid ignored). Retry is an orchestration concern.
461
+ - **No streaming** - Llmbic works with complete results. Streaming is a transport concern.
462
+ - **No chunking** - One content = one extraction. If your content is too long, split it before calling Llmbic.
463
+ - **Normalizers mutate** - For pragmatic reasons, normalizers receive and return the same object. The `merge()` function copies the data first, so the original is never modified.
464
+ - **Rules are sync** - Extraction rules are pure synchronous functions. If you need async lookups, do them before creating the rule.
372
465
 
373
466
  ## API reference
374
467
 
375
468
  ### `createExtractor(config)`
376
469
 
377
- Creates an extractor instance. Config:
470
+ Creates an extractor instance. Signature: `createExtractor<S, TContext = unknown>(config)`. `TContext` is optional and describes the per-call context passed to `Extractor.extract(content, context?)`; see [Per-call context](#per-call-context). Config:
378
471
 
379
472
  | Field | Type | Required | Description |
380
473
  |-------|------|----------|-------------|
381
474
  | `schema` | `ZodObject` | yes | Output schema (drives field enumeration and re-validation). |
382
- | `rules` | `ExtractionRule[]` | yes | Deterministic extraction rules. |
475
+ | `rules` | `ExtractionRule<TContext>[]` | yes | Deterministic extraction rules. |
383
476
  | `llm` | `ExtractorLlmConfig` | no | LLM fallback. Omit for rules-only mode. See below. |
384
477
  | `normalizers` | `Normalizer<T>[]` | no | Post-merge transformations, run in declared order. |
385
478
  | `validators` | `Validator<ExtractedData<T>>[]` | no | Invariants populating `result.validation`. |
386
- | `policy` | `Partial<FieldMergePolicy>` | no | Overrides the per-field merge policy (conflict strategy, confidence defaults, equality). |
479
+ | `policy` | `Partial<FieldMergePolicy>` | no | Overrides the per-field merge policy (conflict strategy, confidence defaults, equality) for every field. |
480
+ | `policyByField` | `{ [K in keyof T]?: Partial<FieldMergePolicy> }` | no | Per-field overrides applied on top of `policy`. Precedence: defaults < `policy` < `policyByField[field]`. |
387
481
  | `logger` | `Logger` | no | Pino/Winston/console-compatible. Warnings from `rule.apply` and `merge.apply` flow through. |
388
482
 
389
483
  `ExtractorLlmConfig`:
@@ -394,15 +488,17 @@ Creates an extractor instance. Config:
394
488
  | `systemPrompt` | `string` | no | Overrides the built-in system prompt. |
395
489
  | `mode` | `'fill-gaps' \| 'cross-check'` | no | `'fill-gaps'` (default) asks the LLM only about fields the rules did not resolve. `'cross-check'` asks about every schema field so `merge.apply` can surface agreements / conflicts. |
396
490
  | `crossCheckHints` | `'bias' \| 'unbiased'` | no | In cross-check mode only. `'unbiased'` (default) hides rule values from the LLM for genuine disagreement detection; `'bias'` re-exposes them to save tokens. |
491
+ | `transformRequest` | `(request, content) => LlmRequest \| Promise<LlmRequest>` | no | Hook called with the built request before `provider.complete`. PII redaction, locale tagging, etc. |
492
+ | `transformResponse` | `(result, request) => LlmResult \| Promise<LlmResult>` | no | Hook called with the parsed LLM result before the merge. PII restoration, post-processing, etc. |
397
493
 
398
494
  ### `rule` namespace
399
495
 
400
496
  | Member | Signature | Description |
401
497
  |---|---|---|
402
- | `rule.create` | `(field, extract) => ExtractionRule` | Declare a rule. `extract(content)` returns a `RuleMatch` or `null`. |
403
- | `rule.regex` | `(field, pattern, score, transform?) => ExtractionRule` | Regex-based rule. On match, capture group 1 (or the full match) is fed to `transform`. |
498
+ | `rule.create` | `<TContext = unknown>(field, extract, options?) => ExtractionRule<TContext>` | Declare a rule. `extract(content, context?)` returns a `RuleMatch` or `null`. `options.id` sets the stable identifier surfaced in `result.sources`. `TContext` is inferred from the callback when present. |
499
+ | `rule.regex` | `(field, pattern, score, transform?, options?) => ExtractionRule` | Regex-based rule. On match, capture group 1 (or the full match) is fed to `transform`. `options.id` sets the stable identifier surfaced in `result.sources`. |
404
500
  | `rule.confidence` | `(value, score) => RuleMatch` | Wrap a value and a confidence score; sugar for custom `extract` callbacks. |
405
- | `rule.apply` | `(content, rules, schema, logger?) => RulesResult` | Run every rule, pick the highest-confidence match per field, type-check against the schema. |
501
+ | `rule.apply` | `<S, TContext = unknown>(content, rules, schema, logger?, context?) => RulesResult` | Run every rule, pick the highest-confidence match per field, type-check against the schema. `context` is forwarded verbatim to each rule's `extract` callback. |
406
502
 
407
503
  ### `validator.of<T>()`
408
504
 
@@ -417,11 +513,11 @@ Binding `T` once lets TypeScript infer each field's type from the field name, so
417
513
 
418
514
  | Method | Sync | Description |
419
515
  |--------|------|-------------|
420
- | `extract(content)` | async | Full pipeline: rules -> LLM (if configured) -> merge -> normalize -> validate. |
421
- | `extractSync(content)` | sync | Rules only. Returns the partial result + `missing` fields. |
516
+ | `extract(content, context?)` | async | Full pipeline: rules -> LLM (if configured) -> merge -> normalize -> validate. `context` is forwarded verbatim to every rule's `extract` callback. |
517
+ | `extractSync(content, context?)` | sync | Rules only. Returns the partial result + `missing` fields. `context` is forwarded verbatim to every rule's `extract` callback. |
422
518
  | `prompt(content, partial)` | sync | Builds the LLM request. Covers `partial.missing` in fill-gaps mode, every schema field in cross-check mode. |
423
519
  | `parse(raw)` | sync | Parses a raw LLM JSON response, validating each field individually. Never throws. |
424
- | `merge(partial, llmResult, content)` | sync | Merges rules + LLM, detects conflicts, normalizes, validates. |
520
+ | `merge(partial, llmResult, content)` | sync | Merges rules + LLM, detects conflicts, normalizes, validates. Does not re-evaluate rules, so takes no `context`. |
425
521
 
426
522
  ## License
427
523
 
@@ -15,11 +15,13 @@ import type { Extractor, ExtractorConfig } from './types/extractor.types.js';
15
15
  * is parsed with {@link prompt.parse} and fused through {@link merge.apply}.
16
16
  *
17
17
  * @typeParam S - A Zod object schema describing the target data shape.
18
+ * @typeParam TContext - Shape of the optional per-call context forwarded to
19
+ * every rule's `extract` callback. Defaults to `unknown`.
18
20
  * @param config - Schema, deterministic rules, and optional LLM fallback,
19
21
  * plus `policy`, `normalizers`, `validators` and `logger` forwarded to
20
22
  * every internal {@link merge.apply} call. The logger is also forwarded
21
23
  * to {@link rule.apply} so schema-rejection warnings stay visible.
22
24
  * @returns An {@link Extractor} bound to `config.schema`.
23
25
  */
24
- export declare function createExtractor<S extends z.ZodObject<z.ZodRawShape>>(config: ExtractorConfig<S>): Extractor<z.infer<S>>;
26
+ export declare function createExtractor<S extends z.ZodObject<z.ZodRawShape>, TContext = unknown>(config: ExtractorConfig<S, TContext>): Extractor<z.infer<S>, TContext>;
25
27
  //# sourceMappingURL=extractor.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"extractor.d.ts","sourceRoot":"","sources":["../src/extractor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAI7B,OAAO,KAAK,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AA8C7E;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,eAAe,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,WAAW,CAAC,EAClE,MAAM,EAAE,eAAe,CAAC,CAAC,CAAC,GACzB,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAqEvB"}
1
+ {"version":3,"file":"extractor.d.ts","sourceRoot":"","sources":["../src/extractor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAI7B,OAAO,KAAK,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAmD7E;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,eAAe,CAC7B,CAAC,SAAS,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,WAAW,CAAC,EACpC,QAAQ,GAAG,OAAO,EAElB,MAAM,EAAE,eAAe,CAAC,CAAC,EAAE,QAAQ,CAAC,GACnC,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,QAAQ,CAAC,CA4EjC"}
package/dist/extractor.js CHANGED
@@ -10,6 +10,7 @@ import { prompt } from './prompt.js';
10
10
  function rulesResultFromPartial(partial, allFields) {
11
11
  const values = {};
12
12
  const confidence = {};
13
+ const sourceIds = {};
13
14
  for (const field of allFields) {
14
15
  const value = partial.data[field];
15
16
  if (value === null) {
@@ -20,8 +21,12 @@ function rulesResultFromPartial(partial, allFields) {
20
21
  if (fieldConfidence !== null) {
21
22
  confidence[field] = fieldConfidence;
22
23
  }
24
+ const source = partial.sources[field];
25
+ if (source !== null && 'ruleId' in source) {
26
+ sourceIds[field] = source.ruleId;
27
+ }
23
28
  }
24
- return { values, confidence, missing: [...partial.missing] };
29
+ return { values, confidence, sourceIds, missing: [...partial.missing] };
25
30
  }
26
31
  /**
27
32
  * Stamp `result.meta.durationMs` with the wall-clock elapsed since `startedAt`.
@@ -50,6 +55,8 @@ function stampDuration(result, startedAt) {
50
55
  * is parsed with {@link prompt.parse} and fused through {@link merge.apply}.
51
56
  *
52
57
  * @typeParam S - A Zod object schema describing the target data shape.
58
+ * @typeParam TContext - Shape of the optional per-call context forwarded to
59
+ * every rule's `extract` callback. Defaults to `unknown`.
53
60
  * @param config - Schema, deterministic rules, and optional LLM fallback,
54
61
  * plus `policy`, `normalizers`, `validators` and `logger` forwarded to
55
62
  * every internal {@link merge.apply} call. The logger is also forwarded
@@ -68,30 +75,37 @@ export function createExtractor(config) {
68
75
  };
69
76
  const mergeOptions = {
70
77
  policy: config.policy,
78
+ policyByField: config.policyByField,
71
79
  normalizers: config.normalizers,
72
80
  validators: config.validators,
73
81
  logger: config.logger,
74
82
  };
75
83
  return {
76
- async extract(content) {
84
+ async extract(content, context) {
77
85
  const startedAt = performance.now();
78
- const rulesResult = rule.apply(content, config.rules, config.schema, config.logger);
86
+ const rulesResult = rule.apply(content, config.rules, config.schema, config.logger, context);
79
87
  const partial = merge.apply(config.schema, rulesResult, null, content, mergeOptions);
80
88
  const shouldCallLlm = config.llm !== undefined &&
81
89
  (buildOptions.mode === 'cross-check' || partial.missing.length > 0);
82
90
  if (!shouldCallLlm) {
83
91
  return stampDuration(partial, startedAt);
84
92
  }
85
- const request = prompt.build(config.schema, partial, content, buildOptions);
93
+ const builtRequest = prompt.build(config.schema, partial, content, buildOptions);
94
+ const request = config.llm.transformRequest
95
+ ? await config.llm.transformRequest(builtRequest, content)
96
+ : builtRequest;
86
97
  const completion = await config.llm.provider.complete(request);
87
98
  const llmTargetFields = buildOptions.mode === 'cross-check' ? allFields : partial.missing;
88
- const llmResult = prompt.parse(config.schema, llmTargetFields, completion.values);
99
+ const parsedLlmResult = prompt.parse(config.schema, llmTargetFields, completion.values);
100
+ const llmResult = config.llm.transformResponse
101
+ ? await config.llm.transformResponse(parsedLlmResult, request)
102
+ : parsedLlmResult;
89
103
  const final = merge.apply(config.schema, rulesResult, llmResult, content, mergeOptions);
90
104
  return stampDuration(final, startedAt);
91
105
  },
92
- extractSync(content) {
106
+ extractSync(content, context) {
93
107
  const startedAt = performance.now();
94
- const rulesResult = rule.apply(content, config.rules, config.schema, config.logger);
108
+ const rulesResult = rule.apply(content, config.rules, config.schema, config.logger, context);
95
109
  const partial = merge.apply(config.schema, rulesResult, null, content, mergeOptions);
96
110
  return stampDuration(partial, startedAt);
97
111
  },
@@ -1 +1 @@
1
- {"version":3,"file":"extractor.js","sourceRoot":"","sources":["../src/extractor.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"extractor.js","sourceRoot":"","sources":["../src/extractor.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Llmbic public entry point.
2
+ * Llmbic - public entry point.
3
3
  *
4
4
  * Re-exports the five namespaces that make up the library (`createExtractor`,
5
5
  * `rule`, `merge`, `prompt`, `validator`) and every public type consumers need
@@ -20,5 +20,5 @@ export type { CrossCheckHints, LlmRequest, PromptBuildMode, PromptBuildOptions,
20
20
  export type { LlmProvider } from './types/provider.types.js';
21
21
  export type { Logger } from './types/logger.types.js';
22
22
  export type { Severity, Violation, Validator, } from './types/validate.types.js';
23
- export type { Conflict, ConflictStrategy, ExtractedData, ExtractionMeta, ExtractionResult, FieldCompare, FieldMergePolicy, FieldMergeResult, LlmResult, MergeApplyOptions, Normalizer, ValidationResult, } from './types/merge.types.js';
23
+ export type { Conflict, ConflictStrategy, ExtractedData, ExtractionMeta, ExtractionResult, FieldCompare, FieldMergePolicy, FieldMergeResult, FieldSource, LlmResult, MergeApplyOptions, Normalizer, ValidationResult, } from './types/merge.types.js';
24
24
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAClC,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAE1C,YAAY,EACV,cAAc,EACd,SAAS,EACT,WAAW,GACZ,MAAM,uBAAuB,CAAC;AAE/B,YAAY,EACV,SAAS,EACT,eAAe,EACf,kBAAkB,GACnB,MAAM,4BAA4B,CAAC;AAEpC,YAAY,EACV,eAAe,EACf,UAAU,EACV,eAAe,EACf,kBAAkB,GACnB,MAAM,yBAAyB,CAAC;AACjC,YAAY,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AAC7D,YAAY,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AAEtD,YAAY,EACV,QAAQ,EACR,SAAS,EACT,SAAS,GACV,MAAM,2BAA2B,CAAC;AAEnC,YAAY,EACV,QAAQ,EACR,gBAAgB,EAChB,aAAa,EACb,cAAc,EACd,gBAAgB,EAChB,YAAY,EACZ,gBAAgB,EAChB,gBAAgB,EAChB,SAAS,EACT,iBAAiB,EACjB,UAAU,EACV,gBAAgB,GACjB,MAAM,wBAAwB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAClC,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAE1C,YAAY,EACV,cAAc,EACd,SAAS,EACT,WAAW,GACZ,MAAM,uBAAuB,CAAC;AAE/B,YAAY,EACV,SAAS,EACT,eAAe,EACf,kBAAkB,GACnB,MAAM,4BAA4B,CAAC;AAEpC,YAAY,EACV,eAAe,EACf,UAAU,EACV,eAAe,EACf,kBAAkB,GACnB,MAAM,yBAAyB,CAAC;AACjC,YAAY,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AAC7D,YAAY,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AAEtD,YAAY,EACV,QAAQ,EACR,SAAS,EACT,SAAS,GACV,MAAM,2BAA2B,CAAC;AAEnC,YAAY,EACV,QAAQ,EACR,gBAAgB,EAChB,aAAa,EACb,cAAc,EACd,gBAAgB,EAChB,YAAY,EACZ,gBAAgB,EAChB,gBAAgB,EAChB,WAAW,EACX,SAAS,EACT,iBAAiB,EACjB,UAAU,EACV,gBAAgB,GACjB,MAAM,wBAAwB,CAAC"}
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Llmbic public entry point.
2
+ * Llmbic - public entry point.
3
3
  *
4
4
  * Re-exports the five namespaces that make up the library (`createExtractor`,
5
5
  * `rule`, `merge`, `prompt`, `validator`) and every public type consumers need
package/dist/merge.d.ts CHANGED
@@ -43,7 +43,7 @@ export declare const merge: {
43
43
  * @param field - Name of the field being merged.
44
44
  * @param ruleMatch - Value proposed by a deterministic rule, or `null` if none.
45
45
  * @param llmValue - Value proposed by the LLM, or `null` if none. Cast to `T`
46
- * without runtime type-check callers that expose `merge.field` via
46
+ * without runtime type-check - callers that expose `merge.field` via
47
47
  * `merge.apply` rely on the final Zod re-validation to reject invalid LLM values.
48
48
  * @param policy - Optional strategy and confidence overrides.
49
49
  * @param logger - Optional logger notified of unexpected runtime situations
@@ -58,7 +58,7 @@ export declare const merge: {
58
58
  * Passing `llmResult = null` runs in rules-only mode: every field keeps
59
59
  * whatever the rules produced and `meta.llmCalled` is `false`.
60
60
  *
61
- * Orchestration only the three phases (fusion, normalization, validation)
61
+ * Orchestration only - the three phases (fusion, normalization, validation)
62
62
  * each live in their own private helper above.
63
63
  *
64
64
  * Runtime fields of `meta` (`durationMs`, `tokensUsed`) are populated by
@@ -1 +1 @@
1
- {"version":3,"file":"merge.d.ts","sourceRoot":"","sources":["../src/merge.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAC7B,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AACtD,OAAO,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAEpE,OAAO,KAAK,EAGV,gBAAgB,EAChB,gBAAgB,EAChB,gBAAgB,EAChB,SAAS,EACT,iBAAiB,EAElB,MAAM,wBAAwB,CAAC;AA+GhC;;;;;GAKG;AACH,eAAO,MAAM,KAAK;IAChB;;;;;;OAMG;;QAED,6CAA6C;;QAE7C,yDAAyD;;QAEzD,sDAAsD;;QAEtD,wDAAwD;;QAExD,qGAAqG;qBACxF,OAAO,KAAK,OAAO,KAAG,OAAO;;IAQ5C;;;;;;;;;;;;;;;;;;;;OAoBG;UACG,CAAC,SACE,MAAM,aACF,SAAS,CAAC,CAAC,CAAC,GAAG,IAAI,YACpB,OAAO,WACR,OAAO,CAAC,gBAAgB,CAAC,WACzB,MAAM,GACd,gBAAgB,CAAC,CAAC,CAAC;IAgEtB;;;;;;;;;;;;;;;;;;;;OAoBG;UACG,CAAC,SAAS,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,WAAW,CAAC,UAChC,CAAC,eACI,WAAW,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,aACzB,SAAS,GAAG,IAAI,WAClB,MAAM,YACL,iBAAiB,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,GACtC,gBAAgB,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;CAmChC,CAAC"}
1
+ {"version":3,"file":"merge.d.ts","sourceRoot":"","sources":["../src/merge.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAC7B,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AACtD,OAAO,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAEpE,OAAO,KAAK,EAGV,gBAAgB,EAChB,gBAAgB,EAChB,gBAAgB,EAEhB,SAAS,EACT,iBAAiB,EAElB,MAAM,wBAAwB,CAAC;AAuKhC;;;;;GAKG;AACH,eAAO,MAAM,KAAK;IAChB;;;;;;OAMG;;QAED,6CAA6C;;QAE7C,yDAAyD;;QAEzD,sDAAsD;;QAEtD,wDAAwD;;QAExD,qGAAqG;qBACxF,OAAO,KAAK,OAAO,KAAG,OAAO;;IAQ5C;;;;;;;;;;;;;;;;;;;;OAoBG;UACG,CAAC,SACE,MAAM,aACF,SAAS,CAAC,CAAC,CAAC,GAAG,IAAI,YACpB,OAAO,WACR,OAAO,CAAC,gBAAgB,CAAC,WACzB,MAAM,GACd,gBAAgB,CAAC,CAAC,CAAC;IAgEtB;;;;;;;;;;;;;;;;;;;;OAoBG;UACG,CAAC,SAAS,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,WAAW,CAAC,UAChC,CAAC,eACI,WAAW,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,aACzB,SAAS,GAAG,IAAI,WAClB,MAAM,YACL,iBAAiB,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,GACtC,gBAAgB,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;CAqChC,CAAC"}
package/dist/merge.js CHANGED
@@ -3,15 +3,16 @@
3
3
  * value, fuse it with the LLM candidate via {@link merge.field}, and collect
4
4
  * per-field outcomes. Invoked once at the top of {@link merge.apply}.
5
5
  */
6
- function fuseAllFields(schemaKeys, rulesResult, llmResult, policy, logger) {
6
+ function fuseAllFields(schemaKeys, rulesResult, llmResult, policy, policyByField, logger) {
7
7
  const data = {};
8
8
  const confidence = {};
9
+ const sources = {};
9
10
  const conflicts = [];
10
11
  const missing = [];
11
12
  let rulesMatched = 0;
12
13
  for (const field of schemaKeys) {
13
14
  const hasRuleValue = field in rulesResult.values;
14
- // hasRuleValue implies confidence[field] is defined rule.apply only writes
15
+ // hasRuleValue implies confidence[field] is defined - rule.apply only writes
15
16
  // to `confidence` when it also writes to `values`.
16
17
  const ruleMatch = hasRuleValue
17
18
  ? {
@@ -23,9 +24,13 @@ function fuseAllFields(schemaKeys, rulesResult, llmResult, policy, logger) {
23
24
  rulesMatched += 1;
24
25
  }
25
26
  const llmValue = llmResult?.values[field] ?? null;
26
- const fused = merge.field(field, ruleMatch, llmValue, policy, logger);
27
+ const fieldOverride = policyByField?.[field];
28
+ const resolvedPolicy = fieldOverride === undefined ? policy : { ...policy, ...fieldOverride };
29
+ const ruleId = rulesResult.sourceIds?.[field];
30
+ const fused = merge.field(field, ruleMatch, llmValue, resolvedPolicy, logger);
27
31
  data[field] = fused.value;
28
32
  confidence[field] = fused.confidence;
33
+ sources[field] = deriveSource(fused, ruleMatch, llmValue, resolvedPolicy, ruleId);
29
34
  if (fused.conflict !== undefined) {
30
35
  conflicts.push(fused.conflict);
31
36
  }
@@ -33,7 +38,47 @@ function fuseAllFields(schemaKeys, rulesResult, llmResult, policy, logger) {
33
38
  missing.push(field);
34
39
  }
35
40
  }
36
- return { data, confidence, conflicts, missing, rulesMatched };
41
+ return { data, confidence, sources, conflicts, missing, rulesMatched };
42
+ }
43
+ /**
44
+ * Classify the origin of a fused value into a {@link FieldSource}. Mirrors
45
+ * the decision tree of {@link merge.field} without re-running the strategy:
46
+ *
47
+ * - rule alone -> `'rule'`
48
+ * - LLM alone -> `'llm'`
49
+ * - both null -> `null`
50
+ * - both present, conflict recorded -> `'flag'` (only the `'flag'` strategy
51
+ * produces a conflict)
52
+ * - both present, no conflict, kept value differs from the rule -> `'llm'`
53
+ * (only `'prefer-llm'` reaches this case)
54
+ * - both present, no conflict, kept value matches the rule -> `'agreement'`
55
+ * when the policy's `compare` returns true, else `'rule'` (`'prefer-rule'`
56
+ * silent path)
57
+ *
58
+ * `ruleId` is `''` when the rule provided no declared id and `rule.apply`
59
+ * was bypassed by the caller.
60
+ */
61
+ function deriveSource(fused, ruleMatch, llmValue, policy, ruleId) {
62
+ if (fused.value === null) {
63
+ return null;
64
+ }
65
+ const id = ruleId ?? '';
66
+ if (ruleMatch === null) {
67
+ return { kind: 'llm' };
68
+ }
69
+ if (llmValue === null || llmValue === undefined) {
70
+ return { kind: 'rule', ruleId: id };
71
+ }
72
+ if (fused.conflict !== undefined) {
73
+ return { kind: 'flag', ruleId: id };
74
+ }
75
+ if (fused.value !== ruleMatch.value) {
76
+ return { kind: 'llm' };
77
+ }
78
+ const compare = policy?.compare ?? merge.defaultFieldPolicy.compare;
79
+ return compare(ruleMatch.value, llmValue)
80
+ ? { kind: 'agreement', ruleId: id }
81
+ : { kind: 'rule', ruleId: id };
37
82
  }
38
83
  /**
39
84
  * Apply every configured {@link Normalizer} to the merged data in declared
@@ -122,7 +167,7 @@ export const merge = {
122
167
  * @param field - Name of the field being merged.
123
168
  * @param ruleMatch - Value proposed by a deterministic rule, or `null` if none.
124
169
  * @param llmValue - Value proposed by the LLM, or `null` if none. Cast to `T`
125
- * without runtime type-check callers that expose `merge.field` via
170
+ * without runtime type-check - callers that expose `merge.field` via
126
171
  * `merge.apply` rely on the final Zod re-validation to reject invalid LLM values.
127
172
  * @param policy - Optional strategy and confidence overrides.
128
173
  * @param logger - Optional logger notified of unexpected runtime situations
@@ -194,7 +239,7 @@ export const merge = {
194
239
  * Passing `llmResult = null` runs in rules-only mode: every field keeps
195
240
  * whatever the rules produced and `meta.llmCalled` is `false`.
196
241
  *
197
- * Orchestration only the three phases (fusion, normalization, validation)
242
+ * Orchestration only - the three phases (fusion, normalization, validation)
198
243
  * each live in their own private helper above.
199
244
  *
200
245
  * Runtime fields of `meta` (`durationMs`, `tokensUsed`) are populated by
@@ -209,13 +254,14 @@ export const merge = {
209
254
  */
210
255
  apply(schema, rulesResult, llmResult, content, options) {
211
256
  const schemaKeys = Object.keys(schema.shape);
212
- const fusion = fuseAllFields(schemaKeys, rulesResult, llmResult, options?.policy, options?.logger);
257
+ const fusion = fuseAllFields(schemaKeys, rulesResult, llmResult, options?.policy, options?.policyByField, options?.logger);
213
258
  const normalized = runNormalizers(fusion.data, options?.normalizers, content);
214
259
  const violations = collectViolations(schema, normalized, fusion.missing, options?.validators);
215
260
  const valid = !violations.some((v) => v.severity === 'error');
216
261
  return {
217
262
  data: normalized,
218
263
  confidence: fusion.confidence,
264
+ sources: fusion.sources,
219
265
  conflicts: fusion.conflicts,
220
266
  missing: fusion.missing,
221
267
  validation: { valid, violations },
package/dist/merge.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"merge.js","sourceRoot":"","sources":["../src/merge.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"merge.js","sourceRoot":"","sources":["../src/merge.ts"],"names":[],"mappings":"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"}
package/dist/prompt.d.ts CHANGED
@@ -39,7 +39,7 @@ export declare const prompt: {
39
39
  /**
40
40
  * Parse a raw LLM response permissively. Accepts either an already-decoded
41
41
  * object or a JSON-encoded string. Each field listed in `missing` is
42
- * validated individually against its Zod schema valid fields flow into
42
+ * validated individually against its Zod schema - valid fields flow into
43
43
  * `values`, invalid ones are dropped and surfaced as warnings. Keys outside
44
44
  * `missing` are dropped as well, with a single aggregated warning so the
45
45
  * caller can spot a prompt/provider mismatch.
@@ -50,7 +50,7 @@ export declare const prompt: {
50
50
  * @param schema - Zod object schema whose fields back the validation.
51
51
  * @param missing - Fields the LLM was expected to produce (typically
52
52
  * {@link ExtractionResult.missing}).
53
- * @param raw - The provider response object or JSON string.
53
+ * @param raw - The provider response - object or JSON string.
54
54
  */
55
55
  parse<S extends z.ZodObject<z.ZodRawShape>>(schema: S, missing: readonly (keyof z.infer<S>)[], raw: unknown): LlmResult;
56
56
  };
package/dist/prompt.js CHANGED
@@ -89,7 +89,7 @@ function buildResponseSchema(schema, missing) {
89
89
  return { type: 'object', properties, required, additionalProperties: false };
90
90
  }
91
91
  /**
92
- * Pick the non-null, non-missing entries of the partial result the values
92
+ * Pick the non-null, non-missing entries of the partial result - the values
93
93
  * the deterministic pass has already resolved.
94
94
  */
95
95
  function collectKnownValues(data, missing) {
@@ -233,7 +233,7 @@ export const prompt = {
233
233
  /**
234
234
  * Parse a raw LLM response permissively. Accepts either an already-decoded
235
235
  * object or a JSON-encoded string. Each field listed in `missing` is
236
- * validated individually against its Zod schema valid fields flow into
236
+ * validated individually against its Zod schema - valid fields flow into
237
237
  * `values`, invalid ones are dropped and surfaced as warnings. Keys outside
238
238
  * `missing` are dropped as well, with a single aggregated warning so the
239
239
  * caller can spot a prompt/provider mismatch.
@@ -244,7 +244,7 @@ export const prompt = {
244
244
  * @param schema - Zod object schema whose fields back the validation.
245
245
  * @param missing - Fields the LLM was expected to produce (typically
246
246
  * {@link ExtractionResult.missing}).
247
- * @param raw - The provider response object or JSON string.
247
+ * @param raw - The provider response - object or JSON string.
248
248
  */
249
249
  parse(schema, missing, raw) {
250
250
  const missingKeys = missing;
package/dist/rules.d.ts CHANGED
@@ -10,13 +10,23 @@ export declare const rule: {
10
10
  * Declare a deterministic extraction rule targeting a single schema field.
11
11
  *
12
12
  * The `extract` callback receives the raw content and must return either a
13
- * {@link RuleMatch} or `null` when the rule does not apply.
13
+ * {@link RuleMatch} or `null` when the rule does not apply. It may also
14
+ * accept an optional caller-defined `context` forwarded verbatim by
15
+ * {@link rule.apply} / {@link Extractor.extract}.
14
16
  *
17
+ * @typeParam TContext - Shape of the optional context forwarded to
18
+ * `extract`. Defaults to `unknown`.
15
19
  * @param field - Name of the schema field the rule writes to.
16
- * @param extract - Callback that inspects the content and proposes a value.
20
+ * @param extract - Callback that inspects the content (and optional context)
21
+ * and proposes a value.
22
+ * @param options - Optional rule metadata. `id` is surfaced in
23
+ * `ExtractionResult.sources` when this rule produces the kept value;
24
+ * defaults to `${field}#${declarationIndex}`.
17
25
  * @returns An {@link ExtractionRule} ready to be passed to {@link rule.apply}.
18
26
  */
19
- create(field: string, extract: (content: string) => RuleMatch<unknown> | null): ExtractionRule;
27
+ create<TContext = unknown>(field: string, extract: (content: string, context?: TContext) => RuleMatch<unknown> | null, options?: {
28
+ id?: string;
29
+ }): ExtractionRule<TContext>;
20
30
  /**
21
31
  * Shortcut to build a regex-based {@link ExtractionRule}. On match, the
22
32
  * value is taken from capture group 1 (or the full match if none), then
@@ -29,7 +39,9 @@ export declare const rule: {
29
39
  * @param transform - Optional mapper from the raw `RegExpMatchArray` to a value.
30
40
  * @returns An {@link ExtractionRule} ready to be passed to {@link rule.apply}.
31
41
  */
32
- regex<T = string>(field: string, pattern: RegExp, confidenceScore: number, transform?: (match: RegExpMatchArray) => T): ExtractionRule;
42
+ regex<T = string>(field: string, pattern: RegExp, confidenceScore: number, transform?: (match: RegExpMatchArray) => T, options?: {
43
+ id?: string;
44
+ }): ExtractionRule;
33
45
  /**
34
46
  * Build a {@link RuleMatch} from a value and a confidence score. Syntactic
35
47
  * sugar used inside custom rule callbacks to avoid writing the object literal
@@ -61,13 +73,21 @@ export declare const rule: {
61
73
  * - Values failing the per-field Zod `safeParse` are discarded and the
62
74
  * field falls back to `missing`. An optional logger receives a warning.
63
75
  *
76
+ * The optional `context` is forwarded verbatim to every rule's `extract`
77
+ * callback. Rules that declare a narrower `ExtractionRule<TContext>` than
78
+ * the one passed here still compile thanks to contextual parameter
79
+ * contravariance; rules that ignore `context` keep working unchanged.
80
+ *
64
81
  * @typeParam S - A Zod object schema.
82
+ * @typeParam TContext - Shape of the optional context forwarded to rules.
65
83
  * @param content - Raw content to extract from (typically markdown or text).
66
84
  * @param rules - Deterministic rules to evaluate.
67
85
  * @param schema - Zod object schema describing the target data shape.
68
86
  * @param logger - Optional logger notified when a value is rejected.
87
+ * @param context - Optional caller-defined value forwarded to every rule's
88
+ * `extract` callback. Left `undefined` when omitted.
69
89
  * @returns The deterministic extraction result (values, confidence, missing).
70
90
  */
71
- apply<S extends z.ZodObject<z.ZodRawShape>>(content: string, rules: ExtractionRule[], schema: S, logger?: Logger): RulesResult<z.infer<S>>;
91
+ apply<S extends z.ZodObject<z.ZodRawShape>, TContext = unknown>(content: string, rules: ExtractionRule<TContext>[], schema: S, logger?: Logger, context?: TContext): RulesResult<z.infer<S>>;
72
92
  };
73
93
  //# sourceMappingURL=rules.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"rules.d.ts","sourceRoot":"","sources":["../src/rules.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAC7B,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AACtD,OAAO,KAAK,EAAE,cAAc,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAEpF;;;GAGG;AACH,eAAO,MAAM,IAAI;IACf;;;;;;;;;OASG;kBAEM,MAAM,WACJ,CAAC,OAAO,EAAE,MAAM,KAAK,SAAS,CAAC,OAAO,CAAC,GAAG,IAAI,GACtD,cAAc;IAIjB;;;;;;;;;;;OAWG;UACG,CAAC,kBACE,MAAM,WACJ,MAAM,mBACE,MAAM,cACX,CAAC,KAAK,EAAE,gBAAgB,KAAK,CAAC,GACzC,cAAc;IAcjB;;;;;;;;;;;;;;;;;OAiBG;eACQ,CAAC,SAAS,CAAC,SAAS,MAAM,GAAG,SAAS,CAAC,CAAC,CAAC;IAIpD;;;;;;;;;;;;;;;;;;OAkBG;UACG,CAAC,SAAS,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,WAAW,CAAC,WAC/B,MAAM,SACR,cAAc,EAAE,UACf,CAAC,WACA,MAAM,GACd,WAAW,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;CAqC3B,CAAC"}
1
+ {"version":3,"file":"rules.d.ts","sourceRoot":"","sources":["../src/rules.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAC7B,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AACtD,OAAO,KAAK,EAAE,cAAc,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAEpF;;;GAGG;AACH,eAAO,MAAM,IAAI;IACf;;;;;;;;;;;;;;;;;OAiBG;WACI,QAAQ,mBACN,MAAM,WACJ,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,QAAQ,KAAK,SAAS,CAAC,OAAO,CAAC,GAAG,IAAI,YACjE;QAAE,EAAE,CAAC,EAAE,MAAM,CAAA;KAAE,GACxB,cAAc,CAAC,QAAQ,CAAC;IAI3B;;;;;;;;;;;OAWG;UACG,CAAC,kBACE,MAAM,WACJ,MAAM,mBACE,MAAM,cACX,CAAC,KAAK,EAAE,gBAAgB,KAAK,CAAC,YAChC;QAAE,EAAE,CAAC,EAAE,MAAM,CAAA;KAAE,GACxB,cAAc;IAYjB;;;;;;;;;;;;;;;;;OAiBG;eACQ,CAAC,SAAS,CAAC,SAAS,MAAM,GAAG,SAAS,CAAC,CAAC,CAAC;IAIpD;;;;;;;;;;;;;;;;;;;;;;;;;;OA0BG;UACG,CAAC,SAAS,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,WAAW,CAAC,EAAE,QAAQ,qBACzC,MAAM,SACR,cAAc,CAAC,QAAQ,CAAC,EAAE,UACzB,CAAC,WACA,MAAM,YACL,QAAQ,GACjB,WAAW,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;CAwC3B,CAAC"}
package/dist/rules.js CHANGED
@@ -7,14 +7,22 @@ export const rule = {
7
7
  * Declare a deterministic extraction rule targeting a single schema field.
8
8
  *
9
9
  * The `extract` callback receives the raw content and must return either a
10
- * {@link RuleMatch} or `null` when the rule does not apply.
10
+ * {@link RuleMatch} or `null` when the rule does not apply. It may also
11
+ * accept an optional caller-defined `context` forwarded verbatim by
12
+ * {@link rule.apply} / {@link Extractor.extract}.
11
13
  *
14
+ * @typeParam TContext - Shape of the optional context forwarded to
15
+ * `extract`. Defaults to `unknown`.
12
16
  * @param field - Name of the schema field the rule writes to.
13
- * @param extract - Callback that inspects the content and proposes a value.
17
+ * @param extract - Callback that inspects the content (and optional context)
18
+ * and proposes a value.
19
+ * @param options - Optional rule metadata. `id` is surfaced in
20
+ * `ExtractionResult.sources` when this rule produces the kept value;
21
+ * defaults to `${field}#${declarationIndex}`.
14
22
  * @returns An {@link ExtractionRule} ready to be passed to {@link rule.apply}.
15
23
  */
16
- create(field, extract) {
17
- return { field, extract };
24
+ create(field, extract, options) {
25
+ return options?.id !== undefined ? { id: options.id, field, extract } : { field, extract };
18
26
  },
19
27
  /**
20
28
  * Shortcut to build a regex-based {@link ExtractionRule}. On match, the
@@ -28,18 +36,16 @@ export const rule = {
28
36
  * @param transform - Optional mapper from the raw `RegExpMatchArray` to a value.
29
37
  * @returns An {@link ExtractionRule} ready to be passed to {@link rule.apply}.
30
38
  */
31
- regex(field, pattern, confidenceScore, transform) {
32
- return {
33
- field,
34
- extract: (content) => {
35
- const match = content.match(pattern);
36
- if (!match) {
37
- return null;
38
- }
39
- const value = transform ? transform(match) : (match[1] ?? match[0]);
40
- return { value, confidence: confidenceScore };
41
- },
39
+ regex(field, pattern, confidenceScore, transform, options) {
40
+ const extract = (content) => {
41
+ const match = content.match(pattern);
42
+ if (!match) {
43
+ return null;
44
+ }
45
+ const value = transform ? transform(match) : (match[1] ?? match[0]);
46
+ return { value, confidence: confidenceScore };
42
47
  };
48
+ return options?.id !== undefined ? { id: options.id, field, extract } : { field, extract };
43
49
  },
44
50
  /**
45
51
  * Build a {@link RuleMatch} from a value and a confidence score. Syntactic
@@ -74,23 +80,33 @@ export const rule = {
74
80
  * - Values failing the per-field Zod `safeParse` are discarded and the
75
81
  * field falls back to `missing`. An optional logger receives a warning.
76
82
  *
83
+ * The optional `context` is forwarded verbatim to every rule's `extract`
84
+ * callback. Rules that declare a narrower `ExtractionRule<TContext>` than
85
+ * the one passed here still compile thanks to contextual parameter
86
+ * contravariance; rules that ignore `context` keep working unchanged.
87
+ *
77
88
  * @typeParam S - A Zod object schema.
89
+ * @typeParam TContext - Shape of the optional context forwarded to rules.
78
90
  * @param content - Raw content to extract from (typically markdown or text).
79
91
  * @param rules - Deterministic rules to evaluate.
80
92
  * @param schema - Zod object schema describing the target data shape.
81
93
  * @param logger - Optional logger notified when a value is rejected.
94
+ * @param context - Optional caller-defined value forwarded to every rule's
95
+ * `extract` callback. Left `undefined` when omitted.
82
96
  * @returns The deterministic extraction result (values, confidence, missing).
83
97
  */
84
- apply(content, rules, schema, logger) {
98
+ apply(content, rules, schema, logger, context) {
85
99
  const schemaKeys = Object.keys(schema.shape);
86
100
  const values = {};
87
101
  const confidenceMap = {};
88
- for (const candidate of rules) {
102
+ const sourceIds = {};
103
+ for (let index = 0; index < rules.length; index += 1) {
104
+ const candidate = rules[index];
89
105
  const field = candidate.field;
90
106
  if (!schemaKeys.includes(field)) {
91
107
  continue;
92
108
  }
93
- const match = candidate.extract(content);
109
+ const match = candidate.extract(content, context);
94
110
  if (match === null) {
95
111
  continue;
96
112
  }
@@ -110,9 +126,10 @@ export const rule = {
110
126
  }
111
127
  values[field] = parsed.data;
112
128
  confidenceMap[field] = match.confidence;
129
+ sourceIds[field] = candidate.id ?? `${candidate.field}#${index}`;
113
130
  }
114
131
  const missing = schemaKeys.filter((key) => !(key in values));
115
- return { values, confidence: confidenceMap, missing };
132
+ return { values, confidence: confidenceMap, sourceIds, missing };
116
133
  },
117
134
  };
118
135
  //# sourceMappingURL=rules.js.map
package/dist/rules.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"rules.js","sourceRoot":"","sources":["../src/rules.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"rules.js","sourceRoot":"","sources":["../src/rules.ts"],"names":[],"mappings":"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"}
@@ -27,6 +27,23 @@ export type ExtractorLlmConfig = {
27
27
  * Ignored when `mode !== 'cross-check'`.
28
28
  */
29
29
  crossCheckHints?: CrossCheckHints;
30
+ /**
31
+ * Hook called with the fully-built {@link LlmRequest} just before
32
+ * `provider.complete`. Return the request to send. Useful for PII
33
+ * redaction (replace emails / phones / IDs in `userContent`), locale
34
+ * tagging (prepend `Language: ...` to `systemPrompt`), or any caller-side
35
+ * pre-processing. The original `content` is forwarded so the hook can
36
+ * cross-reference it. May be asynchronous; errors propagate to `extract`.
37
+ */
38
+ transformRequest?: (request: LlmRequest, content: string) => LlmRequest | Promise<LlmRequest>;
39
+ /**
40
+ * Hook called with the parsed {@link LlmResult} just after
41
+ * `provider.complete`. Return the result the merge step should use. Useful
42
+ * for restoring PII-redacted values, applying caller-side post-processing,
43
+ * or stripping unsafe content. Receives the (possibly transformed) request
44
+ * for context. May be asynchronous; errors propagate to `extract`.
45
+ */
46
+ transformResponse?: (result: LlmResult, request: LlmRequest) => LlmResult | Promise<LlmResult>;
30
47
  };
31
48
  /**
32
49
  * Configuration accepted by {@link createExtractor}. A schema describes the
@@ -34,12 +51,15 @@ export type ExtractorLlmConfig = {
34
51
  * produce values for each field before any LLM fallback kicks in.
35
52
  *
36
53
  * @typeParam S - A Zod object schema describing the target data shape.
54
+ * @typeParam TContext - Shape of the optional per-call context forwarded to
55
+ * every rule's `extract` callback. Defaults to `unknown`, which keeps
56
+ * context-unaware rules assignable with zero boilerplate.
37
57
  */
38
- export type ExtractorConfig<S extends z.ZodObject<z.ZodRawShape>> = {
58
+ export type ExtractorConfig<S extends z.ZodObject<z.ZodRawShape>, TContext = unknown> = {
39
59
  /** Zod object schema the extractor targets. Drives field enumeration and re-validation. */
40
60
  schema: S;
41
61
  /** Deterministic rules evaluated against the raw content before any LLM fallback. */
42
- rules: ExtractionRule[];
62
+ rules: ExtractionRule<TContext>[];
43
63
  /** Optional LLM fallback invoked for fields the rules could not produce. */
44
64
  llm?: ExtractorLlmConfig;
45
65
  /** Post-merge transformations, forwarded to every `merge.apply` call. */
@@ -48,6 +68,14 @@ export type ExtractorConfig<S extends z.ZodObject<z.ZodRawShape>> = {
48
68
  validators?: Validator<ExtractedData<z.infer<S>>>[];
49
69
  /** Overrides for the per-field merge policy (conflict strategy, confidences, compare). */
50
70
  policy?: Partial<FieldMergePolicy>;
71
+ /**
72
+ * Per-field policy overrides applied on top of `policy`. Precedence:
73
+ * library defaults < `policy` < `policyByField[field]`. Forwarded to every
74
+ * internal `merge.apply` call.
75
+ */
76
+ policyByField?: {
77
+ [K in keyof z.infer<S>]?: Partial<FieldMergePolicy>;
78
+ };
51
79
  /** Logger propagated through the merge pipeline for warnings and fallbacks. */
52
80
  logger?: Logger;
53
81
  };
@@ -56,21 +84,25 @@ export type ExtractorConfig<S extends z.ZodObject<z.ZodRawShape>> = {
56
84
  * this interface as the matching slice introduces them.
57
85
  *
58
86
  * @typeParam T - Shape of the target data object inferred from a Zod schema.
87
+ * @typeParam TContext - Shape of the optional per-call context forwarded to
88
+ * every rule's `extract` callback. Defaults to `unknown`.
59
89
  */
60
- export type Extractor<T> = {
90
+ export type Extractor<T, TContext = unknown> = {
61
91
  /**
62
92
  * Run the full extraction pipeline against `content`: deterministic rules,
63
93
  * optionally followed by an LLM fallback for missing fields, then the merge
64
- * + validation step.
94
+ * + validation step. An optional `context` is forwarded verbatim to every
95
+ * rule's `extract` callback.
65
96
  */
66
- extract(content: string): Promise<ExtractionResult<T>>;
97
+ extract(content: string, context?: TContext): Promise<ExtractionResult<T>>;
67
98
  /**
68
99
  * Run the deterministic rules and merge them against a `null` LLM result.
69
100
  * Synchronous counterpart to {@link Extractor.extract} for batch workflows
70
101
  * where the LLM call is managed by the caller (queues, scheduled jobs,
71
- * external batch APIs).
102
+ * external batch APIs). An optional `context` is forwarded verbatim to
103
+ * every rule's `extract` callback.
72
104
  */
73
- extractSync(content: string): ExtractionResult<T>;
105
+ extractSync(content: string, context?: TContext): ExtractionResult<T>;
74
106
  /**
75
107
  * Build the LLM request for `partial`. The target field set depends on the
76
108
  * configured `llm.mode`: `'fill-gaps'` (default) covers only
@@ -87,10 +119,10 @@ export type Extractor<T> = {
87
119
  */
88
120
  parse(raw: unknown): LlmResult;
89
121
  /**
90
- * Merge a previously-obtained `partial` with an LLM result and re-run the
91
- * deterministic rules to produce the final {@link ExtractionResult}.
92
- * Rules are pure, so re-running them is cheaper than carrying private
93
- * state on the extractor.
122
+ * Merge a previously-obtained `partial` with an LLM result to produce the
123
+ * final {@link ExtractionResult}. Reuses the per-field values, confidence
124
+ * and provenance already carried by `partial`: the deterministic rules are
125
+ * not re-evaluated, so no `context` parameter is accepted here.
94
126
  */
95
127
  merge(partial: ExtractionResult<T>, llmResult: LlmResult, content: string): ExtractionResult<T>;
96
128
  };
@@ -1 +1 @@
1
- {"version":3,"file":"extractor.types.d.ts","sourceRoot":"","sources":["../../src/types/extractor.types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAC7B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACtD,OAAO,KAAK,EACV,aAAa,EACb,gBAAgB,EAChB,gBAAgB,EAChB,SAAS,EACT,UAAU,EACX,MAAM,kBAAkB,CAAC;AAC1B,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AACvD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,KAAK,EAAE,eAAe,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAEtF;;;;GAIG;AACH,MAAM,MAAM,kBAAkB,GAAG;IAC/B,+DAA+D;IAC/D,QAAQ,EAAE,WAAW,CAAC;IACtB,4GAA4G;IAC5G,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;;;OAKG;IACH,IAAI,CAAC,EAAE,eAAe,CAAC;IACvB;;;OAGG;IACH,eAAe,CAAC,EAAE,eAAe,CAAC;CACnC,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,MAAM,eAAe,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,WAAW,CAAC,IAAI;IAClE,2FAA2F;IAC3F,MAAM,EAAE,CAAC,CAAC;IACV,qFAAqF;IACrF,KAAK,EAAE,cAAc,EAAE,CAAC;IACxB,4EAA4E;IAC5E,GAAG,CAAC,EAAE,kBAAkB,CAAC;IACzB,yEAAyE;IACzE,WAAW,CAAC,EAAE,UAAU,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IACvC,+EAA+E;IAC/E,UAAU,CAAC,EAAE,SAAS,CAAC,aAAa,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IACpD,0FAA0F;IAC1F,MAAM,CAAC,EAAE,OAAO,CAAC,gBAAgB,CAAC,CAAC;IACnC,+EAA+E;IAC/E,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF;;;;;GAKG;AACH,MAAM,MAAM,SAAS,CAAC,CAAC,IAAI;IACzB;;;;OAIG;IACH,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAAC;IACvD;;;;;OAKG;IACH,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,gBAAgB,CAAC,CAAC,CAAC,CAAC;IAClD;;;;;;OAMG;IACH,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,gBAAgB,CAAC,CAAC,CAAC,GAAG,UAAU,CAAC;IAClE;;;;;OAKG;IACH,KAAK,CAAC,GAAG,EAAE,OAAO,GAAG,SAAS,CAAC;IAC/B;;;;;OAKG;IACH,KAAK,CACH,OAAO,EAAE,gBAAgB,CAAC,CAAC,CAAC,EAC5B,SAAS,EAAE,SAAS,EACpB,OAAO,EAAE,MAAM,GACd,gBAAgB,CAAC,CAAC,CAAC,CAAC;CACxB,CAAC"}
1
+ {"version":3,"file":"extractor.types.d.ts","sourceRoot":"","sources":["../../src/types/extractor.types.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Minimal logger interface consumed by llmbic internals. Any logger that
3
- * exposes a `warn` method (and optionally `info`) can be plugged in
3
+ * exposes a `warn` method (and optionally `info`) can be plugged in -
4
4
  * pino, winston, `console`, or a test double.
5
5
  */
6
6
  export type Logger = {
@@ -30,6 +30,14 @@ export type Normalizer<T> = (data: ExtractedData<T>, content: string) => Extract
30
30
  export type MergeApplyOptions<T> = {
31
31
  /** Overrides forwarded to every field-level fusion call. */
32
32
  policy?: Partial<FieldMergePolicy>;
33
+ /**
34
+ * Per-field policy overrides applied on top of `policy`. Precedence:
35
+ * library defaults < `policy` < `policyByField[field]`. Fields absent from
36
+ * the map fall back to `policy` alone.
37
+ */
38
+ policyByField?: {
39
+ [K in keyof T]?: Partial<FieldMergePolicy>;
40
+ };
33
41
  /** Transformations run in declared order after the per-field fusion. */
34
42
  normalizers?: Normalizer<T>[];
35
43
  /** Invariants run on the normalized data; their violations populate `validation`. */
@@ -40,13 +48,39 @@ export type MergeApplyOptions<T> = {
40
48
  /**
41
49
  * Strategy applied when the rule and the LLM disagree on a field value.
42
50
  *
43
- * - `'flag'` keep the rule value, lower its confidence, and record a
51
+ * - `'flag'` - keep the rule value, lower its confidence, and record a
44
52
  * {@link Conflict} so the caller can review the disagreement.
45
- * - `'prefer-rule'` silently keep the rule value and its confidence.
46
- * - `'prefer-llm'` silently keep the LLM value and the default LLM
53
+ * - `'prefer-rule'` - silently keep the rule value and its confidence.
54
+ * - `'prefer-llm'` - silently keep the LLM value and the default LLM
47
55
  * confidence.
48
56
  */
49
57
  export type ConflictStrategy = 'flag' | 'prefer-rule' | 'prefer-llm';
58
+ /**
59
+ * Origin of the value kept for a field after fusion. Each variant carries the
60
+ * `ruleId` of the deterministic rule involved when applicable, so consumers
61
+ * can attribute extracted values back to the exact rule that produced them.
62
+ *
63
+ * - `'rule'`: only the rule produced a value, or the rule won under
64
+ * `'prefer-rule'`.
65
+ * - `'llm'`: only the LLM produced a value, or the LLM won under
66
+ * `'prefer-llm'`.
67
+ * - `'agreement'`: rule and LLM produced equivalent values per the policy's
68
+ * `compare` callback. `ruleId` points to the rule that matched.
69
+ * - `'flag'`: rule and LLM disagreed under the `'flag'` strategy. The rule
70
+ * value is kept; `ruleId` identifies the rule.
71
+ */
72
+ export type FieldSource = {
73
+ kind: 'rule';
74
+ ruleId: string;
75
+ } | {
76
+ kind: 'llm';
77
+ } | {
78
+ kind: 'agreement';
79
+ ruleId: string;
80
+ } | {
81
+ kind: 'flag';
82
+ ruleId: string;
83
+ };
50
84
  /**
51
85
  * A disagreement between a rule match and an LLM value for the same field.
52
86
  * Produced only when the resolution strategy is `'flag'`.
@@ -147,6 +181,14 @@ export type ExtractionResult<T> = {
147
181
  confidence: {
148
182
  [K in keyof T]: number | null;
149
183
  };
184
+ /**
185
+ * Origin of the value kept for each field, or `null` for missing fields.
186
+ * Use it to attribute extractions back to the rule that produced them, to
187
+ * detect agreement between rules and LLM, or to spot flagged conflicts.
188
+ */
189
+ sources: {
190
+ [K in keyof T]: FieldSource | null;
191
+ };
150
192
  /** Disagreements recorded by the `'flag'` strategy during per-field fusion. */
151
193
  conflicts: Conflict[];
152
194
  /** Fields for which no value could be produced. */
@@ -1 +1 @@
1
- {"version":3,"file":"merge.types.d.ts","sourceRoot":"","sources":["../../src/types/merge.types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"merge.types.d.ts","sourceRoot":"","sources":["../../src/types/merge.types.ts"],"names":[],"mappings":"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"}
@@ -8,7 +8,7 @@ export type LlmProvider = {
8
8
  /**
9
9
  * Send `request` to the underlying model and return the structured values
10
10
  * it produced. Observability concerns (token counters, latency, cost) are
11
- * the caller's responsibility they live outside the llmbic contract so
11
+ * the caller's responsibility - they live outside the llmbic contract so
12
12
  * the library stays free of vendor-specific metering.
13
13
  *
14
14
  * @param request - Prompt, user content, and JSON Schema built by {@link prompt.build}.
@@ -13,12 +13,34 @@ export type RuleMatch<T> = {
13
13
  /**
14
14
  * A deterministic rule that tries to extract a single schema field from raw
15
15
  * content. `extract` returns `null` when the rule does not apply.
16
+ *
17
+ * Rules can optionally accept a `context` argument: an opaque, caller-defined
18
+ * value forwarded verbatim by `rule.apply` / `Extractor.extract`. Typical use
19
+ * is to expose per-call metadata (locale, tenant-specific configuration,
20
+ * feature flags) rules need to decide whether they apply. `TContext`
21
+ * defaults to `unknown` so context-unaware rules stay assignable to arrays
22
+ * typed with any context.
23
+ *
24
+ * @typeParam TContext - Shape of the optional per-call context forwarded to
25
+ * `extract`. Defaults to `unknown`.
16
26
  */
17
- export type ExtractionRule = {
27
+ export type ExtractionRule<TContext = unknown> = {
28
+ /**
29
+ * Stable identifier surfaced in `ExtractionResult.sources` when this rule
30
+ * produces the kept value. Optional: when omitted, `rule.apply` assigns
31
+ * `${field}#${declarationIndex}` based on the rule's position in the
32
+ * `rules` array.
33
+ */
34
+ id?: string;
18
35
  /** Name of the schema field this rule targets. */
19
36
  field: string;
20
- /** Inspects `content` and returns a match, or `null` if nothing was found. */
21
- extract: (content: string) => RuleMatch<unknown> | null;
37
+ /**
38
+ * Inspects `content` - and optionally a caller-provided `context` -
39
+ * and returns a match, or `null` if nothing was found. `context` is
40
+ * forwarded verbatim by `rule.apply` / `Extractor.extract` and left
41
+ * `undefined` when the caller passes no context.
42
+ */
43
+ extract: (content: string, context?: TContext) => RuleMatch<unknown> | null;
22
44
  };
23
45
  /**
24
46
  * The output of the rules pass. Contains the values that deterministic rules
@@ -32,6 +54,15 @@ export type RulesResult<T> = {
32
54
  values: Partial<T>;
33
55
  /** Confidence score per extracted field, in `[0, 1]`. */
34
56
  confidence: Partial<Record<keyof T, number>>;
57
+ /**
58
+ * Identifier of the winning rule per extracted field. Always populated by
59
+ * {@link rule.apply}: either the rule's declared `id` or
60
+ * `${field}#${declarationIndex}` from the source `rules` array. Optional
61
+ * on the type for back-compat with callers who build {@link RulesResult}
62
+ * by hand; consumers (including {@link merge.apply}) tolerate its absence
63
+ * and surface an empty `ruleId` in {@link FieldSource} when missing.
64
+ */
65
+ sourceIds?: Partial<Record<keyof T, string>>;
35
66
  /** Schema fields for which no rule produced a valid value. */
36
67
  missing: (keyof T)[];
37
68
  };
@@ -1 +1 @@
1
- {"version":3,"file":"rule.types.d.ts","sourceRoot":"","sources":["../../src/types/rule.types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,MAAM,SAAS,CAAC,CAAC,IAAI;IACzB,gDAAgD;IAChD,KAAK,EAAE,CAAC,CAAC;IACT,4EAA4E;IAC5E,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF;;;GAGG;AACH,MAAM,MAAM,cAAc,GAAG;IAC3B,kDAAkD;IAClD,KAAK,EAAE,MAAM,CAAC;IACd,8EAA8E;IAC9E,OAAO,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,SAAS,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC;CACzD,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,MAAM,WAAW,CAAC,CAAC,IAAI;IAC3B,mEAAmE;IACnE,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC;IACnB,yDAAyD;IACzD,UAAU,EAAE,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC;IAC7C,8DAA8D;IAC9D,OAAO,EAAE,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC;CACtB,CAAC"}
1
+ {"version":3,"file":"rule.types.d.ts","sourceRoot":"","sources":["../../src/types/rule.types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,MAAM,SAAS,CAAC,CAAC,IAAI;IACzB,gDAAgD;IAChD,KAAK,EAAE,CAAC,CAAC;IACT,4EAA4E;IAC5E,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF;;;;;;;;;;;;;GAaG;AACH,MAAM,MAAM,cAAc,CAAC,QAAQ,GAAG,OAAO,IAAI;IAC/C;;;;;OAKG;IACH,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,kDAAkD;IAClD,KAAK,EAAE,MAAM,CAAC;IACd;;;;;OAKG;IACH,OAAO,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,QAAQ,KAAK,SAAS,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC;CAC7E,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,MAAM,WAAW,CAAC,CAAC,IAAI;IAC3B,mEAAmE;IACnE,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC;IACnB,yDAAyD;IACzD,UAAU,EAAE,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC;IAC7C;;;;;;;OAOG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC;IAC7C,8DAA8D;IAC9D,OAAO,EAAE,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC;CACtB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "llmbic",
3
- "version": "1.1.0",
3
+ "version": "1.3.0",
4
4
  "description": "Hybrid data extraction — deterministic rules + LLM fallback, with per-field confidence scoring.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -30,6 +30,7 @@
30
30
  "typecheck": "tsc --noEmit",
31
31
  "example:ollama": "tsx examples/ollama.ts",
32
32
  "example:openai-batch": "tsx examples/openai-batch.ts",
33
+ "example:pii-redaction": "tsx examples/pii-redaction.ts",
33
34
  "prepublishOnly": "npm run typecheck && npm test && npm run build"
34
35
  },
35
36
  "peerDependencies": {