| 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); |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| 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)), |
| }; |
| } |
|
|