feat: phase 4 of AI pipeline hardening — quiz & content quality
- src/lib/random.js: Fisher–Yates shuffle/sample/pickInt; replace every
biased .sort(() => 0.5 - Math.random()) site in testService.
- testService: debias correctIndex via prompt + runtime re-roll (up to 2x
when one position holds >50%); quality gate rejecting <4 distinct
options, banned filler ("all of the above" etc) and explanations
shorter than 20 chars; dedup new questions against the existing bank
via normalised question text.
- Quiz schema/tool/prompt require difficulty ('easy'|'medium'|'hard');
db.getQuizBank defaults legacy records to 'medium' on read.
- learningService.generateCustomTopic: kebab-case slug ID from the
polished label with collision suffixes; default learning_relevance
'standard' when the model omits it.
- Tests for random helpers, dedup/quality-gate behaviour and the
extended quiz schema.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
29
src/lib/random.js
Normal file
29
src/lib/random.js
Normal file
@@ -0,0 +1,29 @@
|
||||
/**
|
||||
* Shared randomness helpers.
|
||||
*
|
||||
* `Array.prototype.sort(() => 0.5 - Math.random())` is biased — modern V8
|
||||
* sorts use Timsort, which compares each element more than once and skews
|
||||
* the resulting permutation. Use `shuffle` for anything user-visible
|
||||
* (quiz options, review topic selection, leaderboards).
|
||||
*/
|
||||
|
||||
export function shuffle(arr) {
|
||||
const out = [...arr];
|
||||
for (let i = out.length - 1; i > 0; i--) {
|
||||
const j = Math.floor(Math.random() * (i + 1));
|
||||
[out[i], out[j]] = [out[j], out[i]];
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
export function sample(arr, n) {
|
||||
if (n <= 0) return [];
|
||||
if (n >= arr.length) return shuffle(arr);
|
||||
return shuffle(arr).slice(0, n);
|
||||
}
|
||||
|
||||
export function pickInt(min, maxInclusive) {
|
||||
const lo = Math.ceil(min);
|
||||
const hi = Math.floor(maxInclusive);
|
||||
return lo + Math.floor(Math.random() * (hi - lo + 1));
|
||||
}
|
||||
Reference in New Issue
Block a user