Documentation
¶
Overview ¶
Package compiler executes deterministic, reusable extraction rules against HTML documents.
Index ¶
- Constants
- Variables
- func Compile(ctx context.Context, samples []Sample, schema json.RawMessage, ...) (IR, ValidationReport, error)
- func Execute(ir IR, html string) (json.RawMessage, map[string]evidence.Anchor, error)
- func TemplateMedoid(fingerprints []uint64) (uint64, error)
- func ValidateCompileSchema(schema json.RawMessage) error
- type Candidate
- type CandidateSubmitter
- type CompileKey
- type Coordinator
- type Extractor
- type ExtractorState
- type FieldError
- type FieldRule
- type FieldValidation
- type HealResult
- type HealSchedule
- type HealSnapshotReader
- type HealState
- type HealWorker
- type HealWorkerOptions
- type Healer
- type HealerOption
- type IR
- type PageKey
- type ReplayResult
- type Sample
- type SampleCatalog
- type SampleRef
- type SampleSet
- type Store
- func (s *Store) Get(ctx context.Context, id string) (Extractor, error)
- func (s *Store) Lookup(ctx context.Context, key PageKey) (Extractor, bool, error)
- func (s *Store) RecordEmpty(ctx context.Context, key PageKey, id string) (Extractor, error)
- func (s *Store) RecordUse(ctx context.Context, key PageKey, id string, outcome UseOutcome) (Extractor, error)
- func (s *Store) RegisterActive(ctx context.Context, key PageKey, ir IR, report ValidationReport) (Extractor, error)
- func (s *Store) RegisterInitial(ctx context.Context, key PageKey, ir IR, report ValidationReport) (Extractor, error)
- func (s *Store) Retire(ctx context.Context, id string) (Extractor, error)
- func (s *Store) Save(ctx context.Context, key PageKey, ir IR, report ValidationReport) (Extractor, error)
- func (s *Store) SubmitCandidate(ctx context.Context, candidate Candidate) (Submission, error)
- func (s *Store) Touch(ctx context.Context, key PageKey, id string) (Extractor, error)
- type StoreOption
- type Submission
- type SubmissionStatus
- type TruthExtractor
- type TruthExtractorFunc
- type UseOutcome
- type ValidationReport
Constants ¶
const ( // DefaultCompilerProfile is the only content-cleaning profile whose // snapshots may enter the compiler catalog. Keeping the profile fixed avoids // learning one extractor from pages produced by incompatible cleaners. DefaultCompilerProfile = "extract-default-v1" SampleCatalogTTL = 30 * 24 * time.Hour MaxSampleCatalogEntries = 10_000 MaxSampleCatalogClusters = MaxTemplateClusters MaxSampleCatalogClusterEntries = MaxCompileSamples )
const ( MinCompileSamples = 3 MaxCompileSamples = 20 MaxCompileSchemaBytes = 512 << 10 MaxSampleContentBytes = 4 << 20 MaxCompileInputBytes = 32 << 20 MaxTruthScalarBytes = MaxAnchorQuoteBytes MaxCandidatesPerField = 256 MaxCompileCandidates = 4096 MaxDOMNodesPerSample = 100_000 MaxAttributesPerSample = 20_000 MaxDataAttrsPerNode = 8 MaxCompileTextIndexBytes = 16 << 20 MaxAlignmentScanBytes = 64 << 20 MaxTextMatchesPerValue = 256 ValidationThreshold = 0.9 )
const ( CoordinatorQueueSize = 32 CoordinatorTaskTimeout = 2 * time.Minute CoordinatorLeaseDuration = 3 * time.Minute CoordinatorTransientDelay = 15 * time.Minute CoordinatorWeakDelay = 24 * time.Hour CoordinatorAttemptTTL = 30 * 24 * time.Hour MaxCoordinatorAttemptRows = 10_000 )
const ( // CurrentIRVersion is the only extraction program version Execute accepts. CurrentIRVersion = 1 TypeString = "string" TypeNumber = "number" TypeBoolean = "boolean" TypeDate = "date" MaxHTMLBytes = 4 << 20 MaxFields = 100 MaxFieldNameBytes = 4 << 10 MaxSelectorBytes = 4 << 10 MaxAttributeBytes = 1 << 10 MaxRegexBytes = 4 << 10 MaxTransformsPerField = 16 MaxTransformNameBytes = 128 MaxRuleDefinitionBytes = 512 << 10 MaxAnchorQuoteBytes = 8 << 10 MaxOutputBytes = 4 << 20 )
const ( HealTaskTimeout = 2 * time.Minute HealLeaseDuration = 3 * time.Minute HealFinalizeTimeout = 5 * time.Second MaxHealHistoryScan = 1000 MaxHealReplayFacts = 100 MaxHealReplaySnapshots = 20 MaxHealSnapshotBytes = MaxHTMLBytes MaxHealSnapshotTotal = MaxHealReplaySnapshots * MaxHealSnapshotBytes )
const ( TemplateDistanceThreshold = 6 MaxTemplateClusters = 256 MaxExtractorWindow = 20 MaxExtractorReportBytes = 512 << 10 MaxExtractorURLBytes = 16 << 10 )
const DefaultHealWorkerPollInterval = time.Second
const MaxCandidateSamplesJSONBytes = 16 << 10
Variables ¶
var ( // ErrHealingRequired means an immutable cluster history already exists and // a candidate cannot be installed through the initial-registration path. ErrHealingRequired = errors.New("compiler: verified healing is required") // ErrCandidateAmbiguous means candidate samples attribute the proposed // revision to more than one durable extractor lineage. ErrCandidateAmbiguous = errors.New("compiler: candidate source is ambiguous") // ErrCandidateRetired means the only attributable lineage is terminal and // cannot be reactivated or silently replaced. ErrCandidateRetired = errors.New("compiler: candidate source is retired") )
var ( ErrInvalidSampleCatalog = errors.New("compiler: invalid sample catalog input") ErrSampleCatalogCorrupt = errors.New("compiler: sample catalog is corrupt") )
var ( ErrInvalidInput = errors.New("compiler: invalid compile input") ErrUnsupportedSchema = errors.New("compiler: unsupported schema") ErrTruth = errors.New("compiler: invalid truth extraction") ErrNoCandidate = errors.New("compiler: no extractor candidate") )
var ( ErrInvalidCoordinator = errors.New("compiler: invalid coordinator configuration") ErrCoordinatorClosed = errors.New("compiler: coordinator is closed") ErrCoordinatorQueueFull = errors.New("compiler: coordinator queue is full") ErrCoordinatorCapacity = errors.New("compiler: coordinator attempt capacity reached") ErrCoordinatorLeaseLost = errors.New("compiler: coordinator attempt lease lost") )
var ( ErrInvalidHealWorker = errors.New("compiler: invalid heal worker configuration") ErrHealWorkerClosed = errors.New("compiler: heal worker is closed") ErrInvalidHealSchedule = errors.New("compiler: invalid extractor heal schedule") ErrHealExtractorNotFound = errors.New("compiler: extractor for heal scheduling not found") ErrHealScheduleNotReady = errors.New("compiler: extractor heal schedule is not ready") ErrHealScheduleAmbiguous = errors.New("compiler: extractor heal schedule is ambiguous") )
var ( // ErrRequiredField is wrapped when a required rule cannot extract a value. ErrRequiredField = errors.New("compiler: required field missing") // ErrUnsupportedIRVersion is wrapped when Execute cannot safely interpret // the supplied extraction program version. ErrUnsupportedIRVersion = errors.New("compiler: unsupported IR version") // ErrResourceLimit is wrapped by every input, rule, and output size limit. ErrResourceLimit = errors.New("compiler: resource limit exceeded") )
var ( ErrInvalidHealer = errors.New("compiler: invalid healer configuration") ErrHealRunNotFound = errors.New("compiler: extractor heal run not found") ErrHealLeaseBusy = errors.New("compiler: extractor heal lease is busy") ErrHealLeaseLost = errors.New("compiler: extractor heal lease was lost") )
var ( ErrInvalidStoreInput = errors.New("compiler: invalid extractor store input") ErrExtractorNotFound = errors.New("compiler: extractor not found") ErrExtractorStore = errors.New("compiler: extractor store is corrupt") ErrClusterLimit = errors.New("compiler: extractor template cluster limit exceeded") ErrInvalidState = errors.New("compiler: invalid extractor state transition") )
Functions ¶
func Compile ¶
func Compile( ctx context.Context, samples []Sample, schema json.RawMessage, extractor TruthExtractor, ) (IR, ValidationReport, error)
Compile learns one deterministic IR from at least three snapshots. A weak extractor is still returned with CanEnable=false; malformed inputs, invalid truth, resource violations, and fields with no candidate fail atomically.
func Execute ¶
Execute parses html once and evaluates every field in ir against that one goquery document. Optional fields that do not match are omitted. Any invalid rule, conversion failure, or missing required field fails the execution atomically.
func TemplateMedoid ¶
TemplateMedoid chooses the order-independent sample with minimum total Hamming distance. Ties choose the numerically smallest fingerprint. Every sample must be within the public cluster threshold of the selected medoid.
func ValidateCompileSchema ¶
func ValidateCompileSchema(schema json.RawMessage) error
ValidateCompileSchema reports whether schema can be served by the deterministic compiler. It performs no extraction and makes no LLM calls.
Types ¶
type Candidate ¶
type Candidate struct {
Key CompileKey
CatalogRevision string
Schema json.RawMessage
IR IR
Validation ValidationReport
Samples []SampleRef
}
Candidate is one validated compiler result tied to the exact fixed catalog cluster and immutable sample revision that produced it. SubmitCandidate clones every byte and reference before beginning a durable transaction.
type CandidateSubmitter ¶
type CandidateSubmitter interface {
SubmitCandidate(context.Context, Candidate) (Submission, error)
}
CandidateSubmitter separates synthesis from publication. A successful call means the candidate decision is durable before the coordinator records a successful compiler attempt.
type CompileKey ¶
type CompileKey struct {
Host string
SchemaHash string
ContentProfile string
TemplateClusterID string
// ClusterSimHash is the immutable representative persisted by Store.Save.
// Sample fingerprints validate membership but never recenter this value.
ClusterSimHash uint64
}
CompileKey identifies one fixed template cluster. Schema bytes are supplied by the current extract request and are intentionally not recoverable from the catalog after restart.
type Coordinator ¶
type Coordinator struct {
// contains filtered or unexported fields
}
Coordinator asynchronously turns bounded catalog references into durably admitted deterministic candidates. Only a brand-new logical cluster becomes active here; every stale lineage enters verified healing without replacement. It never accepts provider or request credentials; its TruthExtractor is a process-owned managed dependency supplied at startup.
func NewCoordinator ¶
func NewCoordinator( catalog *SampleCatalog, registry *Store, snapshots *snapshot.Store, extractor TruthExtractor, clocks ...func() time.Time, ) (*Coordinator, error)
NewCoordinator starts one background compiler worker. An optional clock is accepted for deterministic embedding and tests; all other limits are fixed.
func (*Coordinator) Close ¶
func (c *Coordinator) Close() error
Close cancels running work, drops queued work, and waits for the sole worker. It is idempotent and rejects every later Observe call.
func (*Coordinator) Observe ¶
func (c *Coordinator) Observe( ctx context.Context, page PageKey, snapshotID string, fetchedAt time.Time, canonicalSchema json.RawMessage, ) error
Observe records a successful snapshot and non-blockingly schedules a ready fixed template cluster. Request cancellation affects only catalog admission; accepted work runs under the coordinator lifecycle.
type Extractor ¶
type Extractor struct {
ID string
Host string
Schema json.RawMessage
SchemaHash string
TemplateClusterID string
TemplateSimHash uint64
IR IR
IRHash string
Version int
Validation ValidationReport
State ExtractorState
EmptyWindow []UseOutcome
StaleReason string
CreatedAt time.Time
LastUsedAt *time.Time
UpdatedAt time.Time
}
Extractor is one immutable compiled revision plus its monotonic health state. EmptyWindow is returned as a copy.
type ExtractorState ¶
type ExtractorState string
ExtractorState is monotonic durable lifecycle state.
const ( StateActive ExtractorState = "active" StateStale ExtractorState = "stale" StateRetired ExtractorState = "retired" )
type FieldError ¶
FieldError identifies the rule and processing stage that failed.
func (*FieldError) Error ¶
func (e *FieldError) Error() string
func (*FieldError) Unwrap ¶
func (e *FieldError) Unwrap() error
type FieldRule ¶
type FieldRule struct {
Name string `json:"name"`
Selector string `json:"selector"`
Attr string `json:"attr,omitempty"`
Regex string `json:"regex,omitempty"`
Transforms []string `json:"transforms,omitempty"`
Type string `json:"type"`
Required bool `json:"required,omitempty"`
}
FieldRule describes how to extract and convert one output field. Selector uses CSS syntax. Attr being empty selects element text. Regex, when set, contributes capture group 1 rather than the complete match.
type FieldValidation ¶
type FieldValidation struct {
Name string `json:"name"`
Matches int `json:"matches"`
Samples int `json:"samples"`
Score float64 `json:"score"`
}
FieldValidation reports atomic full-program agreement for one field.
type HealResult ¶
type HealResult struct {
ID string
SourceExtractorID string
SourceVersion int
Key CompileKey
State HealState
TerminalReason string
ReplayTotal int
ReplayMatched int
ReplayRatio *float64
PromotedExtractorID string
CreatedAt time.Time
UpdatedAt time.Time
CompletedAt *time.Time
}
HealResult is the bounded public view of one durable replay. Candidate IR, schemas, samples, webhook configuration, and storage errors are deliberately absent so lifecycle reporting cannot disclose persisted extraction inputs.
type HealSchedule ¶
HealSchedule is the stable ticket returned when a durable run is already pending or replaying for one exact source extractor revision.
type HealSnapshotReader ¶
type HealSnapshotReader interface {
Content(snapshot.ID, int) ([]byte, error)
HasObservationContext(context.Context, snapshot.ID, func(snapshot.Meta) bool) (bool, error)
}
HealSnapshotReader is the exact immutable-content and provenance surface a Healer needs. snapshot.Store satisfies it directly.
type HealState ¶
type HealState string
HealState is the durable state machine owned by extractor_heal_runs.
type HealWorker ¶
type HealWorker struct {
// contains filtered or unexported fields
}
HealWorker owns one durable polling goroutine. The wake channel is only a coalesced hint: run identity and lifecycle always come from the ledger.
func NewHealWorker ¶
func NewHealWorker( parent context.Context, healer *Healer, options HealWorkerOptions, ) (*HealWorker, error)
NewHealWorker starts exactly one durable replay worker. Snapshot reads and Healer replay always happen after the polling read transaction has ended.
func (*HealWorker) Close ¶
func (w *HealWorker) Close() error
Close cancels polling and replay, waits for accepted ScheduleExtractor calls, and rejects all later scheduling. It is safe to call repeatedly.
func (*HealWorker) ScheduleExtractor ¶
func (w *HealWorker) ScheduleExtractor(ctx context.Context, extractorID string) (HealSchedule, error)
ScheduleExtractor resolves an actionable durable run using the exact (source_extractor_id, source_version) stored on the current extractor row. A live replay lease is already accepted and returns the same ticket. No CompileKey is inferred from an extractor identifier.
type HealWorkerOptions ¶
HealWorkerOptions controls polling and logging. Zero values select fixed production defaults; work and concurrency limits are not configurable.
type Healer ¶
type Healer struct {
// contains filtered or unexported fields
}
Healer claims durable candidates, replays exact confirmed source history, and promotes a passing revision in one serialized ledger transaction.
func NewHealer ¶
func NewHealer(registry *Store, snapshots HealSnapshotReader, options ...HealerOption) (*Healer, error)
NewHealer constructs the transport-neutral self-heal core. It starts no worker and owns no request credentials.
func (*Healer) Heal ¶
func (h *Healer) Heal(ctx context.Context, key CompileKey) (HealResult, error)
Heal claims the exact active run for key. If no pending or replaying row remains, the latest terminal row for the complete fixed key is returned idempotently. Histories for a nearby or different profile never match.
type HealerOption ¶
type HealerOption func(*healerConfig) error
HealerOption configures process-owned healing behavior.
func WithHealWebhook ¶
func WithHealWebhook(rawURL, secret string) HealerOption
WithHealWebhook configures an optional process-owned lifecycle destination. It accepts no per-request override. Literal private/local destinations and userinfo are rejected through the same public-network URL policy used by outbound fetches; errors never echo the supplied URL or secret.
type PageKey ¶
type PageKey struct {
URL string
PageHash string
Host string
Schema json.RawMessage
SchemaHash string
TemplateSimHash uint64
}
PageKey contains only canonical, bounded values suitable for persistence. TemplateSimHash is encoded as an unsigned big-endian BLOB8 in SQLite.
func BuildPageKey ¶
BuildPageKey canonicalizes the URL, reduces a DNS host to eTLD+1 (literal IPs remain literal), canonicalizes the schema JSON, and fingerprints the DOM.
type ReplayResult ¶
type ReplayResult struct {
Value json.RawMessage
Anchor evidence.Anchor
Found bool
}
ReplayResult is the exact result of evaluating one immutable field rule. Found is false only when the selector, attribute, or regular expression did not match. Value and Anchor are populated together for a successful replay.
func ReplayField ¶
func ReplayField(ir IR, fieldName, html string) (ReplayResult, error)
ReplayField evaluates one named rule with the same selector, attribute, regular-expression, transform, type-conversion, and SingleMatcher semantics as Execute. Unlike Execute, a missing required field is reported as Found=false so a verifier can distinguish a genuinely gone field from a corrupt or otherwise non-executable revision.
type Sample ¶
Sample keeps the cleaned content shown to the truth extractor together with the raw snapshot used to learn and validate deterministic selectors.
type SampleCatalog ¶
type SampleCatalog struct {
// contains filtered or unexported fields
}
SampleCatalog stores only bounded references into snapshot CAS.
func NewSampleCatalog ¶
NewSampleCatalog builds a catalog over an already-open ledger. An optional clock is accepted for deterministic embedding and tests.
func (*SampleCatalog) Load ¶
func (c *SampleCatalog) Load(ctx context.Context, key CompileKey, limit int) (SampleSet, bool, error)
Load returns the newest limit samples selected deterministically, then orders those references by stable identity. limit must be 3..20. Expired references are physically removed before the read.
func (*SampleCatalog) Observe ¶
func (c *SampleCatalog) Observe( ctx context.Context, page PageKey, snapshotID string, fetchedAt time.Time, ) (SampleSet, bool, error)
Observe records one successful default-profile snapshot. Expiration, per-page convergence, clustering, and every capacity bound are enforced in the same serialized write transaction. Repeated content may occupy multiple page rows, but Load counts each snapshot identity at most once. ready is true only for at least three distinct page hashes backed by three distinct snapshot IDs.
type SampleRef ¶
type SampleRef struct {
PageHash string
SnapshotID string
SampleSimHash uint64
FetchedAt time.Time
}
SampleRef is a bounded reference to an immutable snapshot blob. Source URLs, page bytes, schemas, model output, credentials, and request metadata are deliberately absent from this type and from the durable catalog.
type SampleSet ¶
type SampleSet struct {
Key CompileKey
Revision string
Count int
Samples []SampleRef
}
SampleSet is a deterministic, caller-owned selection. Revision hashes the ordered page/snapshot identities and their sample fingerprints, but not observation timestamps, so refreshing identical content does not schedule a redundant compilation.
type Store ¶
type Store struct {
// contains filtered or unexported fields
}
Store persists compiled extractors through ledger's narrow transaction API.
func NewStore ¶
func NewStore(durable *ledger.Store, options ...StoreOption) (*Store, error)
NewStore builds a repository over an already-open ledger.
func (*Store) Lookup ¶
Lookup returns the nearest active extractor within distance six. Selection is phase one of a two-phase use protocol: a hit is never bound here because execution has not succeeded yet. The caller must follow a successful or required-empty execution with Touch or RecordEmpty; NotExecuted is a strict zero-write outcome. A generic cache miss never changes extractor state. A previously-bound page that no longer matches any active cluster atomically marks only its bound active extractor stale; all active candidates are re-read in that transaction immediately before the compare-and-swap.
func (*Store) RecordEmpty ¶
RecordEmpty binds page and records an execution whose required output was empty. Binding is transactional with the health update.
func (*Store) RecordUse ¶
func (s *Store) RecordUse(ctx context.Context, key PageKey, id string, outcome UseOutcome) (Extractor, error)
RecordUse is phase two of Lookup. Succeeded and RequiredEmpty atomically bind the page and update the bounded 20-result health window. Drift is evaluated only once the window is full: six empty results remain active; seven mark the extractor stale. NotExecuted performs no write and changes neither binding, window, nor last_used_at.
func (*Store) RegisterActive ¶
func (s *Store) RegisterActive(ctx context.Context, key PageKey, ir IR, report ValidationReport) (Extractor, error)
RegisterActive is the legacy explicit spelling. Active replacement is no longer admitted through this API; verified healing owns every promotion.
func (*Store) RegisterInitial ¶
func (s *Store) RegisterInitial(ctx context.Context, key PageKey, ir IR, report ValidationReport) (Extractor, error)
RegisterInitial installs version one for one exact fixed template cluster. Exact retries return the current row. Once any revision or attributable page binding exists, this path never replaces, reactivates, or recenters it; callers must enter the verified healing workflow instead.
func (*Store) Retire ¶
Retire performs the only explicit terminal transition. A retired extractor is never reactivated, and initial registration cannot append to its lineage.
func (*Store) Save ¶
func (s *Store) Save(ctx context.Context, key PageKey, ir IR, report ValidationReport) (Extractor, error)
Save is retained for callers compiled against the Phase 2 API. It now has the same initial-only safety semantics as RegisterInitial.
func (*Store) SubmitCandidate ¶
SubmitCandidate admits a compiler result without ever replacing an active revision. A brand-new exact cluster is registered as v1; an attributable stale lineage creates an immutable pending heal run; active, retired, and ambiguous histories remain unchanged.
type StoreOption ¶
type StoreOption func(*storeConfig) error
StoreOption configures an extractor repository.
func WithStoreClock ¶
func WithStoreClock(clock func() time.Time) StoreOption
WithStoreClock supplies a deterministic UTC clock for tests and embedding.
type Submission ¶
type Submission struct {
Status SubmissionStatus
Extractor Extractor
HealRunID string
SourceExtractorID string
SourceVersion int
}
Submission identifies either the active extractor retained by admission or the durable pending heal run. Rejected submissions return a stable status together with ErrHealingRequired.
type SubmissionStatus ¶
type SubmissionStatus string
SubmissionStatus makes every non-destructive admission decision explicit.
const ( SubmissionInitialActive SubmissionStatus = "initial_active" SubmissionCurrent SubmissionStatus = "current" SubmissionActiveUnchanged SubmissionStatus = "active_unchanged" SubmissionPendingHeal SubmissionStatus = "pending_heal" SubmissionRejectedRetired SubmissionStatus = "rejected_retired" SubmissionRejectedAmbiguous SubmissionStatus = "rejected_ambiguous" )
type TruthExtractor ¶
type TruthExtractor interface {
ExtractTruth(context.Context, string, json.RawMessage) (json.RawMessage, error)
}
TruthExtractor is the transport-neutral compile-time LLM boundary. API keys, provider configuration, repair policy, and HTTP all belong in the adapter supplied by the caller.
type TruthExtractorFunc ¶
type TruthExtractorFunc func(context.Context, string, json.RawMessage) (json.RawMessage, error)
TruthExtractorFunc adapts a function to TruthExtractor.
func (TruthExtractorFunc) ExtractTruth ¶
func (extract TruthExtractorFunc) ExtractTruth(ctx context.Context, content string, schema json.RawMessage) (json.RawMessage, error)
type UseOutcome ¶
type UseOutcome string
UseOutcome feeds the bounded drift window. NotExecuted deliberately does not enter the window because it says nothing about extractor health.
const ( UseSucceeded UseOutcome = "succeeded" UseRequiredEmpty UseOutcome = "required_empty" UseNotExecuted UseOutcome = "not_executed" )
type ValidationReport ¶
type ValidationReport struct {
PerField []FieldValidation `json:"per_field"`
Overall float64 `json:"overall"`
Samples int `json:"samples"`
ValidSamples int `json:"valid_samples"`
Threshold float64 `json:"threshold"`
CanEnable bool `json:"can_enable"`
}
ValidationReport is safe to publish with an extractor. CanEnable is true only when every sample produced schema-valid JSON and overall agreement met the public threshold.