mirror of
https://github.com/OpenSquawk/OpenSquawk
synced 2026-08-12 04:15:39 +08:00
Tighten readback matching and add classroom home link
This commit is contained in:
@@ -1170,6 +1170,7 @@
|
|||||||
<div class="footer-meta">
|
<div class="footer-meta">
|
||||||
<span class="muted small">© 2025 OpenSquawk. All rights reserved.</span>
|
<span class="muted small">© 2025 OpenSquawk. All rights reserved.</span>
|
||||||
<NuxtLink to="/feedback" target="_blank" class="link ml-2">Give feedback ›</NuxtLink>
|
<NuxtLink to="/feedback" target="_blank" class="link ml-2">Give feedback ›</NuxtLink>
|
||||||
|
<a href="/" class="link ml-4">Back to opensquawk.de ›</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
@@ -1359,6 +1360,20 @@ function similarity(a: string, b: string): number {
|
|||||||
return 1 - distance / maxLen
|
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<ModuleDef[]>(learnModules)
|
const modules = shallowRef<ModuleDef[]>(learnModules)
|
||||||
|
|
||||||
type LessonSearchHit = {
|
type LessonSearchHit = {
|
||||||
@@ -3077,15 +3092,47 @@ const fieldStates = computed<Record<string, FieldState>>(() => {
|
|||||||
const alternatives = field.alternatives?.(scenario.value) ?? []
|
const alternatives = field.alternatives?.(scenario.value) ?? []
|
||||||
const options = [expected, ...alternatives].map(norm).filter(Boolean)
|
const options = [expected, ...alternatives].map(norm).filter(Boolean)
|
||||||
const normalizedAnswer = norm(answer)
|
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] = {
|
map[field.key] = {
|
||||||
key: field.key,
|
key: field.key,
|
||||||
label: field.label,
|
label: field.label,
|
||||||
expected,
|
expected,
|
||||||
answer,
|
answer,
|
||||||
similarity: answer ? best : 0,
|
similarity: answer ? best : 0,
|
||||||
pass
|
pass: Boolean(answer && pass)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return map
|
return map
|
||||||
|
|||||||
Reference in New Issue
Block a user