/**
 * Failure clustering — turn a list of "what failed" into "why it failed".
 *
 * Why this exists
 * ---------------
 * Senior-engineer pain point: 50 samples ran, 14 failed. Reading 14 failure
 * cases one by one is tedious; the user wants the tool to say "8 of these
 * are tool-call errors, 4 are refusals, 2 are formatting errors", with a
 * suggested fix per cluster.
 *
 * This module:
 *
 *  1. Selects failed samples from a report (compositeScore < threshold OR
 *     ok=false on at least one variant).
 *  2. Builds a compact failure description per sample (failed assertion
 *     types + judge reason snippet + tool failure pattern).
 *  3. Sends the full set to a single LLM call asking it to (a) propose
 *     N cluster labels, (b) assign each failure to a cluster, (c) suggest
 *     a fix per cluster.
 *  4. Returns the structured cluster report. Pure function — no I/O,
 *     accepts the executor as an argument.
 *
 * Design choice: single-call LLM clustering rather than embedding-based
 * k-means. Reasons:
 *
 *  - Failure descriptions are short (~50-200 tokens each), so 14 of them
 *    fit comfortably in one prompt. A 50-failure batch is also fine.
 *  - The LLM can produce HUMAN-READABLE cluster labels ("tool call error",
 *    "premature refusal") rather than numeric centroids — exactly what
 *    the user needs to act on.
 *  - One call = one judge cost = predictable. Embedding + clustering
 *    needs N+1 calls minimum.
 *  - Acceptable inaccuracy: clusters are advisory, not load-bearing. We
 *    document this clearly and surface raw failures alongside.
 *
 * The clustering prompt is structured to be JSON-only output for parsing
 * stability. We do NOT include length-debias instructions — clustering is
 * a categorization task, not a quality scoring task, so the length-debias
 * directive is inapplicable.
 */
import type { ExecutorFn, Report } from '../types/index.js';
export interface FailureClusterRequest {
    report: Report;
    /** Executor for the clustering LLM call. */
    executor: ExecutorFn;
    /** Model id to use for clustering. */
    judgeModel: string;
    /** Max number of clusters. Default 5; the LLM may produce fewer. */
    maxClusters?: number;
    /** compositeScore < this OR ok=false counts as failure. Default 3. */
    failureThreshold?: number;
    /** Cap on how many failures to feed the LLM (selects worst by score). Default 50. */
    maxFailuresFed?: number;
}
export interface FailureCase {
    sample_id: string;
    variant: string;
    /** Composite score (0 if errored). */
    score: number;
    /** Whether the executor itself errored (ok=false). */
    errored: boolean;
    /** Description summarizing what went wrong, fed to the LLM. */
    description: string;
}
export interface FailureCluster {
    /** Human-readable cluster label, in the report's primary language. */
    label: string;
    /** Free-text root cause analysis from the LLM. */
    rootCause: string;
    /** Suggested fix the user can apply. */
    suggestedFix: string;
    /** Sample IDs (with variant) belonging to this cluster. */
    members: Array<{
        sample_id: string;
        variant: string;
    }>;
}
export interface FailureClusterReport {
    /** All failures considered (after threshold + cap). */
    failures: FailureCase[];
    /** Clusters returned by the LLM, sorted by member count desc. */
    clusters: FailureCluster[];
    /** Failures the LLM didn't put in any cluster (label = "other"). */
    unclassified: Array<{
        sample_id: string;
        variant: string;
    }>;
    /** USD cost of the clustering call. */
    clusterCostUSD: number;
    /** Truncation flag — set when more failures existed than maxFailuresFed. */
    truncated: boolean;
    totalFailures: number;
}
export declare function clusterFailures(req: FailureClusterRequest): Promise<FailureClusterReport>;
export declare function formatFailureClusterReport(r: FailureClusterReport): string;
//# sourceMappingURL=failure-clusterer.d.ts.map