import {
  exportTrainingData,
  toNdjson
} from "../chunk-KMPRBJK4.js";
import {
  buildTrajectory
} from "../chunk-RZTMDUO7.js";
import "../chunk-5BKGXME7.js";
import {
  TraceEmitter
} from "../chunk-TVVP3ZZQ.js";
import "../chunk-PZ5AY32C.js";

// src/prm/builtin-rubrics.ts
function outputLengthRubric(args = {}) {
  const min = args.minChars ?? 20;
  const max = args.maxChars ?? 8e3;
  return {
    id: "output-length",
    kinds: ["llm"],
    weight: args.weight ?? 0.5,
    async grade({ step }) {
      const llm = step.span;
      const len = (llm.output ?? "").length;
      if (len === 0) return { score: 0, rationale: "empty output" };
      if (len < min)
        return { score: Math.max(0, len / min), rationale: `below min (${len} < ${min})` };
      if (len > max)
        return {
          score: Math.max(0, 1 - (len - max) / max),
          rationale: `above max (${len} > ${max})`
        };
      return { score: 1, rationale: `${len} chars in bounds` };
    }
  };
}
function toolSuccessRubric(args = {}) {
  return {
    id: "tool-success",
    kinds: ["tool"],
    weight: args.weight ?? 1,
    async grade({ step }) {
      const tool = step.span;
      if (tool.status === "error")
        return { score: 0, rationale: `error: ${tool.error ?? "unknown"}` };
      const r = tool.result;
      if (r === null || r === void 0) return { score: 0.3, rationale: "empty result" };
      const asText = typeof r === "string" ? r : JSON.stringify(r);
      if (asText.length < 4) return { score: 0.5, rationale: "tiny result" };
      return { score: 1, rationale: `${tool.toolName} ok` };
    }
  };
}
function toolNonRedundantRubric(args = {}) {
  const weight = args.weight ?? 0.5;
  return {
    id: "tool-non-redundant",
    kinds: ["tool"],
    weight,
    async grade({ step, prior }) {
      const tool = step.span;
      const priorMatches = prior.filter((p) => {
        if (p.span.kind !== "tool") return false;
        const pt = p.span;
        return pt.toolName === tool.toolName && stableStringify(pt.args) === stableStringify(tool.args);
      });
      if (priorMatches.length === 0) return { score: 1, rationale: "novel call" };
      return {
        score: Math.max(0, 1 - priorMatches.length * 0.5),
        rationale: `${priorMatches.length} duplicate(s)`
      };
    }
  };
}
function nonRefusalRubric(args = {}) {
  const weight = args.weight ?? 1;
  const markers = args.markers ?? [
    /\bi\s+(?:can(?:not|'t)|won't|will\s+not)\b/i,
    /\b(?:as\s+an?\s+)?ai\b.*?\b(?:can't|cannot)\b/i
  ];
  return {
    id: "non-refusal",
    kinds: ["llm"],
    weight,
    async grade({ step }) {
      const llm = step.span;
      const out = llm.output ?? "";
      const refused = markers.some((re) => re.test(out));
      return refused ? { score: 0, rationale: "refusal marker present" } : { score: 1, rationale: "no refusal" };
    }
  };
}
function toolIntentAlignmentRubric(args = {}) {
  return {
    id: "tool-intent-alignment",
    kinds: ["llm"],
    weight: args.weight ?? 0.5,
    async grade({ step, next }) {
      const llm = step.span;
      const nextTool = next.find((s) => s.span.kind === "tool");
      if (!nextTool) return null;
      const toolName = nextTool.span.toolName;
      const out = (llm.output ?? "").toLowerCase();
      const mentioned = out.includes(toolName.toLowerCase());
      return mentioned ? { score: 1, rationale: `mentioned "${toolName}" before calling it` } : { score: 0.5, rationale: `called "${toolName}" without announcing it` };
    }
  };
}
function stableStringify(value) {
  if (value === null || typeof value !== "object") return JSON.stringify(value);
  if (Array.isArray(value)) return `[${value.map(stableStringify).join(",")}]`;
  const keys = Object.keys(value).sort();
  return `{${keys.map((k) => `${JSON.stringify(k)}:${stableStringify(value[k])}`).join(",")}}`;
}

// src/prm/inference.ts
var DEFAULT_PRM_CONCURRENCY = 4;
async function prmBestOfN(store, grader, runIds, options = {}) {
  if (runIds.length === 0) throw new Error("prmBestOfN: at least 1 candidate required");
  const concurrency = Math.max(1, options.concurrency ?? DEFAULT_PRM_CONCURRENCY);
  const graded = new Array(runIds.length);
  let cursor = 0;
  async function worker() {
    while (true) {
      const i = cursor++;
      if (i >= runIds.length) return;
      graded[i] = await grader.grade(store, runIds[i]);
    }
  }
  await Promise.all(Array.from({ length: Math.min(concurrency, runIds.length) }, () => worker()));
  const ranked = [...graded].sort((a, b) => b.aggregateScore - a.aggregateScore);
  const mean = graded.reduce((a, g) => a + g.aggregateScore, 0) / graded.length;
  const variance = graded.reduce((a, g) => a + (g.aggregateScore - mean) ** 2, 0) / graded.length;
  return { winner: ranked[0], ranked, stdDev: Math.sqrt(variance) };
}
async function prmEnsembleBestOfN(store, graders, runIds, options = {}) {
  if (graders.length === 0) throw new Error("prmEnsembleBestOfN: at least 1 grader");
  if (runIds.length === 0) throw new Error("prmEnsembleBestOfN: at least 1 candidate required");
  const concurrency = Math.max(1, options.concurrency ?? DEFAULT_PRM_CONCURRENCY);
  const jobs = [];
  for (let gi = 0; gi < graders.length; gi++) {
    for (const runId of runIds) jobs.push({ graderIdx: gi, runId });
  }
  const settled = new Array(jobs.length);
  let cursor = 0;
  async function worker() {
    while (true) {
      const i = cursor++;
      if (i >= jobs.length) return;
      const job = jobs[i];
      settled[i] = await graders[job.graderIdx].grade(store, job.runId).then((value) => ({ status: "fulfilled", value })).catch((reason) => ({ status: "rejected", reason }));
    }
  }
  await Promise.all(Array.from({ length: Math.min(concurrency, jobs.length) }, () => worker()));
  const perGrader = graders.map(() => []);
  const failures = [];
  for (let i = 0; i < jobs.length; i++) {
    const job = jobs[i];
    const result = settled[i];
    if (result.status === "fulfilled") perGrader[job.graderIdx].push(result.value);
    else {
      const error = result.reason instanceof Error ? result.reason.message : String(result.reason);
      failures.push({ graderIdx: job.graderIdx, runId: job.runId, error });
    }
  }
  const survivingGraders = perGrader.filter((ranking) => ranking.length > 0);
  if (survivingGraders.length === 0) {
    throw new Error(
      `prmEnsembleBestOfN: every grader failed on every candidate (${failures.length} call(s)). First error: ${failures[0]?.error ?? "unknown"}`
    );
  }
  for (const ranking of survivingGraders)
    ranking.sort((a, b) => b.aggregateScore - a.aggregateScore);
  const bordaScores = /* @__PURE__ */ new Map();
  for (const ranking of survivingGraders) {
    ranking.forEach((g, rank) => {
      bordaScores.set(g.runId, (bordaScores.get(g.runId) ?? 0) + (ranking.length - rank));
    });
  }
  const byRun = /* @__PURE__ */ new Map();
  for (const ranking of survivingGraders) {
    for (const g of ranking) if (!byRun.has(g.runId)) byRun.set(g.runId, g);
  }
  const ranked = [...byRun.values()].sort(
    (a, b) => (bordaScores.get(b.runId) ?? 0) - (bordaScores.get(a.runId) ?? 0)
  );
  const mean = ranked.reduce((a, g) => a + g.aggregateScore, 0) / ranked.length;
  const variance = ranked.reduce((a, g) => a + (g.aggregateScore - mean) ** 2, 0) / ranked.length;
  return { winner: ranked[0], ranked, stdDev: Math.sqrt(variance) };
}

// src/prm/rubric.ts
var PrmGrader = class {
  constructor(rubrics) {
    this.rubrics = rubrics;
    if (rubrics.length === 0) throw new Error("PrmGrader: at least 1 rubric required");
  }
  rubrics;
  /**
   * Grade every eligible span in a run. Emits a JudgeVerdict span for each
   * (rubric × span) verdict so the result is visible to downstream pipelines
   * (judgeAgreementView, etc.) — PRM is just "a judge that runs per span."
   */
  async grade(store, runId) {
    const trajectory = await buildTrajectory(store, runId);
    const emitter = new TraceEmitter(store, { runId });
    const steps = [];
    let ungraded = 0;
    for (let i = 0; i < trajectory.steps.length; i++) {
      const step = trajectory.steps[i];
      const ctx = {
        trajectory,
        step,
        prior: trajectory.steps.slice(0, i),
        next: trajectory.steps.slice(i + 1)
      };
      let gradedThis = false;
      for (const rubric of this.rubrics) {
        if (rubric.kinds && !rubric.kinds.includes(step.span.kind)) continue;
        const verdict = await rubric.grade(ctx);
        if (verdict === null) continue;
        const weight = rubric.weight ?? 1;
        steps.push({
          spanId: step.span.spanId,
          rubricId: rubric.id,
          score: verdict.score,
          weight,
          rationale: verdict.rationale,
          evidence: verdict.evidence
        });
        gradedThis = true;
        await emitter.recordJudge({
          judgeId: `prm:${rubric.id}`,
          targetSpanId: step.span.spanId,
          dimension: "step_quality",
          score: verdict.score,
          rationale: verdict.rationale,
          evidence: verdict.evidence,
          name: `prm:${rubric.id}`
        });
      }
      if (!gradedThis) ungraded++;
    }
    const totalWeight = steps.reduce((a, s) => a + s.weight, 0);
    const aggregateScore = totalWeight === 0 ? 0 : steps.reduce((a, s) => a + s.score * s.weight, 0) / totalWeight;
    return { runId, steps, aggregateScore, gradedCount: steps.length, ungradedCount: ungraded };
  }
};
function isPrmVerdict(verdict) {
  return verdict.judgeId.startsWith("prm:");
}
export {
  PrmGrader,
  exportTrainingData,
  isPrmVerdict,
  nonRefusalRubric,
  outputLengthRubric,
  prmBestOfN,
  prmEnsembleBestOfN,
  toNdjson,
  toolIntentAlignmentRubric,
  toolNonRedundantRubric,
  toolSuccessRubric
};
//# sourceMappingURL=index.js.map