import type { RagChunk } from '../hallucination/rag.js'; import { type EmbeddingProvider, type VectorSearchOptions, type VectorSearchResult, type VectorStore } from '../hallucination/retrieval.js'; import type { CompletionRequest } from '../types/messages.js'; import { type DocumentSource, type IngestionOptions } from './ingestion.js'; /** What one retrieval asks for. */ export interface RetrieveOptions { /** Most results returned. Defaults to the retriever's own `topK`, or 5. */ topK?: number; /** Exact matches on top-level metadata fields, passed down to every store searched. */ filter?: VectorSearchOptions['filter']; /** Cancels the retrieval, including model calls a reranker or query generator makes. */ signal?: AbortSignal; } /** * Anything that answers a query with ranked chunks. Every retriever here takes others and returns * one, so hybrid search, reranking, diversity, and parent lookup compose in any order over any store. */ export interface Retriever { /** The chunks that best answer a query, best first. */ retrieve(query: string, options?: RetrieveOptions): Promise; } /** * Keyword search that holds its own chunks: an in-memory BM25 `KeywordIndex`, Postgres full-text * search, a search engine. It is a `Retriever`, so `hybridRetriever()` fuses it with vector search, * and it takes writes, so ingestion keeps it in step with a vector store. */ export interface SparseRetriever extends Retriever { /** Adds chunks, or replaces those whose id exists. */ add(chunks: readonly RagChunk[]): Promise | void; /** Removes chunks by id. An id that is not held is ignored. */ delete(ids: readonly string[]): Promise | void; } /** A client that runs one completion — a `NexusAI` instance, or anything with the same `complete()`. */ export interface RetrievalModelClient { /** Runs one completion and returns its text. */ complete(request: CompletionRequest): Promise<{ content: string; }>; } /** A vector store as a retriever, with defaults for every search. */ export declare function vectorRetriever(store: VectorStore, defaults?: VectorSearchOptions): Retriever; /** Options for a `KeywordIndex`. */ export interface KeywordIndexOptions { /** BM25 term-frequency saturation. Defaults to 1.2. */ k1?: number; /** BM25 length normalization, from 0 to 1. Defaults to 0.75. */ b?: number; /** Splits text into terms. Defaults to `tokenize()`. */ tokenize?: (text: string) => string[]; } /** * Lower-cased runs of letters and digits, in any script. Keeps identifiers such as `ERR-4012` findable * as `err` and `4012`, which is where keyword search beats vectors. */ export declare function tokenize(text: string): string[]; /** * Keyword search with BM25 over chunks held in memory. Vector search misses exact terms — product * codes, error numbers, names — that keyword search finds; `hybridRetriever()` fuses the two. * Adding a chunk whose id exists replaces it, as in a vector store. */ export declare class KeywordIndex implements SparseRetriever { private readonly chunks; private readonly postings; private totalLength; private readonly k1; private readonly b; private readonly split; constructor(options?: KeywordIndexOptions); /** Adds chunks, or replaces those whose id exists. */ add(chunks: readonly RagChunk[]): void; /** Removes chunks by id. An id that is not held is ignored. */ delete(ids: readonly string[]): void; /** Chunks held. */ size(): number; /** The chunks that best match a query's terms, best first, scored by BM25. */ retrieve(query: string, options?: RetrieveOptions): Promise; private remove; } /** Options for `reciprocalRankFusion()`. */ export interface FusionOptions { /** Damps the weight of top ranks; higher values flatten the fusion. Defaults to 60, the usual choice. */ k?: number; /** A weight per ranking, in order. Defaults to 1 each. */ weights?: readonly number[]; /** Most results returned. Defaults to every chunk that appears in any ranking. */ topK?: number; } /** * Fuses rankings by reciprocal rank: each chunk scores the sum, over the rankings it appears in, of * `weight / (k + rank)`. Uses ranks, not scores, so rankings on different scales — BM25 and cosine — * combine without normalization. The result's `score` is the fused score. */ export declare function reciprocalRankFusion(rankings: readonly (readonly VectorSearchResult[])[], options?: FusionOptions): VectorSearchResult[]; /** Options for `hybridRetriever()` and `multiQueryRetriever()`. */ export interface FusedRetrieverOptions extends Omit { /** Results returned. Defaults to 5. */ topK?: number; /** Results asked of each retriever before fusing. Defaults to four times `topK`. */ candidates?: number; } /** * Several retrievers at once — typically a vector store and a `KeywordIndex` over the same chunks — * fused by reciprocal rank. The retrievers run in parallel. */ export declare function hybridRetriever(retrievers: readonly Retriever[], options?: FusedRetrieverOptions): Retriever; /** * Scores each chunk's relevance to a query, higher meaning more relevant, one number per chunk in * order. A cross-encoder, a hosted rerank API, or `modelReranker()` over a chat model. */ export type Reranker = (query: string, chunks: readonly VectorSearchResult[], signal?: AbortSignal) => Promise | readonly number[]; /** Options for `rerankRetriever()`. */ export interface RerankRetrieverOptions { /** Results returned. Defaults to 5. */ topK?: number; /** Results asked of the base retriever and scored. Defaults to 20. */ candidates?: number; /** Drops results the reranker scored below this. */ minScore?: number; } /** * A retriever whose results are rescored by a reranker: it asks the base for more candidates than it * returns, scores them all against the query, and keeps the best. The result's `score` is the * reranker's. */ export declare function rerankRetriever(base: Retriever, reranker: Reranker, options?: RerankRetrieverOptions): Retriever; /** Options for `modelReranker()`. */ export interface ModelRerankerOptions { /** The model that judges relevance. */ model: string; /** Characters of each chunk shown to the model. Defaults to 1,000. */ maxChunkCharacters?: number; } /** * A reranker that asks a chat model to score each chunk from 0 to 10 for how well it answers the * query, in one call. Slower and costlier than a cross-encoder, but needs nothing beyond the model you * already use. A reply that cannot be read scores every chunk 0 rather than failing the retrieval. */ export declare function modelReranker(client: RetrievalModelClient, options: ModelRerankerOptions): Reranker; /** Options for `maximalMarginalRelevance()`. */ export interface MmrOptions { /** Trades relevance against diversity: 1 is pure relevance, 0 pure diversity. Defaults to 0.5. */ lambda?: number; /** Candidates picked. Defaults to 5. */ topK?: number; } /** * Picks candidates by maximal marginal relevance: each pick maximizes its similarity to the query * minus its similarity to what was already picked, so near-duplicates stop crowding out other * evidence. Returns the picked candidates' indexes, in pick order. */ export declare function maximalMarginalRelevance(query: readonly number[], candidates: readonly (readonly number[])[], options?: MmrOptions): number[]; /** Options for `mmrRetriever()`. */ export interface MmrRetrieverOptions extends MmrOptions { /** Embeds the query and the candidates, in one call. Use the embedding function the store uses. */ embed: EmbeddingProvider; /** Results asked of the base retriever before picking. Defaults to 20. */ candidates?: number; } /** * A retriever that diversifies its base's results by maximal marginal relevance. The query and the * candidates are embedded together in one call; the results keep the base's scores. */ export declare function mmrRetriever(base: Retriever, options: MmrRetrieverOptions): Retriever; /** Finds parent chunks by id: a map, or a function that fetches them, such as from your database. */ export type ParentLookup = ReadonlyMap | ((ids: readonly string[]) => Promise | readonly RagChunk[]); /** Options for `parentDocumentRetriever()`. */ export interface ParentDocumentRetrieverOptions { /** Where parents are found. */ parents: ParentLookup; /** * The child's metadata field naming its parent. Defaults to `documentId`, which ingestion sets on * every chunk and `splitParentChild()` points at the parent. */ parentKey?: string; /** Results returned. Defaults to 5. */ topK?: number; /** Children asked of the base retriever. Defaults to 20. */ candidates?: number; } /** * Searches small chunks, which match a query precisely, and returns the larger parents they came * from, which give the model enough context to answer. Parents come back in the order of their best * child, each once, scored by that child. A child whose parent is not found is returned as it is. */ export declare function parentDocumentRetriever(base: Retriever, options: ParentDocumentRetrieverOptions): Retriever; /** Options for `splitParentChild()`. */ export interface ParentChildOptions { /** How documents are split into parents. Defaults to 2,000-character chunks. */ parent?: IngestionOptions; /** How parents are split into children. Defaults to 400-character chunks with 50 of overlap. */ child?: IngestionOptions; } /** * Splits documents twice: into parents, kept for `parentDocumentRetriever()` to return, and each * parent into small children, added to the store it searches. Every child's `documentId` is its * parent's id. */ export declare function splitParentChild(documents: readonly DocumentSource[], options?: ParentChildOptions): { parents: RagChunk[]; children: RagChunk[]; }; /** Rewrites a query into alternative phrasings, such as `modelQueryVariants()` over a chat model. */ export type QueryVariants = (query: string, signal?: AbortSignal) => Promise | readonly string[]; /** Options for `multiQueryRetriever()`. */ export interface MultiQueryRetrieverOptions extends FusedRetrieverOptions { /** Searches the original query as well as its variants. On by default. */ includeOriginal?: boolean; } /** * Searches several phrasings of a query and fuses the results by reciprocal rank, so a question worded * differently from the documents still finds them. The searches run in parallel. */ export declare function multiQueryRetriever(base: Retriever, variants: QueryVariants, options?: MultiQueryRetrieverOptions): Retriever; /** Options for `modelQueryVariants()`. */ export interface ModelQueryVariantsOptions { /** The model that rewrites queries. */ model: string; /** Variants asked for. Defaults to 3. */ count?: number; } /** * Query variants from a chat model, in one call. A reply that cannot be read yields no variants, so * the original query is still searched. */ export declare function modelQueryVariants(client: RetrievalModelClient, options: ModelQueryVariantsOptions): QueryVariants;