/** * Retrieval over a caller-supplied document corpus, plus the example builder * that `generate-example` uses. * * WHY A CORPUS THE CALLER PASSES IN: an "assistant" that answers from a model's * own weights cannot show where an answer came from, and this package has no * model. Everything here is lexical and every answer is a span of text the * caller handed us, carrying the id of the document it was quoted from. That is * the difference between an answer and a guess that looks like one. * * SCORING, STATED ONCE SO NO CALLER HAS TO GUESS: a document and a question are * each reduced to the SET of their content terms (`tokenize` drops stop words * and one-character tokens). Each term is weighted by its inverse document * frequency over the corpus, ln(1 + N/df), and the score is the cosine of the * two weighted sets: * * score(q, d) = sum over t in q and d of idf(t)^2 * / ( sqrt(sum over t in q of idf(t)^2) * * sqrt(sum over t in d of idf(t)^2) ) * * so a score is in [0, 1], is 0 exactly when the two share no term, and is * comparable only within one corpus -- changing the corpus changes every idf. * * Binary presence rather than term counts is deliberate: a document that * repeats a word is not more relevant to a question that mentions it once, and * raw counts make a long document outrank a short exact match. * * WHY NOT `cosineSimilarity` FROM `analytics-core`: that one takes two dense * vectors of equal length and requires at least two samples. The vectors here * are sparse over a vocabulary that can legitimately be a single term, so the * sum is taken over the sparse sets directly, with the same algebra. */ import type { JsonSchemaNode } from '../../validation/schema-from-definition.js'; /** One document of the corpus a caller passes in. */ export interface KnowledgeDocument { id: string; title?: string; text: string; tags?: readonly string[]; } /** * Validate a corpus, naming the first offending document. A corpus with two * documents under one id would make every answer's citation ambiguous, and an * empty document would silently contribute a document-frequency denominator to * every idf while never matching anything. */ export declare const validateDocuments: (documents: unknown, label: string) => KnowledgeDocument[]; /** A corpus reduced to the term sets and idf weights every score needs. */ export interface DocumentIndex { documents: readonly KnowledgeDocument[]; /** Term set per document, in corpus order. */ termSets: ReadonlyArray>; /** ln(1 + N/df) per term that appears anywhere in the corpus. */ idf: ReadonlyMap; /** The norm of each document's idf-weighted term set. */ norms: readonly number[]; } export declare const buildIndex: (documents: readonly KnowledgeDocument[]) => DocumentIndex; /** One document's score for one question, with the terms that produced it. */ export interface Match { id: string; title: string | null; score: number; matchedTerms: string[]; } /** * Score every document against a question's terms. Documents that share no * term are left out rather than returned with a zero: a zero score is not a * weak answer, it is the absence of one, and returning it in a ranked list * invites a caller to read the top of the list as an answer. */ export declare const scoreQuery: (index: DocumentIndex, queryTerms: readonly string[]) => Match[]; /** * The question terms no document in the corpus contains. This is the half a * caller cannot derive from a ranked list: a confident-looking top hit and a * question whose decisive word appears nowhere are the same list. */ export declare const absentTerms: (index: DocumentIndex, queryTerms: readonly string[]) => string[]; /** A sentence quoted from a document, with where it came from. */ export interface Quotation { documentId: string; sentenceIndex: number; sentence: string; terms: string[]; } /** * Every sentence of a document that contains at least one of the given terms, * in the document's own order. Order is preserved because a reordered set of * sentences reads as prose that the document does not contain. */ export declare const sentencesMentioning: (document: KnowledgeDocument, terms: readonly string[]) => Quotation[]; /** * The rule `troubleshoot` uses to tell a description of a problem from a * description of what to do about it, published rather than described: a * sentence whose first content word is one of these verbs is treated as a * remedy. It is a shallow rule and the response says so by naming it, which is * what lets a caller judge the result instead of trusting it. */ export declare const REMEDY_VERBS: readonly string[]; /** The name of that rule, returned with every remedy so it can be judged. */ export declare const REMEDY_RULE = "remedy-sentence-verb"; /** The sentences of a document that the published rule marks as remedies. */ export declare const remedySentences: (document: KnowledgeDocument) => Quotation[]; /** * Build the smallest value a JSON Schema node accepts. * * WHAT THIS REFUSES AND WHY: a `pattern` cannot be inverted -- there is no * general way to produce a string matching an arbitrary regex -- and a property * with no `type`, `enum`, `const` or branch gives nothing to construct from. In * both cases this throws, naming the path, rather than returning a value that * the schema would reject. An example that does not validate is worse than no * example, because the caller finds out at the point of use. * * The caller of this function validates the result against the same schema * before returning it, so a defect here surfaces as a reported violation rather * than as a plausible-looking example. */ export declare const exampleFromSchema: (node: JsonSchemaNode, path?: string) => unknown; /** The filler an unconstrained string example uses. */ export declare const EXAMPLE_STRING = "example"; //# sourceMappingURL=knowledge-core.d.ts.map