/**
 * @sanity/ailf-core — Error classification (eval failure observability)
 *
 * Pure classifier that maps a per-result error string (the message Promptfoo
 * records when a test errors before grading completes) into a small taxonomy
 * the layered failure-observability policy keys on:
 *
 *  - `config-error`   — deterministic, structural. The task itself is broken
 *                       (a promptfoo assertion invariant threw, an unknown
 *                       assertion type, a malformed regex). Re-running will not
 *                       fix it; an author must.
 *  - `provider-error` — transient. The model provider failed (rate limit,
 *                       timeout, auth, model unavailable, 429/503/ECONNRESET).
 *                       This is the honest "check API keys / retry" case,
 *                       renamed from the historical blanket `api-error` tag.
 *  - `unknown`        — fallback when neither pattern matches.
 *
 * This is DISTINCT from the graded-judgment failure-mode classifier
 * (`packages/eval/src/pipeline/failure-modes.ts`), which classifies why a
 * *graded* doc-quality judgment scored low. Error classification is about
 * tests that produced NO gradeable output at all.
 *
 * Pure function — safe to call from browser or Node.
 *
 * @see docs/design-docs/eval-failure-observability.md — Data model and
 *      classification (shared keystone)
 */
/**
 * The error-classification vocabulary. Single `as const` tuple so the runtime
 * Zod `z.enum(...)` and the `ErrorClassification` type derive from one source
 * — the same drift-proofing the `REPORT_VALIDITY_STATUSES` tuple uses.
 */
export const ERROR_CLASSIFICATIONS = [
    "config-error",
    "provider-error",
    "unknown",
];
/**
 * Deterministic, structural defects in the task definition. Checked FIRST so a
 * promptfoo invariant message that happens to embed provider-ish words is still
 * read as the config defect it is.
 *
 *  - `invariant` / `must have a value` — promptfoo assertion `invariant(...)`
 *    throws (e.g. `"contains-any" assertion type must have a value`).
 *  - unknown / unsupported assertion type — a typo'd or unregistered `type`.
 *  - bad regex — an un-compilable `regex` assertion value.
 */
const CONFIG_ERROR_PATTERN = /invariant|must have a value|unknown assertion|unsupported assertion|unknown assertion type|invalid regular expression|invalid regexp|bad regex|malformed regex/i;
/**
 * Transient provider failures. The real "check API keys, rate limits, or model
 * availability" case.
 */
const PROVIDER_ERROR_PATTERN = /rate limit|rate-limit|ratelimit|429|503|timeout|timed out|etimedout|econnreset|econnrefused|socket hang up|fetch failed|network|unauthorized|401|403|authentication|invalid api key|api key|model.*(unavailable|not found|does not exist|overloaded)|overloaded|service unavailable|econnaborted|enotfound|quota/i;
/**
 * Classify a per-result error string into an {@link ErrorClassification}.
 *
 * Config defects win over provider matches: a deterministic assertion
 * invariant is the honest diagnosis even if the message also mentions a
 * provider, and it is the boundary the fail-fast policy keys on.
 *
 * @param error The raw error message recorded on the errored result. `null`,
 *              `undefined`, or an empty/whitespace string classify as
 *              `"unknown"`.
 */
export function classifyErrorString(error) {
    if (typeof error !== "string" || error.trim().length === 0) {
        return "unknown";
    }
    if (CONFIG_ERROR_PATTERN.test(error))
        return "config-error";
    if (PROVIDER_ERROR_PATTERN.test(error))
        return "provider-error";
    return "unknown";
}
