@intellectif/lk-core 0.13.1 → 0.14.1
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 +29 -0
- package/README.md +1 -1
- package/dist/{activity-DfAmJ1sl.d.cts → activity-BVkg9VDo.d.cts} +153 -4
- package/dist/{activity-DfAmJ1sl.d.ts → activity-BVkg9VDo.d.ts} +153 -4
- package/dist/{chunk-DQKJUMUW.cjs → chunk-2NIALQEH.cjs} +24 -19
- package/dist/chunk-2NIALQEH.cjs.map +1 -0
- package/dist/{chunk-XKKKWKRH.js → chunk-6RND47QE.js} +694 -126
- package/dist/chunk-6RND47QE.js.map +1 -0
- package/dist/{chunk-FT7SAC3X.cjs → chunk-E7LPIZXI.cjs} +13 -10
- package/dist/chunk-E7LPIZXI.cjs.map +1 -0
- package/dist/chunk-J5OIRIOW.cjs +1034 -0
- package/dist/chunk-J5OIRIOW.cjs.map +1 -0
- package/dist/{chunk-PYP3HCRQ.js → chunk-KTDOZR7Y.js} +7 -4
- package/dist/chunk-KTDOZR7Y.js.map +1 -0
- package/dist/{chunk-Y36S3X3L.js → chunk-NBADQ3JU.js} +7 -2
- package/dist/chunk-NBADQ3JU.js.map +1 -0
- package/dist/chunk-RTIR6R2U.js +1034 -0
- package/dist/chunk-RTIR6R2U.js.map +1 -0
- package/dist/{chunk-XONKWL6D.cjs → chunk-VCXIPLEW.cjs} +585 -17
- package/dist/chunk-VCXIPLEW.cjs.map +1 -0
- package/dist/{index-BefQ8FAS.d.cts → index-BE1bM2Wi.d.cts} +238 -16
- package/dist/{index-BiIvdASA.d.ts → index-v2_-Fyn3.d.ts} +238 -16
- package/dist/index.cjs +110 -80
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +45 -17
- package/dist/index.d.ts +45 -17
- package/dist/index.js +35 -5
- package/dist/index.js.map +1 -1
- package/dist/schemas.cjs +11 -3
- package/dist/schemas.cjs.map +1 -1
- package/dist/schemas.d.cts +2 -2
- package/dist/schemas.d.ts +2 -2
- package/dist/schemas.js +10 -2
- package/dist/scoring.cjs +23 -3
- package/dist/scoring.cjs.map +1 -1
- package/dist/scoring.d.cts +471 -16
- package/dist/scoring.d.ts +471 -16
- package/dist/scoring.js +24 -4
- package/dist/xapi.cjs +3 -3
- package/dist/xapi.d.cts +1 -1
- package/dist/xapi.d.ts +1 -1
- package/dist/xapi.js +2 -2
- package/package.json +1 -1
- package/vectors/README.md +63 -3
- package/vectors/replay.d.mts +7 -2
- package/vectors/replay.mjs +83 -1
- package/vectors/scoring.json +10875 -2
- package/dist/chunk-DQKJUMUW.cjs.map +0 -1
- package/dist/chunk-FGN4LLSP.js +0 -387
- package/dist/chunk-FGN4LLSP.js.map +0 -1
- package/dist/chunk-FT7SAC3X.cjs.map +0 -1
- package/dist/chunk-PYP3HCRQ.js.map +0 -1
- package/dist/chunk-UHQVIC3P.cjs +0 -387
- package/dist/chunk-UHQVIC3P.cjs.map +0 -1
- package/dist/chunk-XKKKWKRH.js.map +0 -1
- package/dist/chunk-XONKWL6D.cjs.map +0 -1
- package/dist/chunk-Y36S3X3L.js.map +0 -1
package/dist/scoring.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export { T as TextMatchPolicy,
|
|
1
|
+
import { x as ItemOutcome, r as Grader, g as DictationData, A as ActivityData, R as ReadAloudData, _ as ValidationResult, P as ReadAloudLearnerResponse, q as GradeRecord, L as LearnerResponse, e as ActivityType, W as ScoringResult } from './activity-BVkg9VDo.js';
|
|
2
|
+
export { T as RecordingRef, X as TextMatchPolicy, Y as TextMatchResult, ad as levenshteinDistance, ae as matchText } from './activity-BVkg9VDo.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Rounding for assessment scores.
|
|
@@ -188,6 +188,246 @@ interface AssessmentScore {
|
|
|
188
188
|
*/
|
|
189
189
|
declare function composeAssessmentScore(sections: readonly AssessmentSectionInput[], policy: CompositionPolicy): AssessmentScore;
|
|
190
190
|
|
|
191
|
+
/**
|
|
192
|
+
* Speech assessment: the neutral shape a pronunciation assessor's result is
|
|
193
|
+
* mapped into, and the types a read-aloud grade reads and writes.
|
|
194
|
+
*
|
|
195
|
+
* The SDK calls no assessor and holds no key. The application maps its
|
|
196
|
+
* provider's response into a {@link SpeechAssessment}; the SDK validates that
|
|
197
|
+
* evidence, checks that it belongs to the item and to the learner's recording,
|
|
198
|
+
* and does the arithmetic.
|
|
199
|
+
*/
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* How the assessor classified a word against the reference text: `none`, read
|
|
203
|
+
* as written; `mispronunciation`, read but not well enough; `omission`, in the
|
|
204
|
+
* reference but not heard; `insertion`, heard but not in the reference.
|
|
205
|
+
*/
|
|
206
|
+
type SpeechWordError = 'none' | 'mispronunciation' | 'omission' | 'insertion';
|
|
207
|
+
/** A phoneme the assessor heard instead of the expected one, and its score. */
|
|
208
|
+
interface SpeechPhonemeCandidate {
|
|
209
|
+
/** The phoneme, in the assessment's `phonemeAlphabet`. */
|
|
210
|
+
symbol: string;
|
|
211
|
+
/** 0..100. */
|
|
212
|
+
score: number;
|
|
213
|
+
}
|
|
214
|
+
/** One phoneme of a word, as the assessor measured it. Assessors report different subsets. */
|
|
215
|
+
interface SpeechPhoneme {
|
|
216
|
+
/** The phoneme, in the assessment's `phonemeAlphabet`. Absent when the assessor does not name it. */
|
|
217
|
+
symbol?: string;
|
|
218
|
+
/** 0..100. Absent means not assessed, never 0. */
|
|
219
|
+
accuracy?: number;
|
|
220
|
+
/** Milliseconds from the start of the recording. */
|
|
221
|
+
startMs?: number;
|
|
222
|
+
/** Milliseconds. */
|
|
223
|
+
durationMs?: number;
|
|
224
|
+
/** The phonemes the assessor heard instead, in the order it reported them. */
|
|
225
|
+
heardAs?: SpeechPhonemeCandidate[];
|
|
226
|
+
}
|
|
227
|
+
/** One syllable of a word, as the assessor measured it. */
|
|
228
|
+
interface SpeechSyllable {
|
|
229
|
+
/** The syllable as the assessor reports it. */
|
|
230
|
+
text: string;
|
|
231
|
+
/** The letters of the word the syllable is written with, when the assessor reports them. */
|
|
232
|
+
grapheme?: string;
|
|
233
|
+
/** 0..100. Absent means not assessed, never 0. */
|
|
234
|
+
accuracy?: number;
|
|
235
|
+
/** Milliseconds from the start of the recording. */
|
|
236
|
+
startMs?: number;
|
|
237
|
+
/** Milliseconds. */
|
|
238
|
+
durationMs?: number;
|
|
239
|
+
}
|
|
240
|
+
/** One word of a {@link SpeechAssessment}. */
|
|
241
|
+
interface SpeechWord {
|
|
242
|
+
/** The word as the assessor reports it. */
|
|
243
|
+
text: string;
|
|
244
|
+
/** 0..100. Absent means not assessed — never read as 0. */
|
|
245
|
+
accuracy?: number;
|
|
246
|
+
/** The assessor's classification of the word. */
|
|
247
|
+
error: SpeechWordError;
|
|
248
|
+
/** The provider's own error label, verbatim, for audit. No grade reads it. */
|
|
249
|
+
vendorError?: string;
|
|
250
|
+
/** Milliseconds from the start of the recording. */
|
|
251
|
+
startMs?: number;
|
|
252
|
+
/** Milliseconds. */
|
|
253
|
+
durationMs?: number;
|
|
254
|
+
syllables?: SpeechSyllable[];
|
|
255
|
+
phonemes?: SpeechPhoneme[];
|
|
256
|
+
/**
|
|
257
|
+
* Confidences (0..1) that an unexpected pause came before the word, or that
|
|
258
|
+
* an expected one is missing. Display only: no grade reads them.
|
|
259
|
+
*/
|
|
260
|
+
breaks?: {
|
|
261
|
+
unexpected?: number;
|
|
262
|
+
missing?: number;
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* A pronunciation assessor's result, in the SDK's neutral shape. Every score in
|
|
267
|
+
* it is on a 0..100 scale. Check one with `validateSpeechAssessment`.
|
|
268
|
+
*/
|
|
269
|
+
interface SpeechAssessment {
|
|
270
|
+
assessmentVersion: '1.0';
|
|
271
|
+
/** `no_speech`: the assessor heard nothing to assess — which is never a score of 0. */
|
|
272
|
+
status: 'assessed' | 'no_speech';
|
|
273
|
+
/** `scripted`: measured against a known text. Only a scripted assessment grades a read-aloud item. */
|
|
274
|
+
task: 'scripted' | 'unscripted';
|
|
275
|
+
/** The locale the assessment was made for: canonical, with a region — the rule `ReadAloudData.locale` follows. */
|
|
276
|
+
locale: string;
|
|
277
|
+
/**
|
|
278
|
+
* The text the assessment was made against: the authored reference text,
|
|
279
|
+
* verbatim. Required when scripted, so a grade can refuse evidence made
|
|
280
|
+
* against another text.
|
|
281
|
+
*/
|
|
282
|
+
referenceText?: string;
|
|
283
|
+
/**
|
|
284
|
+
* The `RecordingRef.key` of the recording that was assessed. Required when
|
|
285
|
+
* scripted, so a grade can refuse evidence made for another recording.
|
|
286
|
+
*/
|
|
287
|
+
recordingKey?: string;
|
|
288
|
+
/** Who measured. Evidence from `'ai'` grades only when the caller opts in. */
|
|
289
|
+
assessor: Grader;
|
|
290
|
+
/** What every score is out of. Stated, so an adapter never leaves it to be assumed. */
|
|
291
|
+
scale: 100;
|
|
292
|
+
/** Utterance-level scores, 0..100. An absent score was not assessed; it is never read as 0. */
|
|
293
|
+
scores: {
|
|
294
|
+
accuracy?: number;
|
|
295
|
+
fluency?: number;
|
|
296
|
+
completeness?: number;
|
|
297
|
+
prosody?: number;
|
|
298
|
+
overall?: number;
|
|
299
|
+
};
|
|
300
|
+
/** What the assessor recognised, as text. */
|
|
301
|
+
recognizedText?: string;
|
|
302
|
+
/**
|
|
303
|
+
* Who judged omissions, insertions and mispronunciations. `assessor`: the
|
|
304
|
+
* assessor did, and its word `error` labels are trusted. `none`: it did not,
|
|
305
|
+
* so a heard word that differs from its reference word is marked
|
|
306
|
+
* mispronounced.
|
|
307
|
+
*/
|
|
308
|
+
miscue: 'assessor' | 'none';
|
|
309
|
+
/** The alphabet phoneme symbols are written in. Required whenever a phoneme names a `symbol` or `heardAs`. */
|
|
310
|
+
phonemeAlphabet?: 'ipa' | 'sapi';
|
|
311
|
+
/** The words, in the order the assessor reported them. */
|
|
312
|
+
words: SpeechWord[];
|
|
313
|
+
/** Utterance-level prosody: `monotoneConfidence` is 0..1, display only. */
|
|
314
|
+
prosody?: {
|
|
315
|
+
monotoneConfidence?: number;
|
|
316
|
+
};
|
|
317
|
+
/** Recording quality. Informational only: no grade reads it. */
|
|
318
|
+
signal?: {
|
|
319
|
+
snrDb?: number;
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
/**
|
|
323
|
+
* The server's own measurement of a recording, from `inspectWav`. It is what
|
|
324
|
+
* decides whether a take is plausible speech; a duration the client reported
|
|
325
|
+
* never is.
|
|
326
|
+
*/
|
|
327
|
+
interface SpeechMeasurement {
|
|
328
|
+
/** Length of the recording, in milliseconds. */
|
|
329
|
+
durationMs: number;
|
|
330
|
+
/** Milliseconds of the recording at or above the silence floor. At most `durationMs`. */
|
|
331
|
+
voicedMs: number;
|
|
332
|
+
}
|
|
333
|
+
/**
|
|
334
|
+
* When a take is too little speech to grade. Both fields are required: the SDK
|
|
335
|
+
* holds no default for either, because each is a decision about an
|
|
336
|
+
* application's learners and its assessor.
|
|
337
|
+
*/
|
|
338
|
+
interface SpeechPlausibilityPolicy {
|
|
339
|
+
/** The most words per second of voiced time a genuine reading can reach. Finite and above 0. */
|
|
340
|
+
maxWordsPerSecond: number;
|
|
341
|
+
/** The least voiced time, in milliseconds, a gradable take contains. Finite and at least 0. */
|
|
342
|
+
minVoicedMs: number;
|
|
343
|
+
}
|
|
344
|
+
/**
|
|
345
|
+
* Why a read-aloud take cannot be graded:
|
|
346
|
+
* - `invalid_assessment` — the assessment fails `validateSpeechAssessment`;
|
|
347
|
+
* - `task_mismatch` — it is not a scripted assessment;
|
|
348
|
+
* - `locale_mismatch` — it was made for another locale than the item's;
|
|
349
|
+
* - `reference_mismatch` — it was made against another text than the item's;
|
|
350
|
+
* - `recording_mismatch` — it was made for another recording than the response's;
|
|
351
|
+
* - `assessor_not_accepted` — its assessor is `'ai'`, and the caller did not opt in;
|
|
352
|
+
* - `no_speech` — the assessor heard no speech;
|
|
353
|
+
* - `insufficient_voiced_time` — the recording holds less voiced time than the policy requires;
|
|
354
|
+
* - `implausible_speech_rate` — more words were recognised than the voiced time can hold;
|
|
355
|
+
* - `missing_dimension` — a weighted dimension has no score in the assessment.
|
|
356
|
+
*/
|
|
357
|
+
type SpeechUnscorableCode = 'invalid_assessment' | 'task_mismatch' | 'locale_mismatch' | 'reference_mismatch' | 'recording_mismatch' | 'assessor_not_accepted' | 'no_speech' | 'insufficient_voiced_time' | 'implausible_speech_rate' | 'missing_dimension';
|
|
358
|
+
/** A read-aloud take that cannot be graded. Never a zero: see `outcomeFromUnscorable`. */
|
|
359
|
+
interface SpeechUnscorable {
|
|
360
|
+
unscorable: true;
|
|
361
|
+
/** Machine-readable. See {@link SpeechUnscorableCode}. */
|
|
362
|
+
code: SpeechUnscorableCode;
|
|
363
|
+
/** A developer-facing English sentence. */
|
|
364
|
+
reason: string;
|
|
365
|
+
}
|
|
366
|
+
/**
|
|
367
|
+
* Options for `gradeReadAloud`. The plausibility policy is required: the SDK
|
|
368
|
+
* holds no default for a threshold that decides whether a take is graded.
|
|
369
|
+
*/
|
|
370
|
+
interface GradeReadAloudOptions {
|
|
371
|
+
/** The server's measurement of the recording (inspectWav). `null` only for a blank response. */
|
|
372
|
+
measured: SpeechMeasurement | null;
|
|
373
|
+
/** When a take is too little speech to grade. */
|
|
374
|
+
plausibility: SpeechPlausibilityPolicy;
|
|
375
|
+
/** Accept `assessor.kind: 'ai'` evidence. Off unless set. */
|
|
376
|
+
allowAiAssessor?: boolean;
|
|
377
|
+
/** Compare the pass line with both sides rounded, as `gradeFromRubric` does. The score stays unrounded. */
|
|
378
|
+
rounding?: RoundingPolicy;
|
|
379
|
+
}
|
|
380
|
+
/** How a word of a read-aloud attempt is marked. */
|
|
381
|
+
type ReadAloudWordState = 'correct' | 'mispronounced' | 'omitted' | 'inserted';
|
|
382
|
+
/**
|
|
383
|
+
* One entry of `alignReadAloud`: a reference word and what was heard for it,
|
|
384
|
+
* or a heard word that is not in the reference.
|
|
385
|
+
*/
|
|
386
|
+
interface ReadAloudWordAlignment {
|
|
387
|
+
/** `w<n>` for a reference word, as `dictationReferenceWords` numbers them; absent for an `inserted` word. */
|
|
388
|
+
itemId?: string;
|
|
389
|
+
/** The normalised reference word; `''` for an `inserted` word. */
|
|
390
|
+
reference: string;
|
|
391
|
+
/**
|
|
392
|
+
* The normalised heard word: `''` for an `omitted` word, and also for an
|
|
393
|
+
* `inserted` word whose text normalises to nothing — an assessor can tag a
|
|
394
|
+
* dash as an insertion, and every insertion gets an entry. Branch on `state`,
|
|
395
|
+
* never on the empty string.
|
|
396
|
+
*/
|
|
397
|
+
heard: string;
|
|
398
|
+
state: ReadAloudWordState;
|
|
399
|
+
/** The assessor word's accuracy (0..100), when it has one. */
|
|
400
|
+
accuracy?: number;
|
|
401
|
+
/** Index into `assessment.words` of the word this entry came from. */
|
|
402
|
+
wordIndex?: number;
|
|
403
|
+
}
|
|
404
|
+
/**
|
|
405
|
+
* What `inspectWav` found in a recording's bytes: the duration, format, peak
|
|
406
|
+
* level and voiced time of a 16-bit PCM WAV, or why it could not read one.
|
|
407
|
+
*/
|
|
408
|
+
type WavInspection = {
|
|
409
|
+
valid: true;
|
|
410
|
+
/** Length of the audio, in milliseconds. */
|
|
411
|
+
durationMs: number;
|
|
412
|
+
sampleRate: number;
|
|
413
|
+
channels: number;
|
|
414
|
+
bitsPerSample: 16;
|
|
415
|
+
/** The loudest sample across every channel, in dBFS; `-Infinity` for digital silence or no samples. */
|
|
416
|
+
peakDbfs: number;
|
|
417
|
+
/** Milliseconds of the windows at or above the policy's silence floor. */
|
|
418
|
+
voicedMs: number;
|
|
419
|
+
} | {
|
|
420
|
+
valid: false;
|
|
421
|
+
reason: 'not_wav' | 'unsupported_encoding' | 'truncated';
|
|
422
|
+
};
|
|
423
|
+
/** How `inspectWav` decides which parts of a recording are voiced. Both fields are required. */
|
|
424
|
+
interface WavInspectionPolicy {
|
|
425
|
+
/** A window whose RMS level is at or above this many dBFS is voiced. Finite, and at most 0. */
|
|
426
|
+
silenceDbfs: number;
|
|
427
|
+
/** The window length, in milliseconds. Finite, and above 0. */
|
|
428
|
+
frameMs: number;
|
|
429
|
+
}
|
|
430
|
+
|
|
191
431
|
/** One transcript word, or one extra word the learner typed, after alignment. */
|
|
192
432
|
interface DictationWordAlignment {
|
|
193
433
|
/** `w<n>`, the 1-based position of a transcript word; absent for an extra word. */
|
|
@@ -314,8 +554,236 @@ declare const DICTATION_MAX_EQUIVALENCES = 100;
|
|
|
314
554
|
*/
|
|
315
555
|
declare const DICTATION_MAX_ACCEPTED_TRANSCRIPTS = 10;
|
|
316
556
|
|
|
557
|
+
/**
|
|
558
|
+
* The pass line, and the authored feedback it selects.
|
|
559
|
+
*
|
|
560
|
+
* A module of its own so grade-producing code outside the scoring barrel can
|
|
561
|
+
* import it without importing the barrel: `gradeReadAloud`, under
|
|
562
|
+
* `scoring/speech/`, calls `gradeFromRubric`, so if `grading.ts` imported the
|
|
563
|
+
* barrel the barrel would import itself through them. `scoring/index.ts`
|
|
564
|
+
* re-exports the pass line and its default by name; the feedback selector
|
|
565
|
+
* beside them stays internal.
|
|
566
|
+
*/
|
|
567
|
+
|
|
317
568
|
/** Default minimum scaled score required to pass when `passThreshold` is absent. */
|
|
318
569
|
declare const DEFAULT_PASS_THRESHOLD = 0.7;
|
|
570
|
+
/**
|
|
571
|
+
* Returns `true` iff `score` meets or exceeds the activity's `passThreshold`,
|
|
572
|
+
* defaulting to {@link DEFAULT_PASS_THRESHOLD} (0.7) when the field is absent.
|
|
573
|
+
*
|
|
574
|
+
* Pass a {@link RoundingPolicy} to compare the way an assessment total is
|
|
575
|
+
* compared — both sides rounded, via {@link gte} — so an item shown as "70%"
|
|
576
|
+
* cannot be recorded as a fail at 69.6. It is **opt-in** rather than the
|
|
577
|
+
* default because switching it on changes item-level pass/fail for scores in
|
|
578
|
+
* the rounding band, and this SDK does not alter historical grades without an
|
|
579
|
+
* explicit decision. Absent, the comparison is the exact raw `>=` it has
|
|
580
|
+
* always been.
|
|
581
|
+
*/
|
|
582
|
+
declare function computePassThreshold(activityData: ActivityData, score: number, rounding?: RoundingPolicy): boolean;
|
|
583
|
+
|
|
584
|
+
/**
|
|
585
|
+
* Marking a read-aloud take word by word: the item's own reference words,
|
|
586
|
+
* paired with the words an assessor heard.
|
|
587
|
+
*
|
|
588
|
+
* The assessor's word list is never taken as the item's. Providers disagree
|
|
589
|
+
* about what a word is, and a provider can be changed; `w<n>` is the position
|
|
590
|
+
* of a REFERENCE word, exactly as `dictationReferenceWords` numbers it, so a
|
|
591
|
+
* mark stored today still names the same word tomorrow. The pairing is the
|
|
592
|
+
* dictation aligner's, run over the dictation normaliser's tokens, so a
|
|
593
|
+
* read-aloud mark and a dictation mark are made the same way.
|
|
594
|
+
*/
|
|
595
|
+
|
|
596
|
+
/**
|
|
597
|
+
* Pairs the words of `assessment` with the words of `data.referenceText`, in
|
|
598
|
+
* reading order: what was read as written, what was mispronounced, what was
|
|
599
|
+
* left out, and what was said that the text does not contain.
|
|
600
|
+
*
|
|
601
|
+
* Reference words are the dictation tokeniser's, so `itemId` is the `w<n>`
|
|
602
|
+
* `dictationReferenceWords` gives, and both `reference` and `heard` are
|
|
603
|
+
* normalised — lowercased, with punctuation and spacing folded away. One
|
|
604
|
+
* assessor word can normalise to no token at all (a dash), or to several (a
|
|
605
|
+
* compatibility form), and two can merge into one (`rock&roll`); tokens are
|
|
606
|
+
* what is aligned, and each remembers the word it came from.
|
|
607
|
+
*
|
|
608
|
+
* A word the assessor tagged `insertion` is never aligned — the reference has
|
|
609
|
+
* nothing for it — so it is placed by the position of its own word, after the
|
|
610
|
+
* last token that came from an earlier word.
|
|
611
|
+
*
|
|
612
|
+
* Whether a paired word counts as read correctly follows `assessment.miscue`:
|
|
613
|
+
* an assessor that judges miscues is trusted, and one that does not
|
|
614
|
+
* (`miscue: 'none'`) has its recognised word compared with the reference word.
|
|
615
|
+
*
|
|
616
|
+
* `heard` is `''` for an omitted word, and also for an inserted word whose text
|
|
617
|
+
* normalises to nothing. Branch on `state`, never on the empty string.
|
|
618
|
+
*
|
|
619
|
+
* The heard tokens are bounded: they are taken in reading order until what the
|
|
620
|
+
* assessment's words spell would pass `DICTATION_MAX_TEXT_LENGTH` code points —
|
|
621
|
+
* the cap the dictation aligner reads a learner's text under — and always on a
|
|
622
|
+
* whole token, because half a token pairs with the wrong word. Every word is
|
|
623
|
+
* charged, an inserted one included, so that total is the whole of the text
|
|
624
|
+
* this function can answer with. Words past the budget contribute no token, so
|
|
625
|
+
* each reference word left with nothing read for it is `omitted`.
|
|
626
|
+
* `validateSpeechAssessment` refuses an assessment whose words spell more than
|
|
627
|
+
* that, measured by the same walk and against the same number, so a validated
|
|
628
|
+
* assessment never reaches the budget: it is a backstop, kept because this
|
|
629
|
+
* function is public and can be handed evidence this build did not check.
|
|
630
|
+
*
|
|
631
|
+
* **`data` is not validated.** Only the assessment is. A `referenceText` longer
|
|
632
|
+
* than `READ_ALOUD_MAX_REFERENCE_LENGTH` code points is cut by the tokeniser
|
|
633
|
+
* with no signal, and one that is absent or is not a string yields no reference
|
|
634
|
+
* word at all, so every heard token comes back `inserted` rather than refused.
|
|
635
|
+
* Validate the item with `validateActivity('read-aloud', data)` first.
|
|
636
|
+
* `gradeReadAloud` is safe without that because it runs the schema itself.
|
|
637
|
+
*
|
|
638
|
+
* Pure and deterministic: no locale, no `Intl`, no clock.
|
|
639
|
+
*
|
|
640
|
+
* @throws TypeError when `assessment` is not a well-formed
|
|
641
|
+
* {@link SpeechAssessment}; check it with `validateSpeechAssessment` first.
|
|
642
|
+
*/
|
|
643
|
+
declare function alignReadAloud(data: Pick<ReadAloudData, 'referenceText'>, assessment: SpeechAssessment): ReadAloudWordAlignment[];
|
|
644
|
+
|
|
645
|
+
/**
|
|
646
|
+
* Checks that `value` is a well-formed {@link SpeechAssessment}. It answers
|
|
647
|
+
* rather than throwing: anything that is not one — a non-object, an unknown key
|
|
648
|
+
* at any depth, a score off the 0..100 scale, a time that is negative or not
|
|
649
|
+
* finite — comes back as `{ success: false, errors }`, one error per problem,
|
|
650
|
+
* each with the path, message and code `validateActivity` gives a schema
|
|
651
|
+
* failure. An object that throws when it is read is the one exception, and the
|
|
652
|
+
* throw is its own: an own property getter, or a `Proxy` trap.
|
|
653
|
+
*
|
|
654
|
+
* Three rules span fields:
|
|
655
|
+
* - `scripted_binding_required` — a `scripted` assessment without
|
|
656
|
+
* `referenceText`, or without `recordingKey`, reported at each missing path;
|
|
657
|
+
* - `phoneme_alphabet_required` — a phoneme names a `symbol` or `heardAs` while
|
|
658
|
+
* `phonemeAlphabet` is absent, reported once, at `phonemeAlphabet`;
|
|
659
|
+
* - `too_big` at `words` — the words carry more text between them, written or
|
|
660
|
+
* normalised, than can be aligned against an item's text. Per-word caps bound
|
|
661
|
+
* no total, and the aligner pairs tokens, of which one word holds as many as
|
|
662
|
+
* its text spells. Every word counts towards both totals, an insertion
|
|
663
|
+
* included: the aligner carries one into its answer, so its text is text the
|
|
664
|
+
* call must hold.
|
|
665
|
+
*
|
|
666
|
+
* The total is measured on the words every field rule accepted, so it says
|
|
667
|
+
* nothing about an assessment that is going back to its adapter anyway.
|
|
668
|
+
*
|
|
669
|
+
* It checks the evidence alone. Whether an assessment belongs to an item and
|
|
670
|
+
* to a learner's recording is decided when it is graded.
|
|
671
|
+
*/
|
|
672
|
+
declare function validateSpeechAssessment(value: unknown): ValidationResult<SpeechAssessment>;
|
|
673
|
+
|
|
674
|
+
/**
|
|
675
|
+
* Grades a read-aloud take from a {@link SpeechAssessment} the application
|
|
676
|
+
* obtained, the response that was submitted, and the server's own measurement
|
|
677
|
+
* of the recording.
|
|
678
|
+
*
|
|
679
|
+
* The weighted total is computed by `gradeFromRubric` from the authored
|
|
680
|
+
* dimensions that carry a weight above 0, each out of 100. The per-word marks
|
|
681
|
+
* in `details` are for display and review: **they never feed the score**, which
|
|
682
|
+
* is the assessor's utterance-level judgement, weighted as the item authored it.
|
|
683
|
+
*
|
|
684
|
+
* Evidence is refused, never scored 0, when it cannot be tied to this item and
|
|
685
|
+
* this recording, when the assessor heard nothing, when the recording holds too
|
|
686
|
+
* little voiced time for the words claimed, or when a weighted dimension has no
|
|
687
|
+
* score — see {@link SpeechUnscorableCode}. Store a refusal with
|
|
688
|
+
* `outcomeFromUnscorable`.
|
|
689
|
+
*
|
|
690
|
+
* A blank (`response.recording === null`) is a grade of 0 with a mark per
|
|
691
|
+
* reference word: the learner submitted, and read nothing. The assessment and
|
|
692
|
+
* the measurement are not read for it.
|
|
693
|
+
*
|
|
694
|
+
* @throws RangeError when `options.plausibility` or `options.rounding` cannot be
|
|
695
|
+
* applied, or when `options.measured` is not a pair of non-negative, finite
|
|
696
|
+
* millisecond counts with `voicedMs` inside `durationMs`.
|
|
697
|
+
* @throws TypeError when `response` is not a read-aloud response, or when a
|
|
698
|
+
* non-blank response is graded without an assessment or without a measurement.
|
|
699
|
+
* @throws ActivitySchemaError when `data` is not valid read-aloud data.
|
|
700
|
+
* @throws RedactedScoringError when `data` is a `redact()` projection. A
|
|
701
|
+
* read-aloud projection is a valid activity, so the authored feedback would
|
|
702
|
+
* simply be missing from the grade; grade against the full data server-side.
|
|
703
|
+
*/
|
|
704
|
+
declare function gradeReadAloud(data: ReadAloudData, response: ReadAloudLearnerResponse, assessment: SpeechAssessment | null, options: GradeReadAloudOptions): GradeRecord | SpeechUnscorable;
|
|
705
|
+
|
|
706
|
+
/**
|
|
707
|
+
* The longest reference text a read-aloud item accepts, in code points, before
|
|
708
|
+
* and after normalisation. It is the dictation transcript cap on purpose: the
|
|
709
|
+
* reference is tokenised as a dictation transcript is, and the dictation
|
|
710
|
+
* normaliser cuts a transcript at that many code points without saying so, so
|
|
711
|
+
* a longer text would be graded against words it had silently lost.
|
|
712
|
+
*/
|
|
713
|
+
declare const READ_ALOUD_MAX_REFERENCE_LENGTH = 2000;
|
|
714
|
+
/** The longest take a read-aloud item may allow, in seconds: the most `recording.maxSeconds` may be. */
|
|
715
|
+
declare const READ_ALOUD_MAX_SECONDS = 300;
|
|
716
|
+
/** The most takes a read-aloud item may allow: the most `recording.maxTakes` may be. */
|
|
717
|
+
declare const READ_ALOUD_MAX_TAKES = 20;
|
|
718
|
+
/**
|
|
719
|
+
* The largest weight a read-aloud dimension may carry. Weights are relative, so
|
|
720
|
+
* no rubric needs more, and the cap keeps a mistyped weight from overflowing
|
|
721
|
+
* the weighted total into a grade that cannot be computed.
|
|
722
|
+
*/
|
|
723
|
+
declare const READ_ALOUD_MAX_DIMENSION_WEIGHT = 1000;
|
|
724
|
+
/**
|
|
725
|
+
* The most words a speech assessment may carry. It bounds one side of the
|
|
726
|
+
* alignment, and one side alone bounds nothing: the aligner pairs TOKENS, and
|
|
727
|
+
* one word normalises to as many as its text holds. What bounds the quadratic
|
|
728
|
+
* work is this cap, the 200-unit cap on a single word's text, and the two rules
|
|
729
|
+
* `validateSpeechAssessment` states over the total of them — what the words are
|
|
730
|
+
* written with, and what they spell once normalised.
|
|
731
|
+
*/
|
|
732
|
+
declare const SPEECH_ASSESSMENT_MAX_WORDS = 1000;
|
|
733
|
+
|
|
734
|
+
/**
|
|
735
|
+
* Measuring a recording from its bytes: how long it is, how loud it gets, and
|
|
736
|
+
* how much of it is voiced.
|
|
737
|
+
*
|
|
738
|
+
* This is the server's own evidence about a take. A duration a browser reported
|
|
739
|
+
* is a claim, and a claim decides nothing: whether a recording holds enough
|
|
740
|
+
* speech to grade is answered from the samples. Only 16-bit PCM WAV is read —
|
|
741
|
+
* the one encoding an application can produce without a decoder — and anything
|
|
742
|
+
* else is reported as unread rather than guessed at.
|
|
743
|
+
*
|
|
744
|
+
* Isomorphic: a `DataView` over the caller's bytes, no `Buffer`, no `node:`
|
|
745
|
+
* imports, and no allocation proportional to the audio.
|
|
746
|
+
*/
|
|
747
|
+
|
|
748
|
+
/**
|
|
749
|
+
* Reads a 16-bit PCM WAV and measures it: `durationMs`, `sampleRate`,
|
|
750
|
+
* `channels`, the loudest sample as `peakDbfs`, and `voicedMs`, the time in
|
|
751
|
+
* windows whose RMS level reaches the policy's silence floor.
|
|
752
|
+
*
|
|
753
|
+
* Anything it cannot measure comes back as `{ valid: false, reason }` rather
|
|
754
|
+
* than a guess: `not_wav` for bytes that are not a RIFF/WAVE file, or whose
|
|
755
|
+
* chunks do not describe the audio before the audio arrives;
|
|
756
|
+
* `unsupported_encoding` for a WAV this reader does not read (compressed, or
|
|
757
|
+
* samples that are not 16-bit); `truncated` when the data chunk is missing or
|
|
758
|
+
* shorter than it declares.
|
|
759
|
+
*
|
|
760
|
+
* Both policy fields are required, because neither has an answer that is right
|
|
761
|
+
* for every microphone: `silenceDbfs` is where an application draws the line
|
|
762
|
+
* between silence and speech, and `frameMs` is how finely it looks.
|
|
763
|
+
*
|
|
764
|
+
* Pure and deterministic. It never throws on the bytes themselves — only on
|
|
765
|
+
* arguments it cannot use.
|
|
766
|
+
*
|
|
767
|
+
* @throws TypeError when `bytes` is not a `Uint8Array`.
|
|
768
|
+
* @throws RangeError when `silenceDbfs` is not a finite number at or below 0,
|
|
769
|
+
* or `frameMs` is not a finite number above 0.
|
|
770
|
+
*/
|
|
771
|
+
declare function inspectWav(bytes: Uint8Array, policy: WavInspectionPolicy): WavInspection;
|
|
772
|
+
|
|
773
|
+
/**
|
|
774
|
+
* Lifts a result that cannot be graded — a `SpeechUnscorable` from
|
|
775
|
+
* `gradeReadAloud`, or anything else carrying a `code` and a `reason` — into
|
|
776
|
+
* the `unscorable` arm of {@link ItemOutcome}, keeping its code.
|
|
777
|
+
*
|
|
778
|
+
* The code is what an application branches on; the reason is a developer-facing
|
|
779
|
+
* sentence. Stored this way, the result composes exactly as the unscorable
|
|
780
|
+
* outcomes `evaluate` returns do. `evaluate` never writes a code itself.
|
|
781
|
+
*/
|
|
782
|
+
declare function outcomeFromUnscorable(result: {
|
|
783
|
+
code: string;
|
|
784
|
+
reason: string;
|
|
785
|
+
}): ItemOutcome;
|
|
786
|
+
|
|
319
787
|
/**
|
|
320
788
|
* Options for {@link score} and {@link evaluate}. Additive: with none, both
|
|
321
789
|
* behave exactly as they always have.
|
|
@@ -335,19 +803,6 @@ interface ScoringOptions {
|
|
|
335
803
|
*/
|
|
336
804
|
rounding?: RoundingPolicy;
|
|
337
805
|
}
|
|
338
|
-
/**
|
|
339
|
-
* Returns `true` iff `score` meets or exceeds the activity's `passThreshold`,
|
|
340
|
-
* defaulting to {@link DEFAULT_PASS_THRESHOLD} (0.7) when the field is absent.
|
|
341
|
-
*
|
|
342
|
-
* Pass a {@link RoundingPolicy} to compare the way an assessment total is
|
|
343
|
-
* compared — both sides rounded, via {@link gte} — so an item shown as "70%"
|
|
344
|
-
* cannot be recorded as a fail at 69.6. It is **opt-in** rather than the
|
|
345
|
-
* default because switching it on changes item-level pass/fail for scores in
|
|
346
|
-
* the rounding band, and this SDK does not alter historical grades without an
|
|
347
|
-
* explicit decision. Absent, the comparison is the exact raw `>=` it has
|
|
348
|
-
* always been.
|
|
349
|
-
*/
|
|
350
|
-
declare function computePassThreshold(activityData: ActivityData, score: number, rounding?: RoundingPolicy): boolean;
|
|
351
806
|
/**
|
|
352
807
|
* Scores a learner response against activity data and returns a full
|
|
353
808
|
* {@link ScoringResult}.
|
|
@@ -381,4 +836,4 @@ declare function score(activityType: ActivityType, activityData: ActivityData, l
|
|
|
381
836
|
*/
|
|
382
837
|
declare function evaluate(data: ActivityData, response: LearnerResponse, options?: ScoringOptions): ItemOutcome;
|
|
383
838
|
|
|
384
|
-
export { type AssessmentScore, type AssessmentSectionInput, type Band, type CompositionPolicy, DEFAULT_PASS_THRESHOLD, DICTATION_MAX_ACCEPTED_TRANSCRIPTS, DICTATION_MAX_EQUIVALENCES, DICTATION_MAX_EQUIVALENCE_LENGTH, DICTATION_MAX_TEXT_LENGTH, DICTATION_MAX_TRANSCRIPT_LENGTH, type DictationAlignment, type DictationCharOp, type DictationReference, type DictationWordAlignment, type PassFailureReason, type RoundingMode, type RoundingPolicy, type ScoredItem, type ScoringOptions, type SectionScore, alignDictation, classifyBand, composeAssessmentScore, computePassThreshold, dictationReferenceWords, diffDictationChars, evaluate, gte, roundGrade, score };
|
|
839
|
+
export { type AssessmentScore, type AssessmentSectionInput, type Band, type CompositionPolicy, DEFAULT_PASS_THRESHOLD, DICTATION_MAX_ACCEPTED_TRANSCRIPTS, DICTATION_MAX_EQUIVALENCES, DICTATION_MAX_EQUIVALENCE_LENGTH, DICTATION_MAX_TEXT_LENGTH, DICTATION_MAX_TRANSCRIPT_LENGTH, type DictationAlignment, type DictationCharOp, type DictationReference, type DictationWordAlignment, type GradeReadAloudOptions, GradeRecord, ItemOutcome, type PassFailureReason, READ_ALOUD_MAX_DIMENSION_WEIGHT, READ_ALOUD_MAX_REFERENCE_LENGTH, READ_ALOUD_MAX_SECONDS, READ_ALOUD_MAX_TAKES, ReadAloudData, ReadAloudLearnerResponse, type ReadAloudWordAlignment, type ReadAloudWordState, type RoundingMode, type RoundingPolicy, SPEECH_ASSESSMENT_MAX_WORDS, type ScoredItem, type ScoringOptions, type SectionScore, type SpeechAssessment, type SpeechMeasurement, type SpeechPhoneme, type SpeechPhonemeCandidate, type SpeechPlausibilityPolicy, type SpeechSyllable, type SpeechUnscorable, type SpeechUnscorableCode, type SpeechWord, type SpeechWordError, type WavInspection, type WavInspectionPolicy, alignDictation, alignReadAloud, classifyBand, composeAssessmentScore, computePassThreshold, dictationReferenceWords, diffDictationChars, evaluate, gradeReadAloud, gte, inspectWav, outcomeFromUnscorable, roundGrade, score, validateSpeechAssessment };
|
package/dist/scoring.js
CHANGED
|
@@ -1,25 +1,35 @@
|
|
|
1
1
|
import {
|
|
2
2
|
DEFAULT_PASS_THRESHOLD,
|
|
3
|
+
alignReadAloud,
|
|
3
4
|
classifyBand,
|
|
4
5
|
composeAssessmentScore,
|
|
5
6
|
computePassThreshold,
|
|
6
7
|
diffDictationChars,
|
|
7
8
|
evaluate,
|
|
9
|
+
gradeReadAloud,
|
|
8
10
|
gte,
|
|
11
|
+
inspectWav,
|
|
12
|
+
outcomeFromUnscorable,
|
|
9
13
|
roundGrade,
|
|
10
|
-
score
|
|
11
|
-
|
|
14
|
+
score,
|
|
15
|
+
validateSpeechAssessment
|
|
16
|
+
} from "./chunk-RTIR6R2U.js";
|
|
12
17
|
import {
|
|
13
18
|
DICTATION_MAX_ACCEPTED_TRANSCRIPTS,
|
|
14
19
|
DICTATION_MAX_EQUIVALENCES,
|
|
15
20
|
DICTATION_MAX_EQUIVALENCE_LENGTH,
|
|
16
21
|
DICTATION_MAX_TEXT_LENGTH,
|
|
17
22
|
DICTATION_MAX_TRANSCRIPT_LENGTH,
|
|
23
|
+
READ_ALOUD_MAX_DIMENSION_WEIGHT,
|
|
24
|
+
READ_ALOUD_MAX_REFERENCE_LENGTH,
|
|
25
|
+
READ_ALOUD_MAX_SECONDS,
|
|
26
|
+
READ_ALOUD_MAX_TAKES,
|
|
27
|
+
SPEECH_ASSESSMENT_MAX_WORDS,
|
|
18
28
|
alignDictation,
|
|
19
29
|
dictationReferenceWords,
|
|
20
30
|
levenshteinDistance,
|
|
21
31
|
matchText
|
|
22
|
-
} from "./chunk-
|
|
32
|
+
} from "./chunk-6RND47QE.js";
|
|
23
33
|
export {
|
|
24
34
|
DEFAULT_PASS_THRESHOLD,
|
|
25
35
|
DICTATION_MAX_ACCEPTED_TRANSCRIPTS,
|
|
@@ -27,17 +37,27 @@ export {
|
|
|
27
37
|
DICTATION_MAX_EQUIVALENCE_LENGTH,
|
|
28
38
|
DICTATION_MAX_TEXT_LENGTH,
|
|
29
39
|
DICTATION_MAX_TRANSCRIPT_LENGTH,
|
|
40
|
+
READ_ALOUD_MAX_DIMENSION_WEIGHT,
|
|
41
|
+
READ_ALOUD_MAX_REFERENCE_LENGTH,
|
|
42
|
+
READ_ALOUD_MAX_SECONDS,
|
|
43
|
+
READ_ALOUD_MAX_TAKES,
|
|
44
|
+
SPEECH_ASSESSMENT_MAX_WORDS,
|
|
30
45
|
alignDictation,
|
|
46
|
+
alignReadAloud,
|
|
31
47
|
classifyBand,
|
|
32
48
|
composeAssessmentScore,
|
|
33
49
|
computePassThreshold,
|
|
34
50
|
dictationReferenceWords,
|
|
35
51
|
diffDictationChars,
|
|
36
52
|
evaluate,
|
|
53
|
+
gradeReadAloud,
|
|
37
54
|
gte,
|
|
55
|
+
inspectWav,
|
|
38
56
|
levenshteinDistance,
|
|
39
57
|
matchText,
|
|
58
|
+
outcomeFromUnscorable,
|
|
40
59
|
roundGrade,
|
|
41
|
-
score
|
|
60
|
+
score,
|
|
61
|
+
validateSpeechAssessment
|
|
42
62
|
};
|
|
43
63
|
//# sourceMappingURL=scoring.js.map
|
package/dist/xapi.cjs
CHANGED
|
@@ -4,13 +4,13 @@
|
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
|
|
7
|
-
var
|
|
8
|
-
require('./chunk-
|
|
7
|
+
var _chunkE7LPIZXIcjs = require('./chunk-E7LPIZXI.cjs');
|
|
8
|
+
require('./chunk-VCXIPLEW.cjs');
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
|
|
14
14
|
|
|
15
|
-
exports.XAPIVerb =
|
|
15
|
+
exports.XAPIVerb = _chunkE7LPIZXIcjs.XAPIVerb; exports.XAPI_VERB_DISPLAY = _chunkE7LPIZXIcjs.XAPI_VERB_DISPLAY; exports.validateXAPIStatement = _chunkE7LPIZXIcjs.validateXAPIStatement; exports.xAPIBuilder = _chunkE7LPIZXIcjs.xAPIBuilder; exports.xapiDefinitionFor = _chunkE7LPIZXIcjs.xapiDefinitionFor;
|
|
16
16
|
//# sourceMappingURL=xapi.cjs.map
|
package/dist/xapi.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { a3 as XAPIActor, W as ScoringResult, a5 as XAPIContext, a9 as XAPIResult, ab as XAPIStatement } from './activity-BVkg9VDo.cjs';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* The set of xAPI verbs emitted by SDK activities, keyed by a stable
|
package/dist/xapi.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { a3 as XAPIActor, W as ScoringResult, a5 as XAPIContext, a9 as XAPIResult, ab as XAPIStatement } from './activity-BVkg9VDo.js';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* The set of xAPI verbs emitted by SDK activities, keyed by a stable
|
package/dist/xapi.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@intellectif/lk-core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.14.1",
|
|
4
4
|
"description": "Framework-free assessment core: Zod activity schemas, pure scoring, rubric & deferred grading, weighted assessment composition, exam attempt plans, and xAPI 1.0.3.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"assessment",
|