/**
 * ADR-121 Phase 16 — Adaptive primitive selection (BEYOND SOTA).
 *
 * Phase 14's topology benchmark proved each RAG primitive dominates
 * exactly one corpus shape:
 *
 *   easy            → plain wins (no fusion needed)
 *   duplicate-heavy → MMR wins on subtopic-coverage
 *   multi-intent    → RRF wins on intent-boundary preservation
 *   q-a-gap         → HyDE wins on centroid-finding
 *
 * Production RAG systems don't route between primitives automatically
 * — they pick one pipeline at design time or run all-of-them and
 * vote/reduce. Both choices leave performance on the table.
 *
 * This module ships **adaptive primitive selection**: extract three
 * cheap signals from the candidate set + query variants + hypothetical
 * answers, then pick the right primitive (or the compound) based on
 * which signals fire. Cost: one extra small-fan cosine pass over the
 * candidate set and the variant/hypothetical embeddings.
 *
 * Signals:
 *
 *   1. duplicateDensity — mean pairwise cosine among the top-N
 *      candidates from a plain search. High = corpus has near-dup
 *      structure → MMR signal.
 *
 *   2. queryIntentCohesion — mean pairwise cosine among the query
 *      reformulation vectors. Low = reformulations cover distinct
 *      intents → RRF signal.
 *
 *   3. qaSpaceGap — 1 - cosine(question_vector, mean(hypothetical_vectors)).
 *      High = the question and the hypothetical answers live in
 *      different vector regions → HyDE signal.
 *
 * The router applies a small set of thresholds (defaults from the
 * Phase 14 topology data) and returns a structured decision the
 * caller can act on or override.
 */
export interface RetrievalFeatures {
    /** Mean pairwise cosine among top-N candidates from a plain search. Range [-1, 1]. Higher = more duplicate-like. */
    readonly duplicateDensity: number;
    /** Mean pairwise cosine among query reformulation vectors. Range [-1, 1]. Higher = more cohesive (single intent). */
    readonly queryIntentCohesion: number;
    /** 1 - cosine(question_vec, mean(hypothetical_vecs)). Range [0, 2]. Higher = larger Q/A space gap. */
    readonly qaSpaceGap: number;
    /**
     * Phase 17 — Mean IDF of unique query tokens (relative to a BM25
     * index over the corpus). Higher = query has rare/technical
     * tokens that dense embeddings tend to underweight → hybrid
     * sparse+dense retrieval wins. 0 when no BM25 index was provided.
     */
    readonly rareTokenDensity: number;
    /** How many top candidates were used to compute duplicateDensity. */
    readonly candidateCountUsed: number;
    /** How many variants were used to compute intent cohesion. */
    readonly variantCount: number;
    /** How many hypotheticals were used to compute Q/A gap. */
    readonly hypotheticalCount: number;
    /** True if the caller provided a BM25 index for rareTokenDensity. */
    readonly bm25IndexProvided: boolean;
}
export interface AdaptiveRouterOptions {
    /**
     * Threshold above which duplicateDensity triggers the MMR signal.
     * Default 0.85 — duplicates are very close to each other in
     * cosine space.
     */
    readonly duplicateThreshold?: number;
    /**
     * Threshold below which queryIntentCohesion triggers the RRF
     * signal. Default 0.55 — orthogonal-ish reformulations.
     */
    readonly intentCohesionThreshold?: number;
    /**
     * Threshold above which qaSpaceGap triggers the HyDE signal.
     * Default 0.35 — meaningful question/answer space drift.
     */
    readonly qaGapThreshold?: number;
    /**
     * Phase 17 — Threshold above which rareTokenDensity (mean query IDF)
     * triggers the HYBRID signal. Default 2.0 — IDF ≥ 2.0 means the
     * query's tokens appear in <~13% of the corpus, where BM25's
     * sparse lexical signal materially helps dense retrieval.
     */
    readonly rareTokenThreshold?: number;
    /**
     * If 2+ signals fire, return `compound` instead of one of the
     * individual primitives. Default true.
     */
    readonly preferCompoundWhenMultipleSignals?: boolean;
}
export type AdaptivePrimitive = 'plain' | 'mmr' | 'rrf' | 'hyde' | 'hybrid' | 'compound';
export interface AdaptiveDecision {
    /** The recommended primitive. */
    readonly primitive: AdaptivePrimitive;
    /** Human-readable explanation citing which signals fired. */
    readonly reason: string;
    /** Which signals were above their respective thresholds. */
    readonly signals: Readonly<{
        mmr: boolean;
        rrf: boolean;
        hyde: boolean;
        hybrid: boolean;
    }>;
    /** Echo of the input features for traceability. */
    readonly features: RetrievalFeatures;
}
/**
 * Extract the routing signals from the available inputs.
 *
 * @param topCandidates  — top-N hits from a cheap plain search,
 *                          with vectors. Used for duplicateDensity.
 *                          Pass `null` to disable that signal.
 * @param queryVector    — the user-question vector. Required for
 *                          qaSpaceGap.
 * @param variantVectors — N query reformulation vectors. Used for
 *                          queryIntentCohesion. At least 2 required
 *                          to compute the signal.
 * @param hypotheticalVectors — N hypothetical-answer vectors. Used
 *                          for qaSpaceGap. At least 1 required.
 * @param bm25Input — optional {queryText, bm25Index} pair for the
 *                          Phase 17 hybrid signal. Pass `null` to
 *                          disable.
 *
 * Cost: O(N²·dim) for the pairwise cosines + O(|q|) for the BM25
 * mean-IDF lookup.
 */
export declare function extractRetrievalFeatures(topCandidates: ReadonlyArray<{
    vector: Float32Array | number[];
}> | null, queryVector: Float32Array | number[], variantVectors: ReadonlyArray<Float32Array | number[]>, hypotheticalVectors: ReadonlyArray<Float32Array | number[]>, bm25Input?: {
    queryText: string;
    index: {
        df: ReadonlyMap<string, number>;
        N: number;
    };
} | null): RetrievalFeatures;
/**
 * Apply the routing heuristic to a set of features. Returns the
 * recommended primitive plus the reasoning trace.
 */
export declare function adaptiveRoute(features: RetrievalFeatures, options?: AdaptiveRouterOptions): AdaptiveDecision;
//# sourceMappingURL=adaptive-router.d.ts.map