@intellectif/lk-core 0.19.0 → 0.21.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +47 -0
- package/README.md +31 -2
- package/dist/{activity-f0bgZSTP.d.cts → activity-xhGV-Aeh.d.cts} +31 -4
- package/dist/{activity-f0bgZSTP.d.ts → activity-xhGV-Aeh.d.ts} +31 -4
- package/dist/ai-check.cjs +5 -5
- package/dist/ai-check.d.cts +1 -1
- package/dist/ai-check.d.ts +1 -1
- package/dist/ai-check.js +2 -2
- package/dist/{chunk-BIJCO2HM.js → chunk-IHOEP4EB.js} +2 -2
- package/dist/{chunk-FVZJ4X3Y.cjs → chunk-NRTP7WCW.cjs} +4 -4
- package/dist/{chunk-FVZJ4X3Y.cjs.map → chunk-NRTP7WCW.cjs.map} +1 -1
- package/dist/{chunk-MMEQKFFQ.js → chunk-Q7MV3IXJ.js} +196 -8
- package/dist/chunk-Q7MV3IXJ.js.map +1 -0
- package/dist/{chunk-COBK2RUX.cjs → chunk-WQVZPIN7.cjs} +197 -9
- package/dist/chunk-WQVZPIN7.cjs.map +1 -0
- package/dist/{index-bpuDhvXV.d.cts → index-BuD8Hc8o.d.cts} +1 -1
- package/dist/{index-DMCM3e3P.d.ts → index-DnJ_PI4d.d.ts} +1 -1
- package/dist/index.cjs +175 -9
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +177 -7
- package/dist/index.d.ts +177 -7
- package/dist/index.js +171 -5
- package/dist/index.js.map +1 -1
- package/dist/schemas.d.cts +2 -2
- package/dist/schemas.d.ts +2 -2
- package/dist/scoring.cjs +14 -2
- package/dist/scoring.cjs.map +1 -1
- package/dist/scoring.d.cts +240 -4
- package/dist/scoring.d.ts +240 -4
- package/dist/scoring.js +13 -1
- package/dist/xapi.d.cts +1 -1
- package/dist/xapi.d.ts +1 -1
- package/package.json +1 -1
- package/vectors/README.md +10 -0
- package/vectors/scoring.json +2127 -1
- package/dist/chunk-COBK2RUX.cjs.map +0 -1
- package/dist/chunk-MMEQKFFQ.js.map +0 -1
- /package/dist/{chunk-BIJCO2HM.js.map → chunk-IHOEP4EB.js.map} +0 -0
package/dist/index.js
CHANGED
|
@@ -45,15 +45,18 @@ import {
|
|
|
45
45
|
checkAiHint,
|
|
46
46
|
hintRevealsAnswer,
|
|
47
47
|
readAiTextResult
|
|
48
|
-
} from "./chunk-
|
|
48
|
+
} from "./chunk-IHOEP4EB.js";
|
|
49
49
|
import {
|
|
50
|
+
DEFAULT_ITEM_SCORING_POLICY,
|
|
50
51
|
DEFAULT_PASS_THRESHOLD,
|
|
52
|
+
ITEM_SCORING_MAX_RETRIES,
|
|
51
53
|
alignReadAloud,
|
|
52
54
|
classifyBand,
|
|
53
55
|
composeAssessmentScore,
|
|
54
56
|
computePassThreshold,
|
|
55
57
|
diffDictationChars,
|
|
56
58
|
evaluate,
|
|
59
|
+
evaluateTries,
|
|
57
60
|
gradeFromRubric,
|
|
58
61
|
gradeReadAloud,
|
|
59
62
|
gte,
|
|
@@ -61,10 +64,13 @@ import {
|
|
|
61
64
|
inspectWav,
|
|
62
65
|
outcomeFromGrade,
|
|
63
66
|
outcomeFromUnscorable,
|
|
67
|
+
resolveItemScoringPolicy,
|
|
64
68
|
roundGrade,
|
|
65
69
|
score,
|
|
70
|
+
scoreTries,
|
|
71
|
+
validateItemScoringPolicy,
|
|
66
72
|
validateSpeechAssessment
|
|
67
|
-
} from "./chunk-
|
|
73
|
+
} from "./chunk-Q7MV3IXJ.js";
|
|
68
74
|
import {
|
|
69
75
|
ActivitySchemaError,
|
|
70
76
|
AiPermissionsSchema,
|
|
@@ -208,6 +214,106 @@ function contentHash(value) {
|
|
|
208
214
|
return fingerprint(canonicalJson(value));
|
|
209
215
|
}
|
|
210
216
|
|
|
217
|
+
// src/delivery.ts
|
|
218
|
+
var OPEN_DELIVERY_POLICY = Object.freeze({
|
|
219
|
+
feedback: true,
|
|
220
|
+
solutions: true,
|
|
221
|
+
hints: true,
|
|
222
|
+
ai: Object.freeze({ hints: true, explanations: true })
|
|
223
|
+
});
|
|
224
|
+
var SETTINGS = ["feedback", "solutions", "hints"];
|
|
225
|
+
var AI_SETTINGS = ["hints", "explanations"];
|
|
226
|
+
function allows(value) {
|
|
227
|
+
return value === void 0 || value === null || value === true;
|
|
228
|
+
}
|
|
229
|
+
function resolveDeliveryPolicy(policy) {
|
|
230
|
+
if (policy === void 0 || policy === null) {
|
|
231
|
+
return OPEN_DELIVERY_POLICY;
|
|
232
|
+
}
|
|
233
|
+
if (typeof policy !== "object" || Array.isArray(policy)) {
|
|
234
|
+
return {
|
|
235
|
+
feedback: false,
|
|
236
|
+
solutions: false,
|
|
237
|
+
hints: false,
|
|
238
|
+
ai: { hints: false, explanations: false }
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
const given = policy;
|
|
242
|
+
const ai = given.ai;
|
|
243
|
+
const aiGiven = ai === void 0 || ai === null || ai === true ? {} : typeof ai === "object" && !Array.isArray(ai) ? ai : { hints: false, explanations: false };
|
|
244
|
+
return {
|
|
245
|
+
feedback: allows(given.feedback),
|
|
246
|
+
solutions: allows(given.solutions),
|
|
247
|
+
hints: allows(given.hints),
|
|
248
|
+
ai: { hints: allows(aiGiven.hints), explanations: allows(aiGiven.explanations) }
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
function combineDeliveryPolicies(...policies) {
|
|
252
|
+
const resolved = policies.map(resolveDeliveryPolicy);
|
|
253
|
+
const every = (read) => resolved.every(read);
|
|
254
|
+
return {
|
|
255
|
+
feedback: every((one) => one.feedback),
|
|
256
|
+
solutions: every((one) => one.solutions),
|
|
257
|
+
hints: every((one) => one.hints),
|
|
258
|
+
ai: {
|
|
259
|
+
hints: every((one) => one.ai.hints),
|
|
260
|
+
explanations: every((one) => one.ai.explanations)
|
|
261
|
+
}
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
function validateDeliveryPolicy(policy) {
|
|
265
|
+
const issues = [];
|
|
266
|
+
if (policy === void 0 || policy === null) {
|
|
267
|
+
return { success: true, data: {} };
|
|
268
|
+
}
|
|
269
|
+
if (typeof policy !== "object" || Array.isArray(policy)) {
|
|
270
|
+
return {
|
|
271
|
+
success: false,
|
|
272
|
+
issues: [{ path: "", message: "A delivery policy is an object of settings." }]
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
const given = policy;
|
|
276
|
+
for (const key of Object.keys(given)) {
|
|
277
|
+
if (key === "ai") {
|
|
278
|
+
continue;
|
|
279
|
+
}
|
|
280
|
+
if (!SETTINGS.includes(key)) {
|
|
281
|
+
issues.push({
|
|
282
|
+
path: key,
|
|
283
|
+
message: `"${key}" is not a delivery setting. The settings are feedback, solutions, hints and ai.`
|
|
284
|
+
});
|
|
285
|
+
continue;
|
|
286
|
+
}
|
|
287
|
+
const value = given[key];
|
|
288
|
+
if (value !== true && value !== false && value !== null && value !== void 0) {
|
|
289
|
+
issues.push({ path: key, message: `"${key}" must be true, false or null.` });
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
const ai = given.ai;
|
|
293
|
+
if (ai !== void 0 && ai !== null && ai !== true && ai !== false) {
|
|
294
|
+
if (typeof ai !== "object" || Array.isArray(ai)) {
|
|
295
|
+
issues.push({
|
|
296
|
+
path: "ai",
|
|
297
|
+
message: '"ai" must be true, false, or an object of hints and explanations.'
|
|
298
|
+
});
|
|
299
|
+
} else {
|
|
300
|
+
for (const [key, value] of Object.entries(ai)) {
|
|
301
|
+
if (!AI_SETTINGS.includes(key)) {
|
|
302
|
+
issues.push({
|
|
303
|
+
path: `ai.${key}`,
|
|
304
|
+
message: `"ai.${key}" is not an AI setting. The settings are hints and explanations.`
|
|
305
|
+
});
|
|
306
|
+
continue;
|
|
307
|
+
}
|
|
308
|
+
if (value !== true && value !== false && value !== null && value !== void 0) {
|
|
309
|
+
issues.push({ path: `ai.${key}`, message: `"ai.${key}" must be true, false or null.` });
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
return issues.length > 0 ? { success: false, issues } : { success: true, data: given };
|
|
315
|
+
}
|
|
316
|
+
|
|
211
317
|
// src/shuffle.ts
|
|
212
318
|
function hashSeed(input) {
|
|
213
319
|
let h = 2166136261 >>> 0;
|
|
@@ -511,6 +617,8 @@ function planSlot(slot, points) {
|
|
|
511
617
|
}
|
|
512
618
|
function planAttempt(entries, options = {}) {
|
|
513
619
|
const { seed, shuffleEntries, points } = options;
|
|
620
|
+
const delivery = frozenDelivery(options.delivery);
|
|
621
|
+
const scoring = frozenScoring(options.scoring);
|
|
514
622
|
const slots = flattenSequence(entries, {
|
|
515
623
|
...shuffleEntries !== void 0 ? { shuffleEntries } : {},
|
|
516
624
|
...seed !== void 0 ? { seed } : {}
|
|
@@ -525,7 +633,22 @@ function planAttempt(entries, options = {}) {
|
|
|
525
633
|
// The plan's own fingerprint covers the slots verbatim — identity, order,
|
|
526
634
|
// points and content hashes — so one stored value answers "is this still
|
|
527
635
|
// the paper that was sat?" and the per-slot hashes then say what moved.
|
|
528
|
-
|
|
636
|
+
// A policy joins it when there is one: the same paper sat with hints and
|
|
637
|
+
// without is two different attempts. Without one, the fingerprint is the
|
|
638
|
+
// one every plan before policies had.
|
|
639
|
+
// So does a scoring policy: the same answers are worth something else when
|
|
640
|
+
// a hint costs a tenth of the marks. Each joins only when it is there, so
|
|
641
|
+
// a plan with neither — or with a delivery policy alone — keeps the
|
|
642
|
+
// fingerprint it always had.
|
|
643
|
+
planHash: contentHash(
|
|
644
|
+
delivery === void 0 && scoring === void 0 ? planned : {
|
|
645
|
+
slots: planned,
|
|
646
|
+
...delivery !== void 0 ? { delivery } : {},
|
|
647
|
+
...scoring !== void 0 ? { scoring } : {}
|
|
648
|
+
}
|
|
649
|
+
),
|
|
650
|
+
...delivery !== void 0 ? { delivery } : {},
|
|
651
|
+
...scoring !== void 0 ? { scoring } : {},
|
|
529
652
|
slots: planned,
|
|
530
653
|
totalPoints
|
|
531
654
|
};
|
|
@@ -540,6 +663,8 @@ function verifyAttemptPlan(plan, current) {
|
|
|
540
663
|
const changedCueSlotIds = [];
|
|
541
664
|
const changedPointsSlotIds = [];
|
|
542
665
|
const reorderedSlotIds = [];
|
|
666
|
+
const deliveryChanged = contentHash(plan.delivery ?? null) !== contentHash(current.delivery ?? null);
|
|
667
|
+
const scoringChanged = contentHash(plan.scoring ?? null) !== contentHash(current.scoring ?? null);
|
|
543
668
|
for (const slot of plan.slots) {
|
|
544
669
|
const now = after.get(slot.slotId);
|
|
545
670
|
if (now === void 0) {
|
|
@@ -562,16 +687,47 @@ function verifyAttemptPlan(plan, current) {
|
|
|
562
687
|
}
|
|
563
688
|
}
|
|
564
689
|
return {
|
|
565
|
-
matches: missingSlotIds.length === 0 && addedSlotIds.length === 0 && changedSlotIds.length === 0 && changedStimulusSlotIds.length === 0 && changedCueSlotIds.length === 0 && changedPointsSlotIds.length === 0 && reorderedSlotIds.length === 0,
|
|
690
|
+
matches: missingSlotIds.length === 0 && addedSlotIds.length === 0 && changedSlotIds.length === 0 && changedStimulusSlotIds.length === 0 && changedCueSlotIds.length === 0 && changedPointsSlotIds.length === 0 && reorderedSlotIds.length === 0 && !deliveryChanged && !scoringChanged,
|
|
566
691
|
missingSlotIds,
|
|
567
692
|
addedSlotIds,
|
|
568
693
|
changedSlotIds,
|
|
569
694
|
changedStimulusSlotIds,
|
|
570
695
|
changedCueSlotIds,
|
|
571
696
|
changedPointsSlotIds,
|
|
572
|
-
reorderedSlotIds
|
|
697
|
+
reorderedSlotIds,
|
|
698
|
+
// Present only when it is true, as `rejectedSlotIds` is on a composed
|
|
699
|
+
// score: a drift report between two plans without a policy — every one
|
|
700
|
+
// written before policies existed — keeps exactly the shape it had.
|
|
701
|
+
...deliveryChanged ? { deliveryChanged: true } : {},
|
|
702
|
+
...scoringChanged ? { scoringChanged: true } : {}
|
|
573
703
|
};
|
|
574
704
|
}
|
|
705
|
+
function frozenDelivery(policy) {
|
|
706
|
+
if (policy === void 0 || policy === null) {
|
|
707
|
+
return void 0;
|
|
708
|
+
}
|
|
709
|
+
const checked = validateDeliveryPolicy(policy);
|
|
710
|
+
if (!checked.success) {
|
|
711
|
+
const first = checked.issues[0];
|
|
712
|
+
throw new Error(
|
|
713
|
+
`planAttempt: the delivery policy is not one to record \u2014 ${first?.message ?? "invalid"}` + (checked.issues.length > 1 ? ` (and ${checked.issues.length - 1} more)` : "")
|
|
714
|
+
);
|
|
715
|
+
}
|
|
716
|
+
return resolveDeliveryPolicy(checked.data);
|
|
717
|
+
}
|
|
718
|
+
function frozenScoring(policy) {
|
|
719
|
+
if (policy === void 0 || policy === null) {
|
|
720
|
+
return void 0;
|
|
721
|
+
}
|
|
722
|
+
const checked = validateItemScoringPolicy(policy);
|
|
723
|
+
if (!checked.success) {
|
|
724
|
+
const first = checked.issues[0];
|
|
725
|
+
throw new Error(
|
|
726
|
+
`planAttempt: the scoring policy is not one to record \u2014 ${first?.message ?? "invalid"}` + (checked.issues.length > 1 ? ` (and ${checked.issues.length - 1} more)` : "")
|
|
727
|
+
);
|
|
728
|
+
}
|
|
729
|
+
return resolveItemScoringPolicy(checked.data);
|
|
730
|
+
}
|
|
575
731
|
function missingOutcome(slot, policy) {
|
|
576
732
|
if (typeof policy === "function") {
|
|
577
733
|
return policy(slot);
|
|
@@ -1509,6 +1665,7 @@ export {
|
|
|
1509
1665
|
ActivitySchemaError,
|
|
1510
1666
|
AiPermissionsSchema,
|
|
1511
1667
|
BlankConfigSchema,
|
|
1668
|
+
DEFAULT_ITEM_SCORING_POLICY,
|
|
1512
1669
|
DEFAULT_PASS_THRESHOLD,
|
|
1513
1670
|
DICTATION_MAX_ACCEPTED_TRANSCRIPTS,
|
|
1514
1671
|
DICTATION_MAX_EQUIVALENCES,
|
|
@@ -1527,6 +1684,7 @@ export {
|
|
|
1527
1684
|
GapSelectDataSchema,
|
|
1528
1685
|
GapSelectGapSchema,
|
|
1529
1686
|
INTERACTIVE_VIDEO_ITEM_TYPES,
|
|
1687
|
+
ITEM_SCORING_MAX_RETRIES,
|
|
1530
1688
|
ItemGroupSchema,
|
|
1531
1689
|
MediaPlaybackSchema,
|
|
1532
1690
|
MediaSchema,
|
|
@@ -1537,6 +1695,7 @@ export {
|
|
|
1537
1695
|
MultipleChoiceOptionMediaSchema,
|
|
1538
1696
|
MultipleChoiceOptionSchema,
|
|
1539
1697
|
NativeControlHintSchema,
|
|
1698
|
+
OPEN_DELIVERY_POLICY,
|
|
1540
1699
|
READ_ALOUD_MAX_DIMENSION_WEIGHT,
|
|
1541
1700
|
READ_ALOUD_MAX_REFERENCE_LENGTH,
|
|
1542
1701
|
READ_ALOUD_MAX_SECONDS,
|
|
@@ -1589,6 +1748,7 @@ export {
|
|
|
1589
1748
|
checkAiExplanation,
|
|
1590
1749
|
checkAiHint,
|
|
1591
1750
|
classifyBand,
|
|
1751
|
+
combineDeliveryPolicies,
|
|
1592
1752
|
composeAssessmentScore,
|
|
1593
1753
|
composeTimelineScore,
|
|
1594
1754
|
computePassThreshold,
|
|
@@ -1605,6 +1765,7 @@ export {
|
|
|
1605
1765
|
diffResponses,
|
|
1606
1766
|
entryKeyOf,
|
|
1607
1767
|
evaluate,
|
|
1768
|
+
evaluateTries,
|
|
1608
1769
|
fillInTheBlanksJsonSchema,
|
|
1609
1770
|
fillInTheBlanksType,
|
|
1610
1771
|
fingerprint,
|
|
@@ -1639,11 +1800,14 @@ export {
|
|
|
1639
1800
|
redactItemGroup,
|
|
1640
1801
|
registerActivityType,
|
|
1641
1802
|
registeredActivityTypes,
|
|
1803
|
+
resolveDeliveryPolicy,
|
|
1804
|
+
resolveItemScoringPolicy,
|
|
1642
1805
|
resolvePlaybackPolicy,
|
|
1643
1806
|
restoreAttemptState,
|
|
1644
1807
|
restoreMediaPlayLedger,
|
|
1645
1808
|
roundGrade,
|
|
1646
1809
|
score,
|
|
1810
|
+
scoreTries,
|
|
1647
1811
|
scoredItemsFromPlan,
|
|
1648
1812
|
seededShuffle,
|
|
1649
1813
|
serializeAttemptState,
|
|
@@ -1652,9 +1816,11 @@ export {
|
|
|
1652
1816
|
stimulusJsonSchema,
|
|
1653
1817
|
stimulusMediaKey,
|
|
1654
1818
|
validateActivity,
|
|
1819
|
+
validateDeliveryPolicy,
|
|
1655
1820
|
validateDraft,
|
|
1656
1821
|
validateItemGroup,
|
|
1657
1822
|
validateItemGroupDraft,
|
|
1823
|
+
validateItemScoringPolicy,
|
|
1658
1824
|
validateSpeechAssessment,
|
|
1659
1825
|
validateXAPIStatement,
|
|
1660
1826
|
verifyAttemptPlan,
|