Skip to main content

internal/extract/extract.go

internal/extract · 336 lines · 11 declarations · source

This file carries the package documentation, rendered on the package page.

Declarations​

type Proposal​

type Proposal struct {
Subject string
Predicate string
Object string
Statement string
Confidence float32
SubjectType string
ObjectType string
// Quote is what the model says it read. It is treated as a SEARCH TERM, never as evidence:
// what is stored is the text found in the message, which is the only string a byte span can
// honestly index.
Quote string
// Polarity is what the message does with the relation: states it, denies it, hedges it, supposes
// it, or reports somebody else saying it. Only the first becomes a fact.
//
// An empty value is not "asserted". It means the model did not answer, and a claim nobody has
// decided the polarity of is refused — see the reasoning at the refusal site.
Polarity domain.Polarity
// Tense is when the message puts the relation: now, over, or not yet. Only the first becomes a
// fact, and an empty value is refused for the same reason an empty polarity is.
//
// Separate from polarity because they are independent and both have to be right: "I used to live
// in Amman" is asserted, positively, about a period that is over.
Tense domain.Tense
}

Proposal is what a model returns, before anything has been checked.

It is deliberately not a domain.Claim. A Claim carries a byte span, and a span this package has not verified is the defect this package exists to prevent — so the model is not given a type it could put one in.

source

type Model​

type Model interface {
Propose(ctx context.Context, message domain.Message, vocabulary domain.Ontology) ([]Proposal, error)
}

Model proposes claims for one message.

The vocabulary is passed in rather than held by the implementation, because it is a per-tenant table and an implementation holding its own copy is a second definition of a closed set.

source

type Result​

type Result struct {
Claims []domain.Claim
// Rejected is not an error. Most messages contain no assertable claim at all, and a message
// whose every proposal was rejected is the ordinary case rather than a failure.
Rejected []domain.RejectedClaim
}

Result is everything one message produced.

source

type Extractor​

type Extractor struct {
model Model
vocabulary domain.Ontology
// unresolvable are terms that cannot be a subject, from the table that holds them. Empty is
// valid and means the check does nothing — a deployment that has not seeded the table gets the
// behaviour it had before, rather than a refusal it cannot explain.
unresolvable map[string]struct{}
}

Extractor applies the vocabulary and the span rule to a model's proposals.

source

func New​

func New(model Model, vocabulary domain.Ontology) *Extractor

New builds an extractor over a model and a tenant's vocabulary.

source

func NewWith​

func NewWith(model Model, v domain.Vocabulary) *Extractor

NewWith builds an extractor over both closed sets: the relations it may admit, and the terms that cannot be a subject.

source

method Extractor.Extract​

func (e *Extractor) Extract(ctx context.Context, message domain.Message) (Result, error)

Extract returns the claims one message supports.

The span is the whole point​

Every returned claim satisfies `message.Content[ByteStart:ByteEnd] == Quote`, and it satisfies it by construction rather than by the model's cooperation: the quote is located IN THE MESSAGE and the stored quote is the text that was found there. A model that paraphrased has produced a claim with no locatable quote, and that claim is not a weaker claim — it is an uncitable one, in a product whose argument is citation.

An empty result is not an error​

Most messages assert nothing. "Thanks, that worked" produces no claims, and a caller that has to distinguish that from a failure will end up treating failures as ordinary.

source

func reject​

func reject(p Proposal, reason string, ordinal int) domain.RejectedClaim

source

func Locate​

func Locate(content, quote string) (start, end int, found bool)

Locate finds a quote in a message and returns its byte span, or reports that it is not there.

Exact first, then whitespace-tolerant, and nothing else​

A model copying text out of a message routinely changes its whitespace: a line break inside a sentence comes back as a space, and indentation disappears. That difference carries no meaning — the words are the same words — so refusing it would discard correct citations for a formatting artefact.

Nothing else is tolerated, and case in particular is not. A model that changed the case has retyped rather than copied, and once retyping is admitted there is no line left to draw: a tolerance wide enough to match "moved to dublin" against "moved to Dublin" is wide enough to match a sentence the model composed.

The span is mapped back to the ORIGINAL bytes​

Matching happens against a whitespace-normalised copy, but the span returned indexes the message as it was stored. A span into a normalised string that exists nowhere is precisely the failure this package exists to prevent, arrived at from a different direction.

The first occurrence wins​

A quote appearing twice in one message is the same words in both places, so a citation resolves to the same text either way; only the instance differs, and nothing reads the instance. Refusing repeats instead would discard a claim for being quoted about something mentioned twice.

source

type normalised​

type normalised struct {
text string
start []int
end []int
}

normalised is a whitespace-collapsed copy of a string together with, for each of its bytes, where that byte came from in the original.

Two parallel slices rather than one, because a match needs the START of its first byte and the END of its last, and for a multi-byte rune those are not one number apart.

source

func normalise​

func normalise(s string) normalised

normalise collapses every run of whitespace to a single space and drops it at both ends.

Runs collapse rather than disappear, because removing whitespace entirely would match "works at" against "worksat" and, worse, would join two words across a line break into one that appears in neither.

source