over / src /lib /commitment /confidence.ts
luguog's picture
Deploy mailbox-automation Next.js app
9646e24 verified
Raw
History Blame Contribute Delete
2.56 kB
import { CommitmentConfidenceBreakdown, CommitmentMetrics } from "@/types";
function clamp01(value: number): number {
if (!Number.isFinite(value)) return 0;
return Math.max(0, Math.min(1, value));
}
function rate(success: number, total: number, priorAlpha: number, priorBeta: number): number {
if (!Number.isFinite(success) || !Number.isFinite(total)) return clamp01(priorAlpha / (priorAlpha + priorBeta));
const s = Math.max(0, success);
const t = Math.max(0, total);
const denom = t + priorAlpha + priorBeta;
if (denom <= 0) return 0;
return clamp01((s + priorAlpha) / denom);
}
/**
* Convert historical outcomes into a calibrated probability breakdown.
*
* This is intentionally simple and fully auditable:
* - each component is an empirical success rate
* - overall is multiplicative, matching the spec
*/
export function computeConfidenceBreakdown(metrics: CommitmentMetrics): CommitmentConfidenceBreakdown {
const capability = rate(metrics.capability.success, metrics.capability.total, 9, 1);
const inputsAvailable = rate(metrics.inputsAvailable.success, metrics.inputsAvailable.total, 9, 1);
const toolCompletion = rate(metrics.toolCompletion.success, metrics.toolCompletion.total, 9, 1);
const qualityApproval = rate(metrics.qualityApproval.success, metrics.qualityApproval.total, 9, 1);
const acceptedWithoutRevision = rate(
metrics.acceptedWithoutRevision.success,
metrics.acceptedWithoutRevision.total,
7,
3,
);
const overall = clamp01(
capability *
inputsAvailable *
toolCompletion *
qualityApproval *
acceptedWithoutRevision,
);
return {
capability,
inputsAvailable,
toolCompletion,
qualityApproval,
acceptedWithoutRevision,
overall,
};
}
function percentile(sorted: number[], p: number): number {
if (sorted.length === 0) return 0;
const idx = (sorted.length - 1) * p;
const lo = Math.floor(idx);
const hi = Math.ceil(idx);
if (lo === hi) return sorted[lo];
const weight = idx - lo;
return sorted[lo] * (1 - weight) + sorted[hi] * weight;
}
export function estimateCompletionTimes(metrics: CommitmentMetrics): {
p50Ms: number | null;
p90Ms: number | null;
} {
const durations = Array.isArray(metrics.durationsMs) ? metrics.durationsMs.filter((d) => d > 0) : [];
if (durations.length < 5) {
return { p50Ms: null, p90Ms: null };
}
const sorted = [...durations].sort((a, b) => a - b);
return {
p50Ms: Math.round(percentile(sorted, 0.5)),
p90Ms: Math.round(percentile(sorted, 0.9)),
};
}