const { normalizeText } = require('./scanGrounding'); const clamp = (value, min, max) => Math.min(max, Math.max(min, value)); // Two DIFFERENT models independently naming the same species is genuine // evidence beyond either model's self-reported confidence, so agreement // earns a bonus on top of the better single estimate. const REVIEW_AGREEMENT_BONUS = 0.2; const REVIEW_AGREEMENT_CONFIDENCE_CAP = 0.97; // Agreement must be judged on the RAW model answers, not the grounded ones: // catalog grounding has a genus-level fallback that can collapse two different // species onto the same catalog entry and fake an agreement. const reviewAgreesWithPrimary = (rawPrimary, rawReview) => { if (!rawPrimary || !rawReview) return false; const primaryBotanical = normalizeText(rawPrimary.botanicalName); const primaryName = normalizeText(rawPrimary.name); const botanicalMatch = Boolean(primaryBotanical) && primaryBotanical === normalizeText(rawReview.botanicalName); const nameMatch = Boolean(primaryName) && primaryName === normalizeText(rawReview.name); return botanicalMatch || nameMatch; }; // The review runs on a stronger model chain than the primary, and models are // not calibrated against each other — so on disagreement the review wins even // when it trails the primary by up to this margin. const REVIEW_DISAGREEMENT_MARGIN = 0.05; // A second low-confidence guess is not a verification: the review may only // replace the primary when it agrees with it or is (near-)competitively // confident. On agreement the higher-confidence variant wins (tie goes to // the review, which runs on the stronger model chain). const decideReviewOutcome = ({ primaryResult, reviewResult, agrees }) => { const primaryConfidence = primaryResult?.confidence || 0; const reviewConfidence = reviewResult?.confidence || 0; if (agrees) { return { accept: true, replace: reviewConfidence >= primaryConfidence, reason: 'review-confirmed-primary', confidence: clamp( Math.max(primaryConfidence, reviewConfidence) + REVIEW_AGREEMENT_BONUS, 0.05, REVIEW_AGREEMENT_CONFIDENCE_CAP, ), }; } if (reviewConfidence >= primaryConfidence - REVIEW_DISAGREEMENT_MARGIN) { return { accept: true, replace: true, reason: 'review-overrode-primary' }; } return { accept: false, replace: false, reason: 'review-rejected-low-confidence' }; }; module.exports = { decideReviewOutcome, reviewAgreesWithPrimary, };