/** * @copyright Sister Software * @license AGPL-3.0 * @author Teffen Ellis, et al. * * Diff two parses of the SAME input, over spans rather than over the component map. * * The component map is what a comparison reaches for and it loses the thing you need. Two arms that both emit * `locality` tell you nothing about whether the locality MOVED, and a map keyed by tag cannot represent a span that * slid one token left — it looks identical to a span that was replaced. This is not hypothetical: it is how a * regression that turned * * Ye Three Lords, 27 Minories, London EC3N 1DE * * from `venue "Ye Three Lords" · locality London · street Minories` into `locality "Ye Three Lords"` reads as "the * locality changed" in a map diff, when what actually happened is that two spans were destroyed and a third was * retagged onto the text of one of them. * * So spans are matched by OVERLAP first and tag second, which lets the four events be told apart: * * - `retagged` — same text, different tag. The venue that became a locality. * - `moved` — same tag, different span. The locality that slid onto the neighbouring segment. * - `removed` / `added` — a span with no counterpart at all. * - `confidence` — same tag, same span, the model simply became more or less sure. * * That last one is why the confidence delta is carried per span rather than as a headline: a row that did not change * its answer but lost 0.3 of confidence on the deciding span is a row about to flip, and an aggregate cannot say so. */ import type { AddressTree } from "./types.ts"; /** * What happened to one span between the two arms. * * `unchanged` is emitted rather than dropped so a renderer can show context lines; a caller wanting only the changes * filters on {@linkcode isChange}. */ export type SpanDeltaKind = "added" | "removed" | "retagged" | "moved" | "confidence" | "unchanged"; /** * A span-level change, carrying both sides where both exist. */ export interface SpanDelta { kind: SpanDeltaKind; /** * The tag on each side. Equal unless `kind` is `retagged`, and one side is absent for `added`/`removed`. */ tagBefore?: string; tagAfter?: string; valueBefore?: string; valueAfter?: string; spanBefore?: [number, number]; spanAfter?: [number, number]; confidenceBefore?: number; confidenceAfter?: number; /** * `after - before`, present only when both sides are. Negative means the arm under test is LESS sure. */ confidenceDelta?: number; /** * Where the assertion came from — `rule`, `neural`, `resolver`. A span whose tag is unchanged but whose source moved * from `resolver` to `neural` lost its gazetteer backing, which no tag-level diff can show. */ sourceBefore?: string; sourceAfter?: string; sourceIDBefore?: string; sourceIDAfter?: string; } /** * Whether a delta represents an actual change, as opposed to a context line. */ export declare function isChange(delta: SpanDelta): boolean; /** * One input's parse on both sides, plus the span-level story of how they differ. */ export interface ParseDiff { input: string; spans: SpanDelta[]; /** * The locale/country call and how sure each arm was of it. A parse that changed nothing else but moved its country * confidence across the scope threshold will geocode somewhere else entirely. */ localeCountryBefore?: { country: string; confidence: number; }; localeCountryAfter?: { country: string; confidence: number; }; /** * True when no span changed and the locale call is identical — the arms agree on this input. */ identical: boolean; } /** * Diff two parses of the same input. * * Matching is greedy on overlap, strongest pair first, with tag equality breaking ties — so a span that kept its tag is * preferred over one that merely sits in the same place. */ export declare function diffParse(input: string, before: AddressTree | null | undefined, after: AddressTree | null | undefined, locale?: { before?: { country: string; confidence: number; }; after?: { country: string; confidence: number; }; }): ParseDiff; /** * Confidence movement below which a same-tag same-span pair is not worth a line of its own. * * Two hundredths: the decoder's aggregate is a mean over a span's tokens, so a one-token re-scoring moves a long span * by a hair and reporting that as a change buries the spans that actually moved. */ export declare const CONFIDENCE_NOISE_FLOOR = 0.02; /** * Render a diff the way a reader reads one — the ADDRESS first, then the spans that moved under it. * * Address-first is the point. An aggregate that reports "18 regressed" without the strings is the shape that let a * venue-destroying regression read as a routine count for five runs. */ export declare function renderParseDiff(diff: ParseDiff, options?: { context?: boolean; }): string; //# sourceMappingURL=parse-diff.d.ts.map