import {
  DEFAULT_RULES,
  classifyFailure,
  compareToBaseline,
  computeToolUseMetrics
} from "../chunk-NACM5RS7.js";
import {
  buildTrajectory
} from "../chunk-RZTMDUO7.js";
import {
  interRaterReliability
} from "../chunk-56GC6VYO.js";
import {
  aggregateLlm,
  argHash,
  llmSpans,
  runFailureClass,
  toolSpans
} from "../chunk-UFSG7ACU.js";
import "../chunk-5BKGXME7.js";
import "../chunk-3BFEG2F6.js";
import "../chunk-PZ5AY32C.js";

// src/pipelines/budget-breach.ts
async function budgetBreachView(store, options = {}) {
  const runs = await store.listRuns({
    scenarioId: options.scenarioId,
    variantId: options.variantId
  });
  const findings = [];
  const byDimension = {};
  const byScenario = {};
  const byVariant = {};
  for (const run of runs) {
    const entries = await store.budget(run.runId);
    for (const e of entries) {
      if (!e.breached) continue;
      const excessRatio = e.limit > 0 ? e.consumed / e.limit : Infinity;
      findings.push({
        runId: run.runId,
        scenarioId: run.scenarioId,
        variantId: run.variantId,
        dimension: e.dimension,
        limit: e.limit,
        consumed: e.consumed,
        excessRatio,
        timestamp: e.timestamp
      });
      byDimension[e.dimension] = (byDimension[e.dimension] ?? 0) + 1;
      byScenario[run.scenarioId] = (byScenario[run.scenarioId] ?? 0) + 1;
      if (run.variantId) byVariant[run.variantId] = (byVariant[run.variantId] ?? 0) + 1;
    }
  }
  const breachedRuns = new Set(findings.map((f) => f.runId));
  return {
    findings,
    byDimension,
    byScenario,
    byVariant,
    totalRuns: runs.length,
    breachedRunRatio: runs.length > 0 ? breachedRuns.size / runs.length : 0
  };
}

// src/pipelines/failure-cluster.ts
async function failureClusterView(store, options = {}) {
  const rules = options.rules ?? DEFAULT_RULES;
  const minSize = options.minClusterSize ?? 1;
  const runs = await store.listRuns();
  const clusters = /* @__PURE__ */ new Map();
  let totalFailures = 0;
  for (const run of runs) {
    if (run.status === "completed" && run.outcome?.pass !== false) continue;
    totalFailures++;
    const spans = await store.spans({ runId: run.runId });
    const events = await store.events({ runId: run.runId });
    const cls = classifyFailure({ run, spans, events }, rules);
    let toolName;
    let argPrefix;
    let dimension;
    if (cls.triggerSpanId) {
      const trig = spans.find((s) => s.spanId === cls.triggerSpanId);
      if (trig?.kind === "tool") {
        toolName = trig.toolName;
        argPrefix = argHash(trig.args).slice(0, 16);
      } else if (trig?.kind === "judge") {
        dimension = trig.dimension;
      }
    }
    if (!toolName) {
      const ts = await toolSpans(store, run.runId);
      const errored = ts.filter((t) => t.status === "error").pop();
      if (errored) {
        toolName = errored.toolName;
        argPrefix = argHash(errored.args).slice(0, 16);
      }
    }
    if (!dimension) {
      const judge = spans.find((s) => s.kind === "judge" && typeof s.dimension === "string");
      if (judge?.kind === "judge") dimension = judge.dimension;
    }
    const key = `${cls.failureClass}|${toolName ?? ""}|${argPrefix ?? ""}|${dimension ?? ""}`;
    let cluster = clusters.get(key);
    if (!cluster) {
      cluster = {
        failureClass: cls.failureClass,
        toolName,
        argPrefix,
        dimension,
        runCount: 0,
        scenarioIds: [],
        exampleRunId: run.runId,
        exampleError: firstErrorMessage(spans) ?? cls.reason
      };
      clusters.set(key, cluster);
    }
    cluster.runCount++;
    if (!cluster.scenarioIds.includes(run.scenarioId)) cluster.scenarioIds.push(run.scenarioId);
  }
  const arr = [...clusters.values()].filter((c) => c.runCount >= minSize).sort((a, b) => b.runCount - a.runCount);
  return { clusters: arr, totalFailures, totalRuns: runs.length };
}
function firstErrorMessage(spans) {
  const errored = spans.find((s) => s.status === "error");
  return errored?.error;
}

// src/pipelines/first-divergence.ts
async function firstDivergenceView(store, runA, runB, options = {}) {
  const [a, b] = await Promise.all([buildTrajectory(store, runA), buildTrajectory(store, runB)]);
  const eq = options.stepEquals ?? defaultStepEquals;
  const minLen = Math.min(a.steps.length, b.steps.length);
  for (let i = 0; i < minLen; i++) {
    const aStep = a.steps[i];
    const bStep = b.steps[i];
    if (!eq(aStep, bStep)) {
      return {
        runA,
        runB,
        firstDivergenceIndex: i,
        aStep,
        bStep,
        reason: describeDifference(aStep, bStep),
        commonPrefixLen: i
      };
    }
  }
  if (a.steps.length === b.steps.length) {
    return { runA, runB, firstDivergenceIndex: null, commonPrefixLen: minLen };
  }
  const longer = a.steps.length > b.steps.length ? a : b;
  const extra = longer.steps.length - minLen;
  const reason = minLen === 0 ? `one trajectory is empty; the other has ${extra} step(s) starting at index 0` : `one trajectory has ${extra} more step(s) after index ${minLen - 1}`;
  return {
    runA,
    runB,
    firstDivergenceIndex: minLen,
    aStep: a.steps[minLen],
    bStep: b.steps[minLen],
    reason,
    commonPrefixLen: minLen
  };
}
function defaultStepEquals(a, b) {
  if (a.span.kind !== b.span.kind) return false;
  if (a.span.kind === "tool" && b.span.kind === "tool") return a.span.toolName === b.span.toolName;
  if (a.span.kind === "llm" && b.span.kind === "llm") return a.span.model === b.span.model;
  if (a.span.kind === "judge" && b.span.kind === "judge")
    return a.span.dimension === b.span.dimension;
  return a.span.name === b.span.name;
}
function describeDifference(a, b) {
  if (a.span.kind !== b.span.kind) return `kind ${a.span.kind} vs ${b.span.kind}`;
  if (a.span.kind === "tool" && b.span.kind === "tool" && a.span.toolName !== b.span.toolName) {
    return `tool ${a.span.toolName} vs ${b.span.toolName}`;
  }
  if (a.span.kind === "llm" && b.span.kind === "llm" && a.span.model !== b.span.model) {
    return `model ${a.span.model} vs ${b.span.model}`;
  }
  return `name "${a.span.name}" vs "${b.span.name}"`;
}

// src/pipelines/judge-agreement.ts
async function judgeAgreementView(store) {
  const all = (await store.spans({ kind: "judge" })).filter(
    (s) => s.kind === "judge"
  );
  if (all.length === 0) return { pairs: [], dimensions: [], judgeIds: [] };
  const byDimension = /* @__PURE__ */ new Map();
  for (const s of all) {
    const arr = byDimension.get(s.dimension) ?? [];
    arr.push(s);
    byDimension.set(s.dimension, arr);
  }
  const judgeIds = [...new Set(all.map((s) => s.judgeId))].sort();
  const pairs = [];
  for (const [dim, spans] of byDimension) {
    const byJudge = /* @__PURE__ */ new Map();
    for (const s of spans) {
      const m = byJudge.get(s.judgeId) ?? /* @__PURE__ */ new Map();
      m.set(s.targetSpanId, s.score);
      byJudge.set(s.judgeId, m);
    }
    const judgesHere = [...byJudge.keys()];
    for (let i = 0; i < judgesHere.length; i++) {
      for (let j = i + 1; j < judgesHere.length; j++) {
        const judgeI = judgesHere[i];
        const judgeJ = judgesHere[j];
        const a = byJudge.get(judgeI);
        const b = byJudge.get(judgeJ);
        const common = [];
        for (const [target, scoreA] of a) {
          const scoreB = b.get(target);
          if (scoreB !== void 0) common.push([scoreA, scoreB]);
        }
        if (common.length < 2) continue;
        const judgeScores = common.map(
          ([scoreA, scoreB]) => [
            { judgeName: judgeI, dimension: dim, score: scoreA, reasoning: "" },
            { judgeName: judgeJ, dimension: dim, score: scoreB, reasoning: "" }
          ]
        );
        const k = interRaterReliability(
          judgeScores[0].map((_, k2) => judgeScores.map((pair) => pair[k2]))
        );
        pairs.push({
          judgeA: judgeI,
          judgeB: judgeJ,
          dimension: dim,
          commonItems: common.length,
          pearson: pearson(
            common.map((c) => c[0]),
            common.map((c) => c[1])
          ),
          krippendorff: k
        });
      }
    }
  }
  return {
    pairs: pairs.sort((a, b) => b.commonItems - a.commonItems),
    dimensions: [...byDimension.keys()].sort(),
    judgeIds
  };
}
function pearson(a, b) {
  if (a.length !== b.length || a.length < 2) return NaN;
  const mA = a.reduce((s, v) => s + v, 0) / a.length;
  const mB = b.reduce((s, v) => s + v, 0) / b.length;
  let num = 0, denA = 0, denB = 0;
  for (let i = 0; i < a.length; i++) {
    const dA = a[i] - mA;
    const dB = b[i] - mB;
    num += dA * dB;
    denA += dA * dA;
    denB += dB * dB;
  }
  if (denA === 0 || denB === 0) return denA === 0 && denB === 0 ? 1 : 0;
  return num / Math.sqrt(denA * denB);
}

// src/pipelines/regression.ts
async function regressionView(store, metrics, options) {
  const baselineRuns = await store.listRuns(options.baseline);
  const candidateRuns = await store.listRuns(options.candidate);
  const samples = await Promise.all(
    metrics.map(async (m) => {
      const extract = m.extract ?? defaultExtract(m.metric);
      const baseline = await extractAll(baselineRuns, extract, store);
      const candidate = await extractAll(candidateRuns, extract, store);
      return { metric: m.metric, higherIsBetter: m.higherIsBetter, baseline, candidate };
    })
  );
  return compareToBaseline(samples, options);
}
async function extractAll(runs, extract, store) {
  const out = [];
  for (const r of runs) {
    const v = await extract(r, store);
    if (v !== null && Number.isFinite(v)) out.push(v);
  }
  return out;
}
function defaultExtract(metric) {
  return async (run, store) => {
    switch (metric) {
      case "score":
      case "overallScore":
        return run.outcome?.score ?? null;
      case "pass":
        return run.outcome?.pass === true ? 1 : 0;
      case "durationMs":
        return run.endedAt && run.startedAt ? run.endedAt - run.startedAt : null;
      case "costUsd": {
        const llm = await llmSpans(store, run.runId);
        return aggregateLlm(llm).costUsd;
      }
      case "inputTokens": {
        const llm = await llmSpans(store, run.runId);
        return aggregateLlm(llm).inputTokens;
      }
      case "outputTokens": {
        const llm = await llmSpans(store, run.runId);
        return aggregateLlm(llm).outputTokens;
      }
      case "failureClass": {
        return runFailureClass(run) === "success" ? 1 : 0;
      }
      default:
        return null;
    }
  };
}

// src/pipelines/stuck-loop.ts
async function stuckLoopView(store, options = {}) {
  const minOccurrences = options.minOccurrences ?? 3;
  const runs = options.runId ? [{ runId: options.runId }] : (await store.listRuns()).map((r) => ({ runId: r.runId }));
  const findings = [];
  for (const { runId } of runs) {
    const tools = await toolSpans(store, runId);
    const byKey = /* @__PURE__ */ new Map();
    for (const t of tools) {
      const h = argHash(t.args);
      const key = `${t.toolName}\0${h}`;
      const bucket = byKey.get(key) ?? { spans: [], argHash: h, toolName: t.toolName };
      bucket.spans.push(t);
      byKey.set(key, bucket);
    }
    for (const { spans, argHash: h, toolName } of byKey.values()) {
      if (spans.length < minOccurrences) continue;
      const sorted = [...spans].sort((a, b) => a.startedAt - b.startedAt);
      const first = sorted[0].startedAt;
      const last = sorted[sorted.length - 1].startedAt;
      findings.push({
        runId,
        toolName,
        argHash: h,
        occurrences: sorted.length,
        spanIds: sorted.map((s) => s.spanId),
        windowMs: last - first
      });
    }
  }
  const affectedRuns = new Set(findings.map((f) => f.runId));
  return {
    findings,
    affectedRunRatio: runs.length > 0 ? affectedRuns.size / runs.length : 0,
    totalRuns: runs.length
  };
}

// src/pipelines/tool-waste.ts
async function toolWasteView(store, options = {}) {
  const runs = options.runId ? [options.runId] : (await store.listRuns()).map((r) => r.runId);
  const byRun = [];
  let totalCalls = 0;
  let totalWasted = 0;
  for (const runId of runs) {
    const tools = await toolSpans(store, runId);
    if (tools.length === 0) {
      byRun.push({ runId, wastedCalls: 0, totalCalls: 0, wasteRate: 0 });
      continue;
    }
    const llms = await llmSpans(store, runId);
    const sortedLlm = [...llms].sort((a, b) => a.startedAt - b.startedAt);
    const startTimes = sortedLlm.map((l) => l.startedAt);
    const suffixText = buildSuffixText(sortedLlm);
    let wasted = 0;
    for (const t of tools) {
      if (t.status === "error") {
        wasted++;
        continue;
      }
      const cutoff = upperBound(startTimes, t.startedAt);
      if (options.usageOracle) {
        if (!options.usageOracle(t, { llm: sortedLlm.slice(cutoff) })) wasted++;
      } else {
        const resultStr = stringify(t.result);
        if (resultStr === "") continue;
        const haystack = suffixText[cutoff] ?? "";
        const used = haystack.includes(resultStr.slice(0, 120));
        if (!used) wasted++;
      }
    }
    const wasteRate = wasted / tools.length;
    byRun.push({ runId, wastedCalls: wasted, totalCalls: tools.length, wasteRate });
    totalCalls += tools.length;
    totalWasted += wasted;
  }
  return { byRun, overallWasteRate: totalCalls > 0 ? totalWasted / totalCalls : 0 };
}
function buildSuffixText(spans) {
  const result = new Array(spans.length + 1);
  result[spans.length] = "";
  for (let i = spans.length - 1; i >= 0; i--) {
    const own = spans[i].messages.map(
      (m) => typeof m.content === "string" ? m.content : ""
    ).join("\n");
    result[i] = `${own}
${result[i + 1]}`;
  }
  return result;
}
function upperBound(sorted, target) {
  let lo = 0;
  let hi = sorted.length;
  while (lo < hi) {
    const mid = lo + hi >>> 1;
    if (sorted[mid] <= target) lo = mid + 1;
    else hi = mid;
  }
  return lo;
}
function stringify(v) {
  if (v === null || v === void 0) return "";
  if (typeof v === "string") return v;
  try {
    return JSON.stringify(v);
  } catch {
    return String(v);
  }
}
export {
  budgetBreachView,
  computeToolUseMetrics,
  failureClusterView,
  firstDivergenceView,
  judgeAgreementView,
  regressionView,
  stuckLoopView,
  toolWasteView
};
//# sourceMappingURL=index.js.map