From c6c67e00a0252b6c4f2a2501e62859f28aa460e4 Mon Sep 17 00:00:00 2001 From: Remi <73385395+itsrubberduck@users.noreply.github.com> Date: Sun, 19 Oct 2025 16:12:52 +0200 Subject: [PATCH] Tighten readback matching and add classroom home link --- app/pages/classroom.vue | 53 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 50 insertions(+), 3 deletions(-) diff --git a/app/pages/classroom.vue b/app/pages/classroom.vue index e18411c..e5dadb7 100644 --- a/app/pages/classroom.vue +++ b/app/pages/classroom.vue @@ -1170,6 +1170,7 @@ @@ -1359,6 +1360,20 @@ function similarity(a: string, b: string): number { return 1 - distance / maxLen } +function allowedDistance(length: number): number { + if (length <= 12) return 0 + if (length <= 24) return 1 + if (length <= 36) return 2 + return 3 +} + +function tightenedThreshold(length: number, base: number): number { + if (length >= 36) return Math.max(base, 0.9) + if (length >= 24) return Math.max(base, 0.92) + if (length >= 16) return Math.max(base, 0.94) + return Math.max(base, 0.97) +} + const modules = shallowRef(learnModules) type LessonSearchHit = { @@ -3077,15 +3092,47 @@ const fieldStates = computed>(() => { const alternatives = field.alternatives?.(scenario.value) ?? [] const options = [expected, ...alternatives].map(norm).filter(Boolean) const normalizedAnswer = norm(answer) - const best = options.length ? Math.max(...options.map(option => similarity(normalizedAnswer, option))) : 0 - const pass = answer ? best >= (field.threshold ?? 0.82) : false + + let best = 0 + let pass = false + + if (answer && options.length) { + for (const option of options) { + if (!option) continue + if (normalizedAnswer === option) { + best = 1 + pass = true + break + } + + const distance = lev(normalizedAnswer, option) + const span = Math.max(option.length, normalizedAnswer.length, 1) + const score = 1 - distance / span + if (score > best) { + best = score + } + + const allowance = allowedDistance(span) + if (allowance <= 0) continue + + const threshold = tightenedThreshold(span, field.threshold ?? 0.82) + if (distance <= allowance && score >= threshold) { + pass = true + } + } + + if (!pass && !best && options.length) { + best = Math.max(...options.map(option => similarity(normalizedAnswer, option))) + } + } + map[field.key] = { key: field.key, label: field.label, expected, answer, similarity: answer ? best : 0, - pass + pass: Boolean(answer && pass) } } return map