Documentation
¶
Overview ¶
Package review owns a topic's review queue, its labels (human verdicts, including imported ones) and calibration with a holdout (spec §12).
Index ¶
- Constants
- Variables
- func AppendPreview(topicRoot string, p Preview) error
- func DevBucket(subject string) bool
- func ImportLinks(vaultPath, topicRoot string, c *corpus.Corpus) (int, error)
- func ItemID(queue, subject, target, question string) string
- func NormalizeURL(raw string) string
- func RenderCalibration(w io.Writer, report Report) error
- func RenderList(w io.Writer, view ListView) error
- func WriteCalibration(topicRoot string, report Report) error
- type CalibrateOptions
- type ImportOptions
- type ImportReport
- type ImportedPart
- type Item
- type Label
- type ListItem
- type ListView
- type Metrics
- type Preview
- type PurposeReport
- type Report
- type Store
- func (s *Store) Add(item Item) (bool, error)
- func (s *Store) AddLabel(label Label) error
- func (s *Store) Dir() string
- func (s *Store) Get(id string) (Item, bool, error)
- func (s *Store) Items(queue, status string) ([]Item, error)
- func (s *Store) Labels() ([]Label, error)
- func (s *Store) Pending(queue string) ([]Item, error)
- func (s *Store) PendingCounts() (map[string]int, error)
- func (s *Store) Resolve(id, status string) (Item, error)
- type SweepPoint
Constants ¶
const ( // MinDevLabels is the dev floor per purpose. MinDevLabels = 30 // MinHoldoutLabels is the holdout floor per purpose. MinHoldoutLabels = 10 // TargetPrecision is the dev precision the sweep looks for. TargetPrecision = 0.90 )
Calibration floors and targets (spec §12.3).
const ( MatchURL = "url" MatchPath = "path" MatchDOI = "doi" MatchPMCID = "pmcid" )
Match keys accepted by ImportLabels.
const ( PurposeRelevance = string(decisions.PurposeRelevance) QuestionRole = "role" PurposeLink = string(decisions.PurposeLink) QuestionShouldLink = "should_link" PurposeQuality = string(decisions.PurposeQuality) )
Label identities (purpose and question ids) used by imports and calibration.
const ( QueueGate = "gate" QueueSkip = "skip" QueueLink = "link" QueueContradiction = "contradiction" QueueConceptProposal = "concept-proposal" QueueOKFType = "okf-type" QueueRecapture = "recapture" QueueRemove = "remove" )
Queue names (spec §12.1).
const ( StatusPending = "pending" StatusAccepted = "accepted" StatusRejected = "rejected" )
Item statuses.
const ( QueueFile = "review.jsonl" LabelsFile = "labels.jsonl" )
Files under <topic>/.decisions/.
const ( VerdictPositive = "positive" VerdictNegative = "negative" OriginReview = "review" OriginImportLinks = "import-links" )
Label verdicts and origins.
const CalibrationFile = "calibration.json"
CalibrationFile is <topic>/.decisions/calibration.json. The JSON shape is the one internal/session reads (session.Calibration); it is mirrored here because session imports review.
const InsertedLinksFile = "inserted-links.jsonl"
InsertedLinksFile is the log of body links inserted by `kb link`, inside <topic>/.decisions/. Rows: {time, subject, target, text, mode}.
const OriginImportPrefix = "import:"
OriginImportPrefix prefixes the origin of labels imported from a file.
const PreviewsFile = "previews.jsonl"
PreviewsFile is the impact-preview ledger inside <topic>/.decisions/.
Variables ¶
var DefaultKeepValues = []string{"include", "true"}
DefaultKeepValues are the decision values read as "keep" when ImportOptions.KeepValues is empty.
var ErrNotEnoughLabels = errors.New("review: not enough labels; nothing written")
ErrNotEnoughLabels is returned by WriteCalibration when no purpose has a recommendation.
var Queues = []string{QueueGate, QueueSkip, QueueRecapture, QueueRemove, QueueLink, QueueContradiction, QueueConceptProposal, QueueOKFType}
Queues lists every queue in display order.
Functions ¶
func AppendPreview ¶
AppendPreview appends p to previews.jsonl; an empty Time is set to now.
func DevBucket ¶
DevBucket reports whether subject falls in the dev split: the first byte of sha256(subject) mod 10 is 0–6.
func ImportLinks ¶
ImportLinks turns the existing links between topic documents into positive `link` labels (question `should_link`, subject = linking document, target = linked document, origin `import-links`), which gives calibration a gold set (spec §12.2). Counted: body wikilinks and markdown links plus the `sources` and relation frontmatter lists. Excluded: links kb inserted (inserted-links.jsonl rows not in shadow mode) and relation lists kb wrote that nobody edited since (state row `written` hash equals the current value hash). Links to quarantined or non-corpus files and self links are ignored. A subject/target pair that already has a link label is skipped, so re-running writes nothing new. Returns the number of labels written.
func NormalizeURL ¶
NormalizeURL reduces a URL to a comparison key: scheme, `www.`, default ports, fragment, trailing slash and utm_* parameters are dropped, the host is lower-cased and the remaining query parameters are sorted.
func RenderCalibration ¶
RenderCalibration writes a calibration report: one block per purpose with label counts, metrics at the current and chosen thresholds on dev and holdout, imported-label metrics and the dev sweep.
func RenderList ¶
RenderList writes the queue counts and the pending items as tables.
func WriteCalibration ¶
WriteCalibration stores every recommended threshold in topic.yaml `decisions.thresholds` (other thresholds are kept) and records the calibration of those purposes in <topic>/.decisions/calibration.json (entries of other purposes are kept). Without any recommendation nothing is written and ErrNotEnoughLabels is returned.
Types ¶
type CalibrateOptions ¶
type CalibrateOptions struct {
// ContractHash is the active contract hash. Only receipts judged under
// it are joined, and labels shown in a preview made under another
// contract are forced to dev.
ContractHash string
// Model is the active decision model; only receipts it produced are
// joined.
Model string
// Thresholds are the current thresholds; nil means the defaults merged
// with topic.yaml `decisions.thresholds`.
Thresholds decisions.Thresholds
// Now defaults to time.Now.
Now func() time.Time
}
CalibrateOptions configures Calibrate.
type ImportOptions ¶
type ImportOptions struct {
// From is the JSONL, JSON (array of objects) or CSV file to import.
From string
// IDField is the column holding the source identifier.
IDField string
// Match is how IDField is matched to a topic source: url|path|doi|pmcid.
Match string
// DecisionField is the column holding the screening decision.
DecisionField string
// KeepValues are the decision values meaning keep/relevant
// (case-insensitive); every other value is a negative label.
KeepValues []string
// DecidedByField names the column copied to Label.DecidedBy; when empty
// `screening_stage` or `decided_by` are used when present.
DecidedByField string
// Corpus is the loaded topic; loaded from the topic root when nil.
Corpus *corpus.Corpus
// Now defaults to time.Now.
Now func() time.Time
}
ImportOptions configures ImportLabels.
type ImportReport ¶
type ImportReport struct {
// Rows is the number of data rows read.
Rows int
// Matched counts rows matched to a topic source.
Matched int
// Unmatched counts rows with no matching source.
Unmatched int
// UnmatchedIDs lists the identifiers of unmatched rows in file order.
UnmatchedIDs []string
// Positive and Negative count the labels written by this import.
Positive int
Negative int
// Duplicates counts matched rows already imported from the same file.
Duplicates int
// Origin is the origin recorded on every label.
Origin string
}
ImportReport summarizes an import.
func ImportLabels ¶
func ImportLabels(topicRoot string, opts ImportOptions) (ImportReport, error)
ImportLabels imports relevance labels from a topic's own screening or curation file (spec §12.2). Each row is matched to a source by the chosen key; a decision in KeepValues is a positive (keep) label, anything else a negative one, with purpose `relevance`, question `role` and origin `import:<file base name>@<sha256 of the file>`. Re-importing the same file writes nothing new; when one subject appears on several rows the last row wins.
type ImportedPart ¶
ImportedPart scores imported labels alone, at the chosen threshold (or the current one when there is no recommendation).
type Item ¶
type Item struct {
ID string `json:"id"`
Queue string `json:"queue"`
Purpose string `json:"purpose"`
Subject string `json:"subject"`
Target string `json:"target,omitempty"`
Question string `json:"question,omitempty"`
Probability float64 `json:"probability"`
ReceiptKey string `json:"receipt_key,omitempty"`
Evidence string `json:"evidence,omitempty"`
Action map[string]any `json:"action,omitempty"`
Status string `json:"status"`
Created string `json:"created"`
Resolved string `json:"resolved,omitempty"`
}
Item is one review-queue entry. Status changes are appended as new rows with the same ID; readers keep the last row per ID.
type Label ¶
type Label struct {
Time string `json:"time"`
Subject string `json:"subject"`
Purpose string `json:"purpose"`
Question string `json:"question,omitempty"`
Target string `json:"target,omitempty"`
Verdict string `json:"verdict"` // positive | negative
ReceiptKey string `json:"receipt_key,omitempty"`
Origin string `json:"origin"` // review | import:<file>@<sha256> | import-links
DecidedBy string `json:"decided_by,omitempty"`
ItemID string `json:"item_id,omitempty"`
}
Label is a human verdict on a decision (spec §12.2).
func LoadLabels ¶
LoadLabels reads <topic>/.decisions/labels.jsonl (missing → empty).
type ListView ¶
type ListView struct {
// Counts are the pending items per queue.
Counts map[string]int `json:"counts"`
// Items are the pending items shown.
Items []ListItem `json:"items"`
}
ListView is the data behind `kb review list`.
type Metrics ¶
type Metrics struct {
// Total is the number of labels in the split.
Total int `json:"total"`
// N is the number of labels joined to a judged gate quantity.
N int `json:"n"`
// Predicted counts joined labels at or above the apply threshold.
Predicted int `json:"predicted"`
// TruePositive counts predicted labels whose verdict is the target.
TruePositive int `json:"true_positive"`
// Precision = TruePositive / Predicted.
Precision float64 `json:"precision"`
// Recall = TruePositive / joined labels whose verdict is the target.
Recall float64 `json:"recall"`
// Coverage = N / Total: the share of labels the model judged.
Coverage float64 `json:"coverage"`
// ReviewRate = joined labels in [review, apply) / N.
ReviewRate float64 `json:"review_rate"`
}
Metrics are the scores of one split at one threshold. Labels without a judged gate quantity count in Total only.
type Preview ¶
type Preview struct {
Time string `json:"time"`
Contract string `json:"contract"`
Subjects []string `json:"subjects"`
}
Preview records the documents a contract impact preview showed (or used to estimate precision) under one contract hash. Calibration demotes the labels of those subjects to dev once the contract changes (spec §12.3).
func LoadPreviews ¶
LoadPreviews reads previews.jsonl (missing → empty; malformed lines are skipped).
type PurposeReport ¶
type PurposeReport struct {
Purpose string `json:"purpose"`
Quantity string `json:"quantity"`
// Banks maps the built-in bank asking the gate quantity to the version
// scores were joined under (with Report.Contract and Report.Model, the
// decision context of this calibration).
Banks map[string]string `json:"banks"`
// ApplyName and ReviewName are the threshold names calibrated.
ApplyName string `json:"apply_name"`
ReviewName string `json:"review_name"`
Current float64 `json:"current"`
Review float64 `json:"review"`
// DevLabels and HoldoutLabels count joined labels per split (imported
// ones included); ImportedLabels counts the imported ones among them.
DevLabels int `json:"dev_labels"`
HoldoutLabels int `json:"holdout_labels"`
ImportedLabels int `json:"imported_labels"`
// ReviewDevLabels counts the joined dev labels given in `kb review`
// (not imported); the dev floor must be met by these alone (spec §12.2:
// imported labels are never the only evidence behind an apply).
ReviewDevLabels int `json:"review_dev_labels"`
// ImportedOnly flags a purpose whose dev floor is met only with imported
// labels: metrics are reported, no threshold is recommended.
ImportedOnly bool `json:"imported_only,omitempty"`
// Demoted counts labels forced to dev by a preview under another
// contract.
Demoted int `json:"demoted"`
// Unjoined counts labels with no judged gate quantity.
Unjoined int `json:"unjoined"`
// Recommended reports whether Chosen is a recommendation.
Recommended bool `json:"recommended"`
Chosen float64 `json:"chosen,omitempty"`
// Note explains a missing recommendation ("not enough labels", ...).
Note string `json:"note,omitempty"`
DevCurrent Metrics `json:"dev_current"`
HoldoutCurrent Metrics `json:"holdout_current"`
DevChosen Metrics `json:"dev_chosen"`
HoldoutChosen Metrics `json:"holdout_chosen"`
Imported ImportedPart `json:"imported"`
Sweep []SweepPoint `json:"sweep"`
}
PurposeReport is the calibration of one purpose.
type Report ¶
type Report struct {
Time string `json:"time"`
Contract string `json:"contract"`
Model string `json:"model"`
Purposes []PurposeReport `json:"purposes"`
}
Report is a calibration report over every calibrated purpose.
func Calibrate ¶
func Calibrate(topicRoot string, opts CalibrateOptions) (Report, error)
Calibrate measures, per purpose, precision, recall, coverage and review rate at the current thresholds and over a sweep, from labels only (spec §12.3). Each label is joined to a decided receipt of the active decision context only: judged under opts.ContractHash, by opts.Model, with the current version of the purpose's built-in bank. A receipt of another context scores different questions and is never joined; such labels count as unjoined until current scores exist. Relevance and quality labels use Label.ReceiptKey when that receipt is compatible and carries the needed answer, else the latest compatible receipt for the same subject. Link labels join only through their producing evidence: the `should_link_<id>` question id and receipt key of the label or of its review item (candidate ids are positional, so another receipt of the subject may ask the same id about a different target). Labels are split by the first byte of sha256(subject) mod 10: buckets 0–6 are dev, 7–9 holdout; subjects that appeared in a preview under another contract are forced to dev. Thresholds 0.50..0.95 (step 0.05) are swept on dev only: the lowest threshold with dev precision ≥ 0.90 wins, else the best F0.5. Below 30 dev or 10 holdout joined labels a purpose gets "not enough labels" and no recommendation; when the dev floor is met only with imported labels (fewer than 30 dev labels given in `kb review`) the purpose is flagged "imported labels only" and gets no recommendation either (spec §12.2, §20).
type Store ¶
type Store struct {
// contains filtered or unexported fields
}
Store is a topic's review queue and label log. Safe for concurrent use.
func (*Store) Add ¶
Add enqueues an item as pending. An item whose ID already exists (pending or resolved) is left alone, so a verdict is never re-asked; returns whether a row was written.
func (*Store) Items ¶
Items returns every item (latest row per id) in insertion order, filtered by queue ("" = all) and status ("" = all).
func (*Store) Pending ¶
Pending returns pending items of one queue ("" = all), highest probability first.
func (*Store) PendingCounts ¶
PendingCounts counts pending items per queue.
type SweepPoint ¶
SweepPoint is the dev score at one candidate threshold.