@intellectif/lk-core 1.2.0 → 1.4.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 +29 -0
- package/README.md +2 -2
- package/dist/ai-CnmLPMzd.d.cts +2417 -0
- package/dist/ai-CnmLPMzd.d.cts.map +1 -0
- package/dist/ai-CnmLPMzd.d.ts +2417 -0
- package/dist/ai-CnmLPMzd.d.ts.map +1 -0
- package/dist/ai-check.cjs +845 -620
- package/dist/ai-check.cjs.map +1 -1
- package/dist/ai-check.d.cts +82 -80
- package/dist/ai-check.d.cts.map +1 -0
- package/dist/ai-check.d.ts +82 -80
- package/dist/ai-check.d.ts.map +1 -0
- package/dist/ai-check.js +843 -621
- package/dist/ai-check.js.map +1 -1
- package/dist/ai-writing-CPpH9Qxq.js +2601 -0
- package/dist/ai-writing-CPpH9Qxq.js.map +1 -0
- package/dist/ai-writing-G6tnCGzw.cjs +2846 -0
- package/dist/ai-writing-G6tnCGzw.cjs.map +1 -0
- package/dist/index-2nigSf4N.d.cts +339 -0
- package/dist/index-2nigSf4N.d.cts.map +1 -0
- package/dist/index-B54baFUA.d.ts +339 -0
- package/dist/index-B54baFUA.d.ts.map +1 -0
- package/dist/index-BAfEHCnD.d.cts +1071 -0
- package/dist/index-BAfEHCnD.d.cts.map +1 -0
- package/dist/index-BqMXWuNC.d.cts +153 -0
- package/dist/index-BqMXWuNC.d.cts.map +1 -0
- package/dist/index-CVWvymVb.d.ts +1071 -0
- package/dist/index-CVWvymVb.d.ts.map +1 -0
- package/dist/index-CcHDTxIZ.d.ts +153 -0
- package/dist/index-CcHDTxIZ.d.ts.map +1 -0
- package/dist/index.cjs +1211 -1146
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +941 -861
- package/dist/index.d.cts.map +1 -0
- package/dist/index.d.ts +941 -861
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1063 -1147
- package/dist/index.js.map +1 -1
- package/dist/item-group-DXiR8nOr.js +445 -0
- package/dist/item-group-DXiR8nOr.js.map +1 -0
- package/dist/item-group-DroFXgLb.cjs +505 -0
- package/dist/item-group-DroFXgLb.cjs.map +1 -0
- package/dist/registry-DVi0LR_i.cjs +5915 -0
- package/dist/registry-DVi0LR_i.cjs.map +1 -0
- package/dist/registry-Dsx9mNsS.js +5449 -0
- package/dist/registry-Dsx9mNsS.js.map +1 -0
- package/dist/schemas-Bdw0nGPo.js +152 -0
- package/dist/schemas-Bdw0nGPo.js.map +1 -0
- package/dist/schemas-DVbAOrtd.cjs +224 -0
- package/dist/schemas-DVbAOrtd.cjs.map +1 -0
- package/dist/schemas.cjs +16 -34
- package/dist/schemas.d.cts +2 -2
- package/dist/schemas.d.ts +2 -2
- package/dist/schemas.js +3 -34
- package/dist/scoring-B2_HEhMT.cjs +1748 -0
- package/dist/scoring-B2_HEhMT.cjs.map +1 -0
- package/dist/scoring-BZqUkuDF.js +1604 -0
- package/dist/scoring-BZqUkuDF.js.map +1 -0
- package/dist/scoring.cjs +37 -75
- package/dist/scoring.d.cts +3 -1103
- package/dist/scoring.d.ts +3 -1103
- package/dist/scoring.js +3 -75
- package/dist/xapi-CMTph7wG.js +332 -0
- package/dist/xapi-CMTph7wG.js.map +1 -0
- package/dist/xapi-DNzkSJ0S.cjs +362 -0
- package/dist/xapi-DNzkSJ0S.cjs.map +1 -0
- package/dist/xapi.cjs +7 -16
- package/dist/xapi.d.cts +2 -150
- package/dist/xapi.d.ts +2 -150
- package/dist/xapi.js +2 -16
- package/package.json +4 -4
- package/dist/activity-D705CXng.d.cts +0 -2162
- package/dist/activity-D705CXng.d.ts +0 -2162
- package/dist/chunk-46BXIGLG.cjs +0 -272
- package/dist/chunk-46BXIGLG.cjs.map +0 -1
- package/dist/chunk-4YANYTFN.cjs +0 -1256
- package/dist/chunk-4YANYTFN.cjs.map +0 -1
- package/dist/chunk-7KOWU4RO.js +0 -2207
- package/dist/chunk-7KOWU4RO.js.map +0 -1
- package/dist/chunk-7THXD7AN.cjs +0 -5057
- package/dist/chunk-7THXD7AN.cjs.map +0 -1
- package/dist/chunk-CYCTIXUN.cjs +0 -118
- package/dist/chunk-CYCTIXUN.cjs.map +0 -1
- package/dist/chunk-CYPA5BW5.js +0 -118
- package/dist/chunk-CYPA5BW5.js.map +0 -1
- package/dist/chunk-JTNDIBZO.js +0 -272
- package/dist/chunk-JTNDIBZO.js.map +0 -1
- package/dist/chunk-MV2N2WNS.js +0 -352
- package/dist/chunk-MV2N2WNS.js.map +0 -1
- package/dist/chunk-MXI26H62.cjs +0 -2207
- package/dist/chunk-MXI26H62.cjs.map +0 -1
- package/dist/chunk-QS7B42ZR.js +0 -5057
- package/dist/chunk-QS7B42ZR.js.map +0 -1
- package/dist/chunk-QX2ERL72.js +0 -1256
- package/dist/chunk-QX2ERL72.js.map +0 -1
- package/dist/chunk-ZWXQSKCX.cjs +0 -352
- package/dist/chunk-ZWXQSKCX.cjs.map +0 -1
- package/dist/index-BChYtrEe.d.ts +0 -548
- package/dist/index-VXt_v3YI.d.cts +0 -548
- package/dist/schemas.cjs.map +0 -1
- package/dist/schemas.js.map +0 -1
- package/dist/scoring.cjs.map +0 -1
- package/dist/scoring.js.map +0 -1
- package/dist/xapi.cjs.map +0 -1
- package/dist/xapi.js.map +0 -1
|
@@ -0,0 +1,1071 @@
|
|
|
1
|
+
import { En as Grader, Ht as ItemOutcome, Ot as DictationData, Qt as ReadAloudData, Tn as GradeRecord, Ut as LearnerResponse, ln as ValidationResult, sn as ScoringResult, tn as ReadAloudLearnerResponse, wt as ActivityType, yt as ActivityData } from "./ai-CnmLPMzd.cjs";
|
|
2
|
+
//#region src/scoring/rounding.d.ts
|
|
3
|
+
/**
|
|
4
|
+
* Rounding for assessment scores.
|
|
5
|
+
*
|
|
6
|
+
* Rounding is **two different operations** that must not share one policy, and
|
|
7
|
+
* conflating them silently inverts one of them:
|
|
8
|
+
*
|
|
9
|
+
* - **Grade rounding** decides the number a learner is shown and recorded
|
|
10
|
+
* against. It rounds to a fixed number of decimal places, conventionally
|
|
11
|
+
* half-up, so a learner shown "70%" is not recorded as a fail at 69.6.
|
|
12
|
+
* - **Band / level classification** decides which level someone is placed in.
|
|
13
|
+
* It deliberately **floors**: placing a learner above their real level is
|
|
14
|
+
* the more harmful error, so a band boundary must not be reached by
|
|
15
|
+
* rounding up.
|
|
16
|
+
*
|
|
17
|
+
* There is therefore no default `RoundingPolicy` anywhere in this SDK, and
|
|
18
|
+
* `dp` has no default either — an integrator's deliberate choice must never be
|
|
19
|
+
* supplied by us.
|
|
20
|
+
*/
|
|
21
|
+
/** How a value is rounded to `dp` decimal places. */
|
|
22
|
+
type RoundingMode = 'half-up' | 'half-even' | 'floor' | 'ceil';
|
|
23
|
+
/** A rounding policy. Both fields are required — the SDK never guesses either. */
|
|
24
|
+
interface RoundingPolicy {
|
|
25
|
+
mode: RoundingMode;
|
|
26
|
+
/** Decimal places. Load-bearing: `dp: 2` is what stops 69.6 becoming a fail at 70. */
|
|
27
|
+
dp: number;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Rounds `value` to `policy.dp` places under `policy.mode`.
|
|
31
|
+
*
|
|
32
|
+
* Idempotent for every mode: rounding an already-rounded value returns it
|
|
33
|
+
* unchanged. That matters because a composed score is rounded once and then
|
|
34
|
+
* compared through {@link gte}, which rounds again.
|
|
35
|
+
*/
|
|
36
|
+
declare function roundGrade(value: number, policy: RoundingPolicy): number;
|
|
37
|
+
/**
|
|
38
|
+
* Threshold comparison that rounds **both sides** before comparing.
|
|
39
|
+
*
|
|
40
|
+
* Comparing a raw float against a rounded threshold is how a learner ends up
|
|
41
|
+
* shown one number and recorded against another. Rounding both sides — and
|
|
42
|
+
* allowing an epsilon — makes "what the learner sees" and "what the gradebook
|
|
43
|
+
* decides" the same comparison.
|
|
44
|
+
*/
|
|
45
|
+
declare function gte(value: number, threshold: number, policy: RoundingPolicy): boolean;
|
|
46
|
+
/** A named band with an inclusive lower bound, e.g. `{ name: 'B1', min: 0.6 }`. */
|
|
47
|
+
interface Band {
|
|
48
|
+
name: string;
|
|
49
|
+
/** Inclusive lower bound on the same scale as the value being classified. */
|
|
50
|
+
min: number;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Classifies a value into a band by **flooring**: the highest band whose `min`
|
|
54
|
+
* the value actually reaches. Deliberately does NOT round up to a boundary —
|
|
55
|
+
* over-placement is the more harmful error, so a learner just below a boundary
|
|
56
|
+
* stays below it.
|
|
57
|
+
*
|
|
58
|
+
* Returns `null` when the value reaches no band's minimum.
|
|
59
|
+
*/
|
|
60
|
+
declare function classifyBand(value: number, bands: readonly Band[]): Band | null;
|
|
61
|
+
//#endregion
|
|
62
|
+
//#region src/types/speech.d.ts
|
|
63
|
+
/**
|
|
64
|
+
* How the assessor classified a word against the reference text: `none`, read
|
|
65
|
+
* as written; `mispronunciation`, read but not well enough; `omission`, in the
|
|
66
|
+
* reference but not heard; `insertion`, heard but not in the reference.
|
|
67
|
+
*/
|
|
68
|
+
type SpeechWordError = 'none' | 'mispronunciation' | 'omission' | 'insertion';
|
|
69
|
+
/** A phoneme the assessor heard instead of the expected one, and its score. */
|
|
70
|
+
interface SpeechPhonemeCandidate {
|
|
71
|
+
/** The phoneme, in the assessment's `phonemeAlphabet`. */
|
|
72
|
+
symbol: string;
|
|
73
|
+
/** 0..100. */
|
|
74
|
+
score: number;
|
|
75
|
+
}
|
|
76
|
+
/** One phoneme of a word, as the assessor measured it. Assessors report different subsets. */
|
|
77
|
+
interface SpeechPhoneme {
|
|
78
|
+
/** The phoneme, in the assessment's `phonemeAlphabet`. Absent when the assessor does not name it. */
|
|
79
|
+
symbol?: string;
|
|
80
|
+
/** 0..100. Absent means not assessed, never 0. */
|
|
81
|
+
accuracy?: number;
|
|
82
|
+
/** Milliseconds from the start of the recording. */
|
|
83
|
+
startMs?: number;
|
|
84
|
+
/** Milliseconds. */
|
|
85
|
+
durationMs?: number;
|
|
86
|
+
/** The phonemes the assessor heard instead, in the order it reported them. */
|
|
87
|
+
heardAs?: SpeechPhonemeCandidate[];
|
|
88
|
+
}
|
|
89
|
+
/** One syllable of a word, as the assessor measured it. */
|
|
90
|
+
interface SpeechSyllable {
|
|
91
|
+
/** The syllable as the assessor reports it. */
|
|
92
|
+
text: string;
|
|
93
|
+
/** The letters of the word the syllable is written with, when the assessor reports them. */
|
|
94
|
+
grapheme?: string;
|
|
95
|
+
/** 0..100. Absent means not assessed, never 0. */
|
|
96
|
+
accuracy?: number;
|
|
97
|
+
/** Milliseconds from the start of the recording. */
|
|
98
|
+
startMs?: number;
|
|
99
|
+
/** Milliseconds. */
|
|
100
|
+
durationMs?: number;
|
|
101
|
+
}
|
|
102
|
+
/** One word of a {@link SpeechAssessment}. */
|
|
103
|
+
interface SpeechWord {
|
|
104
|
+
/** The word as the assessor reports it. */
|
|
105
|
+
text: string;
|
|
106
|
+
/** 0..100. Absent means not assessed — never read as 0. */
|
|
107
|
+
accuracy?: number;
|
|
108
|
+
/** The assessor's classification of the word. */
|
|
109
|
+
error: SpeechWordError;
|
|
110
|
+
/** The provider's own error label, verbatim, for audit. No grade reads it. */
|
|
111
|
+
vendorError?: string;
|
|
112
|
+
/** Milliseconds from the start of the recording. */
|
|
113
|
+
startMs?: number;
|
|
114
|
+
/** Milliseconds. */
|
|
115
|
+
durationMs?: number;
|
|
116
|
+
syllables?: SpeechSyllable[];
|
|
117
|
+
phonemes?: SpeechPhoneme[];
|
|
118
|
+
/**
|
|
119
|
+
* Confidences (0..1) that an unexpected pause came before the word, or that
|
|
120
|
+
* an expected one is missing. Display only: no grade reads them.
|
|
121
|
+
*/
|
|
122
|
+
breaks?: {
|
|
123
|
+
unexpected?: number;
|
|
124
|
+
missing?: number;
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* A pronunciation assessor's result, in the SDK's neutral shape. Every score in
|
|
129
|
+
* it is on a 0..100 scale. Check one with `validateSpeechAssessment`.
|
|
130
|
+
*/
|
|
131
|
+
interface SpeechAssessment {
|
|
132
|
+
assessmentVersion: '1.0';
|
|
133
|
+
/** `no_speech`: the assessor heard nothing to assess — which is never a score of 0. */
|
|
134
|
+
status: 'assessed' | 'no_speech';
|
|
135
|
+
/** `scripted`: measured against a known text. Only a scripted assessment grades a read-aloud item. */
|
|
136
|
+
task: 'scripted' | 'unscripted';
|
|
137
|
+
/** The locale the assessment was made for: canonical, with a region — the rule `ReadAloudData.locale` follows. */
|
|
138
|
+
locale: string;
|
|
139
|
+
/**
|
|
140
|
+
* The text the assessment was made against: the authored reference text,
|
|
141
|
+
* verbatim. Required when scripted, so a grade can refuse evidence made
|
|
142
|
+
* against another text.
|
|
143
|
+
*/
|
|
144
|
+
referenceText?: string;
|
|
145
|
+
/**
|
|
146
|
+
* The `RecordingRef.key` of the recording that was assessed. Required when
|
|
147
|
+
* scripted, so a grade can refuse evidence made for another recording.
|
|
148
|
+
*/
|
|
149
|
+
recordingKey?: string;
|
|
150
|
+
/** Who measured. Evidence from `'ai'` grades only when the caller opts in. */
|
|
151
|
+
assessor: Grader;
|
|
152
|
+
/** What every score is out of. Stated, so an adapter never leaves it to be assumed. */
|
|
153
|
+
scale: 100;
|
|
154
|
+
/** Utterance-level scores, 0..100. An absent score was not assessed; it is never read as 0. */
|
|
155
|
+
scores: {
|
|
156
|
+
accuracy?: number;
|
|
157
|
+
fluency?: number;
|
|
158
|
+
completeness?: number;
|
|
159
|
+
prosody?: number;
|
|
160
|
+
overall?: number;
|
|
161
|
+
};
|
|
162
|
+
/** What the assessor recognised, as text. */
|
|
163
|
+
recognizedText?: string;
|
|
164
|
+
/**
|
|
165
|
+
* Who judged omissions, insertions and mispronunciations. `assessor`: the
|
|
166
|
+
* assessor did, and its word `error` labels are trusted. `none`: it did not,
|
|
167
|
+
* so a heard word that differs from its reference word is marked
|
|
168
|
+
* mispronounced.
|
|
169
|
+
*/
|
|
170
|
+
miscue: 'assessor' | 'none';
|
|
171
|
+
/** The alphabet phoneme symbols are written in. Required whenever a phoneme names a `symbol` or `heardAs`. */
|
|
172
|
+
phonemeAlphabet?: 'ipa' | 'sapi';
|
|
173
|
+
/** The words, in the order the assessor reported them. */
|
|
174
|
+
words: SpeechWord[];
|
|
175
|
+
/** Utterance-level prosody: `monotoneConfidence` is 0..1, display only. */
|
|
176
|
+
prosody?: {
|
|
177
|
+
monotoneConfidence?: number;
|
|
178
|
+
};
|
|
179
|
+
/** Recording quality. Informational only: no grade reads it. */
|
|
180
|
+
signal?: {
|
|
181
|
+
snrDb?: number;
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* The server's own measurement of a recording, from `inspectWav`. It is what
|
|
186
|
+
* decides whether a take is plausible speech; a duration the client reported
|
|
187
|
+
* never is.
|
|
188
|
+
*/
|
|
189
|
+
interface SpeechMeasurement {
|
|
190
|
+
/** Length of the recording, in milliseconds. */
|
|
191
|
+
durationMs: number;
|
|
192
|
+
/** Milliseconds of the recording at or above the silence floor. At most `durationMs`. */
|
|
193
|
+
voicedMs: number;
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* When a take is too little speech to grade. Both fields are required: the SDK
|
|
197
|
+
* holds no default for either, because each is a decision about an
|
|
198
|
+
* application's learners and its assessor.
|
|
199
|
+
*/
|
|
200
|
+
interface SpeechPlausibilityPolicy {
|
|
201
|
+
/** The most words per second of voiced time a genuine reading can reach. Finite and above 0. */
|
|
202
|
+
maxWordsPerSecond: number;
|
|
203
|
+
/** The least voiced time, in milliseconds, a gradable take contains. Finite and at least 0. */
|
|
204
|
+
minVoicedMs: number;
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Why a read-aloud take cannot be graded:
|
|
208
|
+
* - `invalid_assessment` — the assessment fails `validateSpeechAssessment`;
|
|
209
|
+
* - `task_mismatch` — it is not a scripted assessment;
|
|
210
|
+
* - `locale_mismatch` — it was made for another locale than the item's;
|
|
211
|
+
* - `reference_mismatch` — it was made against another text than the item's;
|
|
212
|
+
* - `recording_mismatch` — it was made for another recording than the response's;
|
|
213
|
+
* - `assessor_not_accepted` — its assessor is `'ai'`, and the caller did not opt in;
|
|
214
|
+
* - `no_speech` — the assessor heard no speech;
|
|
215
|
+
* - `insufficient_voiced_time` — the recording holds less voiced time than the policy requires;
|
|
216
|
+
* - `implausible_speech_rate` — more words were recognised than the voiced time can hold;
|
|
217
|
+
* - `missing_dimension` — a weighted dimension has no score in the assessment.
|
|
218
|
+
*/
|
|
219
|
+
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';
|
|
220
|
+
/** A read-aloud take that cannot be graded. Never a zero: see `outcomeFromUnscorable`. */
|
|
221
|
+
interface SpeechUnscorable {
|
|
222
|
+
unscorable: true;
|
|
223
|
+
/** Machine-readable. See {@link SpeechUnscorableCode}. */
|
|
224
|
+
code: SpeechUnscorableCode;
|
|
225
|
+
/** A developer-facing English sentence. */
|
|
226
|
+
reason: string;
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* Options for `gradeReadAloud`. The plausibility policy is required: the SDK
|
|
230
|
+
* holds no default for a threshold that decides whether a take is graded.
|
|
231
|
+
*/
|
|
232
|
+
interface GradeReadAloudOptions {
|
|
233
|
+
/** The server's measurement of the recording (inspectWav). `null` only for a blank response. */
|
|
234
|
+
measured: SpeechMeasurement | null;
|
|
235
|
+
/** When a take is too little speech to grade. */
|
|
236
|
+
plausibility: SpeechPlausibilityPolicy;
|
|
237
|
+
/** Accept `assessor.kind: 'ai'` evidence. Off unless set. */
|
|
238
|
+
allowAiAssessor?: boolean;
|
|
239
|
+
/** Compare the pass line with both sides rounded, as `gradeFromRubric` does. The score stays unrounded. */
|
|
240
|
+
rounding?: RoundingPolicy;
|
|
241
|
+
}
|
|
242
|
+
/** How a word of a read-aloud attempt is marked. */
|
|
243
|
+
type ReadAloudWordState = 'correct' | 'mispronounced' | 'omitted' | 'inserted';
|
|
244
|
+
/**
|
|
245
|
+
* One entry of `alignReadAloud`: a reference word and what was heard for it,
|
|
246
|
+
* or a heard word that is not in the reference.
|
|
247
|
+
*/
|
|
248
|
+
interface ReadAloudWordAlignment {
|
|
249
|
+
/** `w<n>` for a reference word, as `dictationReferenceWords` numbers them; absent for an `inserted` word. */
|
|
250
|
+
itemId?: string;
|
|
251
|
+
/** The normalised reference word; `''` for an `inserted` word. */
|
|
252
|
+
reference: string;
|
|
253
|
+
/**
|
|
254
|
+
* The normalised heard word: `''` for an `omitted` word, and also for an
|
|
255
|
+
* `inserted` word whose text normalises to nothing — an assessor can tag a
|
|
256
|
+
* dash as an insertion, and every insertion gets an entry. Branch on `state`,
|
|
257
|
+
* never on the empty string.
|
|
258
|
+
*/
|
|
259
|
+
heard: string;
|
|
260
|
+
state: ReadAloudWordState;
|
|
261
|
+
/** The assessor word's accuracy (0..100), when it has one. */
|
|
262
|
+
accuracy?: number;
|
|
263
|
+
/** Index into `assessment.words` of the word this entry came from. */
|
|
264
|
+
wordIndex?: number;
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* What `inspectWav` found in a recording's bytes: the duration, format, peak
|
|
268
|
+
* level and voiced time of a 16-bit PCM WAV, or why it could not read one.
|
|
269
|
+
*/
|
|
270
|
+
type WavInspection = {
|
|
271
|
+
valid: true;
|
|
272
|
+
/** Length of the audio, in milliseconds. */
|
|
273
|
+
durationMs: number;
|
|
274
|
+
sampleRate: number;
|
|
275
|
+
channels: number;
|
|
276
|
+
bitsPerSample: 16;
|
|
277
|
+
/** The loudest sample across every channel, in dBFS; `-Infinity` for digital silence or no samples. */
|
|
278
|
+
peakDbfs: number;
|
|
279
|
+
/** Milliseconds of the windows at or above the policy's silence floor. */
|
|
280
|
+
voicedMs: number;
|
|
281
|
+
} | {
|
|
282
|
+
valid: false;
|
|
283
|
+
reason: 'not_wav' | 'unsupported_encoding' | 'truncated';
|
|
284
|
+
};
|
|
285
|
+
/** How `inspectWav` decides which parts of a recording are voiced. Both fields are required. */
|
|
286
|
+
interface WavInspectionPolicy {
|
|
287
|
+
/** A window whose RMS level is at or above this many dBFS is voiced. Finite, and at most 0. */
|
|
288
|
+
silenceDbfs: number;
|
|
289
|
+
/** The window length, in milliseconds. Finite, and above 0. */
|
|
290
|
+
frameMs: number;
|
|
291
|
+
}
|
|
292
|
+
//#endregion
|
|
293
|
+
//#region src/scoring/compose.d.ts
|
|
294
|
+
/**
|
|
295
|
+
* Sectioned assessment scoring — weights, per-section thresholds, and an
|
|
296
|
+
* explicit reason when an attempt fails.
|
|
297
|
+
*
|
|
298
|
+
* This exists because the formula is invariably implemented twice: once on the
|
|
299
|
+
* server that records the grade, and again on the client that shows a learner
|
|
300
|
+
* their grade breakdown. Two implementations of one formula is exactly the
|
|
301
|
+
* duplication an SDK should remove, and they drift — usually in the scale
|
|
302
|
+
* (0–1 vs 0–100) or in whether a section override is honoured.
|
|
303
|
+
*/
|
|
304
|
+
/** One item's contribution to a section. */
|
|
305
|
+
interface ScoredItem {
|
|
306
|
+
/**
|
|
307
|
+
* Identity of the SLOT, not the activity. The same activity can legitimately
|
|
308
|
+
* appear in two sections; keying on the activity id collapses them into one
|
|
309
|
+
* and silently scores the second occurrence as zero.
|
|
310
|
+
*/
|
|
311
|
+
slotId: string;
|
|
312
|
+
/** The activity that filled this slot, for reporting. */
|
|
313
|
+
activityId?: string;
|
|
314
|
+
/** Maximum points this slot is worth. */
|
|
315
|
+
points: number;
|
|
316
|
+
/** What the learner achieved, or why there is no grade yet. */
|
|
317
|
+
outcome: ItemOutcome;
|
|
318
|
+
}
|
|
319
|
+
/** A weighted section of an assessment. */
|
|
320
|
+
interface AssessmentSectionInput {
|
|
321
|
+
id: string;
|
|
322
|
+
title?: string;
|
|
323
|
+
/** Relative weight. Weights are normalised by their sum, so they need not total 1. */
|
|
324
|
+
weight: number;
|
|
325
|
+
/** Overrides the assessment-wide section threshold for this section only. */
|
|
326
|
+
passThresholdOverride?: number;
|
|
327
|
+
items: ScoredItem[];
|
|
328
|
+
}
|
|
329
|
+
/** Policy for {@link composeAssessmentScore}. */
|
|
330
|
+
interface CompositionPolicy {
|
|
331
|
+
/** Scaled [0,1] overall score required to pass. */
|
|
332
|
+
passThreshold: number;
|
|
333
|
+
/** Scaled [0,1] score each section must reach, when sections gate the pass. */
|
|
334
|
+
sectionThreshold?: number;
|
|
335
|
+
/**
|
|
336
|
+
* How grades are rounded. Required, with no default: see `RoundingPolicy` —
|
|
337
|
+
* grade rounding and band classification are different operations and the
|
|
338
|
+
* SDK must not choose either for you.
|
|
339
|
+
*/
|
|
340
|
+
rounding: RoundingPolicy;
|
|
341
|
+
}
|
|
342
|
+
/** Per-section result. */
|
|
343
|
+
interface SectionScore {
|
|
344
|
+
id: string;
|
|
345
|
+
title?: string;
|
|
346
|
+
/** The authored weight, verbatim. */
|
|
347
|
+
weight: number;
|
|
348
|
+
/**
|
|
349
|
+
* The weight ACTUALLY used in the total, so a client can rebuild the grade
|
|
350
|
+
* from `sections[]` and agree with the record:
|
|
351
|
+
*
|
|
352
|
+
* ```ts
|
|
353
|
+
* roundGrade(
|
|
354
|
+
* sections.reduce((sum, s) => sum + s.score * s.normalizedWeight, 0),
|
|
355
|
+
* policy.rounding,
|
|
356
|
+
* ) === result.score // exact, by construction
|
|
357
|
+
* ```
|
|
358
|
+
*
|
|
359
|
+
* Apply the same final rounding: the raw weighted sum of already-rounded
|
|
360
|
+
* section scores is not itself a rounded value (0.85 and 1.00 at equal
|
|
361
|
+
* weights sum to 0.925 against a recorded 0.93), so comparing it unrounded
|
|
362
|
+
* is off by up to half a quantum.
|
|
363
|
+
*
|
|
364
|
+
* Sections with nothing graded carry `0` here, because they contribute
|
|
365
|
+
* nothing; the remaining weights are renormalised among themselves.
|
|
366
|
+
*/
|
|
367
|
+
normalizedWeight: number;
|
|
368
|
+
earnedPoints: number;
|
|
369
|
+
/** Points that are currently gradable — excludes items still awaiting a grade. */
|
|
370
|
+
gradedMaxPoints: number;
|
|
371
|
+
/** Every point in the section, whether graded yet or not. */
|
|
372
|
+
maxPoints: number;
|
|
373
|
+
/** Scaled [0,1] over the GRADED points, rounded once. */
|
|
374
|
+
score: number;
|
|
375
|
+
passed: boolean;
|
|
376
|
+
/** Threshold this section was judged against, after any override. */
|
|
377
|
+
appliedThreshold: number | null;
|
|
378
|
+
/**
|
|
379
|
+
* Slots still awaiting a grade — including any in {@link rejectedSlotIds},
|
|
380
|
+
* whose grade came back unusable and is still owed.
|
|
381
|
+
*/
|
|
382
|
+
pendingSlotIds: string[];
|
|
383
|
+
/** See {@link AssessmentScore.rejectedSlotIds}; this section's share of them. */
|
|
384
|
+
rejectedSlotIds?: string[];
|
|
385
|
+
/** Slots that can never be graded, excluded from the denominator. */
|
|
386
|
+
unscorableSlotIds: string[];
|
|
387
|
+
}
|
|
388
|
+
/** Why an attempt failed, or `null` when it passed. */
|
|
389
|
+
type PassFailureReason = 'overall_below_threshold' | 'section_below_threshold' | 'both' | null;
|
|
390
|
+
/** Result of composing an assessment. */
|
|
391
|
+
interface AssessmentScore {
|
|
392
|
+
sections: SectionScore[];
|
|
393
|
+
/** Weighted total, scaled [0,1], rounded once. */
|
|
394
|
+
score: number;
|
|
395
|
+
/**
|
|
396
|
+
* Whether the attempt passed — `null` while `status` is `provisional`,
|
|
397
|
+
* because an attempt with work still ungraded has not passed OR failed yet.
|
|
398
|
+
* Returning `false` there would let a UI keyed on `passed` show a fail for
|
|
399
|
+
* an essay nobody has marked.
|
|
400
|
+
*/
|
|
401
|
+
passed: boolean | null;
|
|
402
|
+
/** `null` while provisional, for the same reason as {@link passed}. */
|
|
403
|
+
passFailureReason: PassFailureReason;
|
|
404
|
+
/**
|
|
405
|
+
* `provisional` while any item is still awaiting a grade — the total is
|
|
406
|
+
* computed over what HAS been graded, so it can still move. Do not record a
|
|
407
|
+
* provisional score as final. Items that can NEVER be graded
|
|
408
|
+
* (`unscorableSlotIds`) do not hold the result provisional; items whose
|
|
409
|
+
* grade was rejected (`rejectedSlotIds`) do.
|
|
410
|
+
*/
|
|
411
|
+
status: 'final' | 'provisional';
|
|
412
|
+
/**
|
|
413
|
+
* Slots still awaiting a grade, excluded from the denominator. `status` is
|
|
414
|
+
* `provisional` exactly when this is not empty. Includes every slot in
|
|
415
|
+
* {@link rejectedSlotIds}.
|
|
416
|
+
*/
|
|
417
|
+
pendingSlotIds: string[];
|
|
418
|
+
/**
|
|
419
|
+
* Slots whose outcome claimed a grade that cannot be one: a `score` or
|
|
420
|
+
* `maxScore` that is not a finite number, a `maxScore` of 0 or less, or a
|
|
421
|
+
* score below 0 or above its `maxScore` (beyond float noise of one part in a
|
|
422
|
+
* billion) — and deferred outcomes `outcomeFromGrade` returned for such a
|
|
423
|
+
* record (`reason: 'grade_rejected'`). Each is also in
|
|
424
|
+
* {@link pendingSlotIds}: its real grade is still owed, so it holds the
|
|
425
|
+
* result `provisional` with `passed: null` rather than being counted (NaN or
|
|
426
|
+
* 4300% on the record) or dropped (a failing grade vanishing into a final
|
|
427
|
+
* pass). Re-grade these slots; waiting will not fix them.
|
|
428
|
+
*
|
|
429
|
+
* Present only when at least one slot was rejected, so a result without one
|
|
430
|
+
* is exactly what earlier versions returned.
|
|
431
|
+
*/
|
|
432
|
+
rejectedSlotIds?: string[];
|
|
433
|
+
/** Slots that can never be graded. Excluded from the denominator. */
|
|
434
|
+
unscorableSlotIds: string[];
|
|
435
|
+
}
|
|
436
|
+
/**
|
|
437
|
+
* Composes per-item outcomes into a sectioned assessment score.
|
|
438
|
+
*
|
|
439
|
+
* Weights are normalised by their sum. Each section's score is computed over
|
|
440
|
+
* the points that are actually gradable and rounded ONCE, before any threshold
|
|
441
|
+
* comparison, so the number a learner is shown is the number that decides the
|
|
442
|
+
* outcome. Items still awaiting a grade are excluded from the denominator
|
|
443
|
+
* rather than counted as zero, and the result is reported as `provisional`
|
|
444
|
+
* until every item has a grade. An outcome whose score cannot be a grade out of
|
|
445
|
+
* its `maxScore` is not a grade either: it is held the same way and named in
|
|
446
|
+
* `rejectedSlotIds`.
|
|
447
|
+
*/
|
|
448
|
+
declare function composeAssessmentScore(sections: readonly AssessmentSectionInput[], policy: CompositionPolicy): AssessmentScore;
|
|
449
|
+
//#endregion
|
|
450
|
+
//#region src/types/item-scoring.d.ts
|
|
451
|
+
/**
|
|
452
|
+
* How the questions on a paper are scored when a learner can try again or ask
|
|
453
|
+
* for hints: how many tries there are, which one counts, and what each try and
|
|
454
|
+
* each hint costs.
|
|
455
|
+
*
|
|
456
|
+
* It sits beside the {@link DeliveryPolicy} and is set per paper in the same
|
|
457
|
+
* way, but it is a separate object for a reason: every delivery setting takes
|
|
458
|
+
* something away from what a learner is shown and cannot move a grade, while
|
|
459
|
+
* every setting here moves one. A delivery policy can be read charitably; this
|
|
460
|
+
* one cannot, because a grade-moving setting has no safe direction to read a
|
|
461
|
+
* mistake in. {@link resolveItemScoringPolicy} throws on one.
|
|
462
|
+
*
|
|
463
|
+
* Every setting is optional, and absent or `null` is its default. The defaults
|
|
464
|
+
* are exactly how questions were scored before this policy existed: one try,
|
|
465
|
+
* nothing costs anything, and the first (only) try counts.
|
|
466
|
+
*
|
|
467
|
+
* **The SDK's components apply it in `practice`**, where they grade. An exam's
|
|
468
|
+
* hints stay free and uncounted, as they always were. See
|
|
469
|
+
* https://github.com/intellectif/learning-kit/blob/main/docs/scoring.md
|
|
470
|
+
*
|
|
471
|
+
* @see {@link scoreTries} for the arithmetic.
|
|
472
|
+
*/
|
|
473
|
+
interface ItemScoringPolicy {
|
|
474
|
+
/**
|
|
475
|
+
* What each hint costs, as a fraction of the question's marks: `0.1` takes
|
|
476
|
+
* a tenth of them per hint. A hint is any a learner is shown before
|
|
477
|
+
* answering — an author's hint on a blank, a dictation's hint word, an AI
|
|
478
|
+
* hint — and is counted in the response's `hintsRevealed`.
|
|
479
|
+
*
|
|
480
|
+
* A number from 0 to 1. Default 0.
|
|
481
|
+
*/
|
|
482
|
+
hintPenalty?: number | null;
|
|
483
|
+
/**
|
|
484
|
+
* How many more tries a learner gets after an answer that did not earn full
|
|
485
|
+
* marks: "Try again". A whole number from 0 to 10. Default 0 — one try, as
|
|
486
|
+
* before.
|
|
487
|
+
*/
|
|
488
|
+
retries?: number | null;
|
|
489
|
+
/**
|
|
490
|
+
* What each try after the first costs, as a fraction of the question's
|
|
491
|
+
* marks: with `0.25`, a right answer on the second try earns 75%. A number
|
|
492
|
+
* from 0 to 1. Default 0.
|
|
493
|
+
*
|
|
494
|
+
* It applies only where a later try can count — `counts: 'best'` or
|
|
495
|
+
* `'last'` — and {@link validateItemScoringPolicy} refuses it otherwise.
|
|
496
|
+
*/
|
|
497
|
+
retryPenalty?: number | null;
|
|
498
|
+
/**
|
|
499
|
+
* Which try's score is the question's score.
|
|
500
|
+
*
|
|
501
|
+
* - `first` (default): the first. Tries after it are for learning, and
|
|
502
|
+
* change no grade — which is why it is the default: switching tries on
|
|
503
|
+
* moves no grade until a school also chooses one of the others.
|
|
504
|
+
* - `best`: the highest, after what each try cost. Never goes down.
|
|
505
|
+
* - `last`: the latest the policy allows.
|
|
506
|
+
*/
|
|
507
|
+
counts?: ItemScoringCount | null;
|
|
508
|
+
}
|
|
509
|
+
/** Which try counts: see {@link ItemScoringPolicy.counts}. */
|
|
510
|
+
type ItemScoringCount = 'first' | 'best' | 'last';
|
|
511
|
+
/**
|
|
512
|
+
* A policy with every setting spelled out: what
|
|
513
|
+
* {@link resolveItemScoringPolicy} returns and what an attempt plan records,
|
|
514
|
+
* so the record never depends on a default that may one day change.
|
|
515
|
+
*/
|
|
516
|
+
interface ResolvedItemScoringPolicy {
|
|
517
|
+
hintPenalty: number;
|
|
518
|
+
retries: number;
|
|
519
|
+
retryPenalty: number;
|
|
520
|
+
counts: ItemScoringCount;
|
|
521
|
+
}
|
|
522
|
+
/** One thing {@link validateItemScoringPolicy} found wrong with a policy. */
|
|
523
|
+
interface ItemScoringPolicyIssue {
|
|
524
|
+
/** The setting, e.g. `hintPenalty`, or `''` for the policy itself. */
|
|
525
|
+
path: string;
|
|
526
|
+
message: string;
|
|
527
|
+
}
|
|
528
|
+
/** One graded try at a question, as {@link scoreTries} reads it. */
|
|
529
|
+
interface ItemTry {
|
|
530
|
+
/** What the try's answer scored, before anything it cost: from `score()`. */
|
|
531
|
+
score: number;
|
|
532
|
+
maxScore: number;
|
|
533
|
+
/**
|
|
534
|
+
* Hints shown before this try was submitted, counted from the start of the
|
|
535
|
+
* question — so a later try's count includes an earlier one's. The response's
|
|
536
|
+
* `hintsRevealed`. Anything but a whole number of 0 or more reads as 0.
|
|
537
|
+
*/
|
|
538
|
+
hintsRevealed?: number | undefined;
|
|
539
|
+
}
|
|
540
|
+
/** One try, as {@link scoreTries} scored it. */
|
|
541
|
+
interface ItemTryScore {
|
|
542
|
+
/** What the answer scored, before anything it cost. */
|
|
543
|
+
score: number;
|
|
544
|
+
/** What it scored after what it cost, never below 0. */
|
|
545
|
+
scored: number;
|
|
546
|
+
maxScore: number;
|
|
547
|
+
/** The hints it was charged for. */
|
|
548
|
+
hintsRevealed: number;
|
|
549
|
+
/** What it cost, as a fraction of `maxScore`: hints, and the tries before it. */
|
|
550
|
+
penalty: number;
|
|
551
|
+
}
|
|
552
|
+
/** A question's score under a policy, from its tries. */
|
|
553
|
+
interface ItemTriesScore {
|
|
554
|
+
/** The counted try's score after what it cost, never below 0. */
|
|
555
|
+
score: number;
|
|
556
|
+
maxScore: number;
|
|
557
|
+
/** 0-based: the try whose score counts. */
|
|
558
|
+
counted: number;
|
|
559
|
+
/**
|
|
560
|
+
* Every try the policy allows, in order, as scored. A try past the
|
|
561
|
+
* allowance (`1 + retries`) is left out: a learner cannot be given one, so a
|
|
562
|
+
* record that holds one is not believed.
|
|
563
|
+
*/
|
|
564
|
+
tries: ItemTryScore[];
|
|
565
|
+
}
|
|
566
|
+
//#endregion
|
|
567
|
+
//#region src/scoring/dictation/align.d.ts
|
|
568
|
+
/** One transcript word, or one extra word the learner typed, after alignment. */
|
|
569
|
+
interface DictationWordAlignment {
|
|
570
|
+
/** `w<n>`, the 1-based position of a transcript word; absent for an extra word. */
|
|
571
|
+
itemId?: string;
|
|
572
|
+
/** The transcript word, normalised; `''` for an extra word. */
|
|
573
|
+
reference: string;
|
|
574
|
+
/** The word the learner typed for it, normalised; `''` for a missing word. */
|
|
575
|
+
attempt: string;
|
|
576
|
+
/** How alike the two words are, in [0, 1]; `0` for a missing or an extra word. */
|
|
577
|
+
similarity: number;
|
|
578
|
+
status: 'correct' | 'incorrect' | 'missing' | 'extra';
|
|
579
|
+
}
|
|
580
|
+
/**
|
|
581
|
+
* The comparison a dictation grade is made of: the normalised strings, which
|
|
582
|
+
* candidate transcript the attempt was scored against, and the word pairings.
|
|
583
|
+
* The same data the scorer reads, so a marked display and the grade cannot
|
|
584
|
+
* disagree.
|
|
585
|
+
*/
|
|
586
|
+
interface DictationAlignment {
|
|
587
|
+
/**
|
|
588
|
+
* Which candidate the attempt was scored against, by ORIGINAL position:
|
|
589
|
+
* `0` is `transcript`, `n` is `acceptedTranscripts[n − 1]`; `-1` when no
|
|
590
|
+
* candidate survives normalisation (stale or transcript-less data).
|
|
591
|
+
*/
|
|
592
|
+
candidateIndex: number;
|
|
593
|
+
/** The chosen candidate, normalised; `''` when `candidateIndex` is `-1`. */
|
|
594
|
+
reference: string;
|
|
595
|
+
/** The attempt, normalised and possibly truncated. */
|
|
596
|
+
attempt: string;
|
|
597
|
+
/**
|
|
598
|
+
* Whether the learner's text was cut: at `DICTATION_MAX_TEXT_LENGTH` before or
|
|
599
|
+
* after normalisation, or while equivalence rules were growing it.
|
|
600
|
+
*/
|
|
601
|
+
truncated: boolean;
|
|
602
|
+
/** The similarity of `attempt` to `reference` — the score. */
|
|
603
|
+
similarity: number;
|
|
604
|
+
/** Transcript words and extra words, in the order they align. */
|
|
605
|
+
words: DictationWordAlignment[];
|
|
606
|
+
}
|
|
607
|
+
/**
|
|
608
|
+
* What `alignDictation` reads. Every field optional, and read defensively: full
|
|
609
|
+
* data, a practice payload, a `redact(data, { reveal: 'after-submit' })`
|
|
610
|
+
* projection, or a plain redacted projection with no transcript at all — which
|
|
611
|
+
* yields `candidateIndex: -1`, never a throw.
|
|
612
|
+
*/
|
|
613
|
+
type DictationReference = Partial<Pick<DictationData, 'transcript' | 'acceptedTranscripts' | 'tolerance'>>;
|
|
614
|
+
/**
|
|
615
|
+
* The words the scorer can emit a detail for, one list per candidate — index 0
|
|
616
|
+
* is the transcript, index n is `acceptedTranscripts[n − 1]` — each word in the
|
|
617
|
+
* normalised form `correctResponse` carries. A candidate that normalises to
|
|
618
|
+
* nothing gives an empty list. Derivable from the activity alone, which is what
|
|
619
|
+
* a validator of stored details joins against: by id AND by word, because
|
|
620
|
+
* positional ids alone cannot tell an edited transcript of the same length.
|
|
621
|
+
*/
|
|
622
|
+
declare function dictationReferenceWords(data: DictationReference): readonly (readonly {
|
|
623
|
+
itemId: string;
|
|
624
|
+
word: string;
|
|
625
|
+
}[])[];
|
|
626
|
+
/**
|
|
627
|
+
* Compares a learner's text with a dictation's transcript(s). Pure and
|
|
628
|
+
* deterministic: no locale, no `Intl`, integer alignment costs, one pinned
|
|
629
|
+
* tie order.
|
|
630
|
+
*
|
|
631
|
+
* The attempt is cut at `DICTATION_MAX_TEXT_LENGTH` code points before and
|
|
632
|
+
* after normalisation — a cut that ends on a space leaves no empty word after
|
|
633
|
+
* it — and `truncated` is also set when the rules grew its
|
|
634
|
+
* working text past the normaliser's bound (see `normalizeDictationText`).
|
|
635
|
+
* Every candidate — the transcript and at most
|
|
636
|
+
* `DICTATION_MAX_ACCEPTED_TRANSCRIPTS` accepted transcripts — is normalised
|
|
637
|
+
* with the same tolerance and cut at `DICTATION_MAX_TRANSCRIPT_LENGTH`; the
|
|
638
|
+
* attempt is scored against each surviving candidate and the first best wins.
|
|
639
|
+
* Words are then paired by a word-level edit distance with exact-equality
|
|
640
|
+
* costs, backtracked from the end preferring a pairing, then a missing
|
|
641
|
+
* transcript word, then an extra typed word; a paired word's similarity is its
|
|
642
|
+
* own character similarity.
|
|
643
|
+
*/
|
|
644
|
+
declare function alignDictation(data: DictationReference, text: string): DictationAlignment;
|
|
645
|
+
//#endregion
|
|
646
|
+
//#region src/scoring/dictation/chars.d.ts
|
|
647
|
+
/** One code-point edit between a reference string and an attempt. */
|
|
648
|
+
interface DictationCharOp {
|
|
649
|
+
op: 'equal' | 'substitute' | 'missing' | 'extra';
|
|
650
|
+
/** One code point of the reference, or `''` for an extra character. */
|
|
651
|
+
reference: string;
|
|
652
|
+
/** One code point of the attempt, or `''` for a missing character. */
|
|
653
|
+
attempt: string;
|
|
654
|
+
}
|
|
655
|
+
/**
|
|
656
|
+
* The code-point edit operations between two NORMALISED strings — the
|
|
657
|
+
* `reference` and `attempt` of a `DictationAlignment`, or one word pair from
|
|
658
|
+
* it — for a marked display: a character the learner typed right, typed
|
|
659
|
+
* wrong, left out, or added. Ties are broken preferring an equal or substituted
|
|
660
|
+
* pair, then a missing character, then an extra one, from the end of both
|
|
661
|
+
* strings; the number of non-`equal` ops is the edit distance.
|
|
662
|
+
*
|
|
663
|
+
* Decoration, never grading: it needs O(|reference|·|attempt|) memory, so the
|
|
664
|
+
* scorer does not call it, and a display computes it once per result.
|
|
665
|
+
*/
|
|
666
|
+
declare function diffDictationChars(reference: string, attempt: string): DictationCharOp[];
|
|
667
|
+
//#endregion
|
|
668
|
+
//#region src/scoring/dictation/normalize.d.ts
|
|
669
|
+
/**
|
|
670
|
+
* The longest transcript a dictation accepts, in code points, before and after
|
|
671
|
+
* its equivalences are applied — and its rules may not grow it past twice
|
|
672
|
+
* `DICTATION_MAX_TEXT_LENGTH` on the way. A dictation is a sentence or two; the
|
|
673
|
+
* cap keeps the scorer's quadratic step bounded on the server that grades an
|
|
674
|
+
* exam.
|
|
675
|
+
*/
|
|
676
|
+
declare const DICTATION_MAX_TRANSCRIPT_LENGTH = 2000;
|
|
677
|
+
/**
|
|
678
|
+
* The longest learner text the scorer compares, in code points. Text beyond it
|
|
679
|
+
* is cut off — before normalisation, and again after it, because a rewrite rule
|
|
680
|
+
* or a folded character can lengthen text — and the alignment says so.
|
|
681
|
+
*/
|
|
682
|
+
declare const DICTATION_MAX_TEXT_LENGTH = 8000;
|
|
683
|
+
/** The longest `from` and `to` of an equivalence rule, in code points. */
|
|
684
|
+
declare const DICTATION_MAX_EQUIVALENCE_LENGTH = 200;
|
|
685
|
+
/**
|
|
686
|
+
* The most equivalence rules a dictation carries. The scorer reads no rule
|
|
687
|
+
* past this many, so stale data with more cannot slow a grading run.
|
|
688
|
+
*/
|
|
689
|
+
declare const DICTATION_MAX_EQUIVALENCES = 100;
|
|
690
|
+
/**
|
|
691
|
+
* The most accepted transcripts a dictation carries. The scorer reads no more
|
|
692
|
+
* than this many, for the same reason.
|
|
693
|
+
*/
|
|
694
|
+
declare const DICTATION_MAX_ACCEPTED_TRANSCRIPTS = 10;
|
|
695
|
+
//#endregion
|
|
696
|
+
//#region src/scoring/item-scoring.d.ts
|
|
697
|
+
/**
|
|
698
|
+
* The policy that changes nothing: one try, nothing costs anything, the first
|
|
699
|
+
* try counts. What an absent policy resolves to, and exactly how questions were
|
|
700
|
+
* scored before policies existed.
|
|
701
|
+
*/
|
|
702
|
+
declare const DEFAULT_ITEM_SCORING_POLICY: ResolvedItemScoringPolicy;
|
|
703
|
+
/** The most tries after the first a policy can give. */
|
|
704
|
+
declare const ITEM_SCORING_MAX_RETRIES = 10;
|
|
705
|
+
/**
|
|
706
|
+
* Checks a policy before it is stored or used: every setting absent, `null`,
|
|
707
|
+
* or a value it can take — costs from 0 to 1, `retries` a whole number from 0
|
|
708
|
+
* to {@link ITEM_SCORING_MAX_RETRIES}, `counts` one of `first`, `best` or
|
|
709
|
+
* `last` — and no setting it does not know.
|
|
710
|
+
*
|
|
711
|
+
* It also refuses a `retryPenalty` above 0 while the first try counts: that
|
|
712
|
+
* cost can never be charged, and a school that set it believes retries cost
|
|
713
|
+
* something. Choose `counts: 'best'` or `'last'`.
|
|
714
|
+
*/
|
|
715
|
+
declare function validateItemScoringPolicy(policy: unknown): {
|
|
716
|
+
success: true;
|
|
717
|
+
data: ItemScoringPolicy;
|
|
718
|
+
} | {
|
|
719
|
+
success: false;
|
|
720
|
+
issues: ItemScoringPolicyIssue[];
|
|
721
|
+
};
|
|
722
|
+
/**
|
|
723
|
+
* A policy with every setting spelled out.
|
|
724
|
+
*
|
|
725
|
+
* **It throws rather than guess.** A delivery policy reads a setting it cannot
|
|
726
|
+
* read as a restriction, because that is the safe way round; a setting here
|
|
727
|
+
* moves a grade, and there is no safe way round — a cost misread as 0 raises
|
|
728
|
+
* grades, misread as 1 lowers them. So a policy {@link validateItemScoringPolicy}
|
|
729
|
+
* refuses is refused here too, with a `RangeError` naming what is wrong, as an
|
|
730
|
+
* unreadable `rounding` is.
|
|
731
|
+
*/
|
|
732
|
+
declare function resolveItemScoringPolicy(policy: unknown): ResolvedItemScoringPolicy;
|
|
733
|
+
/**
|
|
734
|
+
* A question's score under a policy, from its tries in the order they were
|
|
735
|
+
* made.
|
|
736
|
+
*
|
|
737
|
+
* - **What a try costs** is a fraction of the question's marks, subtracted
|
|
738
|
+
* from what its answer scored: `hintPenalty` for every hint shown before it
|
|
739
|
+
* (its `hintsRevealed`, which counts from the start of the question), and
|
|
740
|
+
* `retryPenalty` for every try before it. A try never scores below 0.
|
|
741
|
+
* - **Which try counts** is the policy's `counts`: the first, the best after
|
|
742
|
+
* costs (the earliest, where two are equal), or the last.
|
|
743
|
+
* - **Only the tries the policy allows are read**: the first `1 + retries`. A
|
|
744
|
+
* record with more cannot have come from a learner under this policy.
|
|
745
|
+
*
|
|
746
|
+
* Nothing that cost nothing is changed: with no cost to charge, a try's score
|
|
747
|
+
* is exactly what its answer scored.
|
|
748
|
+
*
|
|
749
|
+
* @throws RangeError for a policy {@link resolveItemScoringPolicy} refuses, for
|
|
750
|
+
* an empty list of tries, and for a try whose `score` and `maxScore`
|
|
751
|
+
* cannot be a grade — the rule `outcomeFromGrade` applies: `maxScore`
|
|
752
|
+
* positive and finite, `score` finite from 0 to it.
|
|
753
|
+
*/
|
|
754
|
+
declare function scoreTries(tries: readonly ItemTry[], policy?: ItemScoringPolicy | ResolvedItemScoringPolicy | null): ItemTriesScore;
|
|
755
|
+
//#endregion
|
|
756
|
+
//#region src/scoring/pass-threshold.d.ts
|
|
757
|
+
/** Default minimum scaled score required to pass when `passThreshold` is absent. */
|
|
758
|
+
declare const DEFAULT_PASS_THRESHOLD = 0.7;
|
|
759
|
+
/**
|
|
760
|
+
* Returns `true` iff `score` meets or exceeds the activity's `passThreshold`,
|
|
761
|
+
* defaulting to {@link DEFAULT_PASS_THRESHOLD} (0.7) when the field is absent.
|
|
762
|
+
*
|
|
763
|
+
* Pass a {@link RoundingPolicy} to compare the way an assessment total is
|
|
764
|
+
* compared — both sides rounded, via {@link gte} — so an item shown as "70%"
|
|
765
|
+
* cannot be recorded as a fail at 69.6. It is **opt-in** rather than the
|
|
766
|
+
* default because switching it on changes item-level pass/fail for scores in
|
|
767
|
+
* the rounding band, and this SDK does not alter historical grades without an
|
|
768
|
+
* explicit decision. Absent, the comparison is the exact raw `>=` it has
|
|
769
|
+
* always been.
|
|
770
|
+
*/
|
|
771
|
+
declare function computePassThreshold(activityData: ActivityData, score: number, rounding?: RoundingPolicy): boolean;
|
|
772
|
+
//#endregion
|
|
773
|
+
//#region src/scoring/speech/align.d.ts
|
|
774
|
+
/**
|
|
775
|
+
* Pairs the words of `assessment` with the words of `data.referenceText`, in
|
|
776
|
+
* reading order: what was read as written, what was mispronounced, what was
|
|
777
|
+
* left out, and what was said that the text does not contain.
|
|
778
|
+
*
|
|
779
|
+
* Reference words are the dictation tokeniser's, so `itemId` is the `w<n>`
|
|
780
|
+
* `dictationReferenceWords` gives, and both `reference` and `heard` are
|
|
781
|
+
* normalised — lowercased, with punctuation and spacing folded away. One
|
|
782
|
+
* assessor word can normalise to no token at all (a dash), or to several (a
|
|
783
|
+
* compatibility form), and two can merge into one (`rock&roll`); tokens are
|
|
784
|
+
* what is aligned, and each remembers the word it came from.
|
|
785
|
+
*
|
|
786
|
+
* A word the assessor tagged `insertion` is never aligned — the reference has
|
|
787
|
+
* nothing for it — so it is placed by the position of its own word, after the
|
|
788
|
+
* last token that came from an earlier word.
|
|
789
|
+
*
|
|
790
|
+
* Whether a paired word counts as read correctly follows `assessment.miscue`:
|
|
791
|
+
* an assessor that judges miscues is trusted, and one that does not
|
|
792
|
+
* (`miscue: 'none'`) has its recognised word compared with the reference word.
|
|
793
|
+
*
|
|
794
|
+
* `heard` is `''` for an omitted word, and also for an inserted word whose text
|
|
795
|
+
* normalises to nothing. Branch on `state`, never on the empty string.
|
|
796
|
+
*
|
|
797
|
+
* The heard tokens are bounded: they are taken in reading order until what the
|
|
798
|
+
* assessment's words spell would pass `DICTATION_MAX_TEXT_LENGTH` code points —
|
|
799
|
+
* the cap the dictation aligner reads a learner's text under — and always on a
|
|
800
|
+
* whole token, because half a token pairs with the wrong word. Every word is
|
|
801
|
+
* charged, an inserted one included, so that total is the whole of the text
|
|
802
|
+
* this function can answer with. Words past the budget contribute no token, so
|
|
803
|
+
* each reference word left with nothing read for it is `omitted`.
|
|
804
|
+
* `validateSpeechAssessment` refuses an assessment whose words spell more than
|
|
805
|
+
* that, measured by the same walk and against the same number, so a validated
|
|
806
|
+
* assessment never reaches the budget: it is a backstop, kept because this
|
|
807
|
+
* function is public and can be handed evidence this build did not check.
|
|
808
|
+
*
|
|
809
|
+
* **`data` is not validated.** Only the assessment is. A `referenceText` longer
|
|
810
|
+
* than `READ_ALOUD_MAX_REFERENCE_LENGTH` code points is cut by the tokeniser
|
|
811
|
+
* with no signal, and one that is absent or is not a string yields no reference
|
|
812
|
+
* word at all, so every heard token comes back `inserted` rather than refused.
|
|
813
|
+
* Validate the item with `validateActivity('read-aloud', data)` first.
|
|
814
|
+
* `gradeReadAloud` is safe without that because it runs the schema itself.
|
|
815
|
+
*
|
|
816
|
+
* Pure and deterministic: no locale, no `Intl`, no clock.
|
|
817
|
+
*
|
|
818
|
+
* @throws TypeError when `assessment` is not a well-formed
|
|
819
|
+
* {@link SpeechAssessment}; check it with `validateSpeechAssessment` first.
|
|
820
|
+
*/
|
|
821
|
+
declare function alignReadAloud(data: Pick<ReadAloudData, 'referenceText'>, assessment: SpeechAssessment): ReadAloudWordAlignment[];
|
|
822
|
+
//#endregion
|
|
823
|
+
//#region src/scoring/speech/assessment.d.ts
|
|
824
|
+
/**
|
|
825
|
+
* Checks that `value` is a well-formed {@link SpeechAssessment}. It answers
|
|
826
|
+
* rather than throwing: anything that is not one — a non-object, an unknown key
|
|
827
|
+
* at any depth, a score off the 0..100 scale, a time that is negative or not
|
|
828
|
+
* finite — comes back as `{ success: false, errors }`, one error per problem,
|
|
829
|
+
* each with the path, message and code `validateActivity` gives a schema
|
|
830
|
+
* failure. An object that throws when it is read is the one exception, and the
|
|
831
|
+
* throw is its own: an own property getter, or a `Proxy` trap.
|
|
832
|
+
*
|
|
833
|
+
* Three rules span fields:
|
|
834
|
+
* - `scripted_binding_required` — a `scripted` assessment without
|
|
835
|
+
* `referenceText`, or without `recordingKey`, reported at each missing path;
|
|
836
|
+
* - `phoneme_alphabet_required` — a phoneme names a `symbol` or `heardAs` while
|
|
837
|
+
* `phonemeAlphabet` is absent, reported once, at `phonemeAlphabet`;
|
|
838
|
+
* - `too_big` at `words` — the words carry more text between them, written or
|
|
839
|
+
* normalised, than can be aligned against an item's text. Per-word caps bound
|
|
840
|
+
* no total, and the aligner pairs tokens, of which one word holds as many as
|
|
841
|
+
* its text spells. Every word counts towards both totals, an insertion
|
|
842
|
+
* included: the aligner carries one into its answer, so its text is text the
|
|
843
|
+
* call must hold.
|
|
844
|
+
*
|
|
845
|
+
* The total is measured on the words every field rule accepted, so it says
|
|
846
|
+
* nothing about an assessment that is going back to its adapter anyway.
|
|
847
|
+
*
|
|
848
|
+
* It checks the evidence alone. Whether an assessment belongs to an item and
|
|
849
|
+
* to a learner's recording is decided when it is graded.
|
|
850
|
+
*/
|
|
851
|
+
declare function validateSpeechAssessment(value: unknown): ValidationResult<SpeechAssessment>;
|
|
852
|
+
//#endregion
|
|
853
|
+
//#region src/scoring/speech/grade.d.ts
|
|
854
|
+
/**
|
|
855
|
+
* Grades a read-aloud take from a {@link SpeechAssessment} the application
|
|
856
|
+
* obtained, the response that was submitted, and the server's own measurement
|
|
857
|
+
* of the recording.
|
|
858
|
+
*
|
|
859
|
+
* The weighted total is computed by `gradeFromRubric` from the authored
|
|
860
|
+
* dimensions that carry a weight above 0, each out of 100. The per-word marks
|
|
861
|
+
* in `details` are for display and review: **they never feed the score**, which
|
|
862
|
+
* is the assessor's utterance-level judgement, weighted as the item authored it.
|
|
863
|
+
*
|
|
864
|
+
* Evidence is refused, never scored 0, when it cannot be tied to this item and
|
|
865
|
+
* this recording, when the assessor heard nothing, when the recording holds too
|
|
866
|
+
* little voiced time for the words claimed, or when a weighted dimension has no
|
|
867
|
+
* score — see {@link SpeechUnscorableCode}. Store a refusal with
|
|
868
|
+
* `outcomeFromUnscorable`.
|
|
869
|
+
*
|
|
870
|
+
* A blank (`response.recording === null`) is a grade of 0 with a mark per
|
|
871
|
+
* reference word: the learner submitted, and read nothing. The assessment and
|
|
872
|
+
* the measurement are not read for it.
|
|
873
|
+
*
|
|
874
|
+
* @throws RangeError when `options.plausibility` or `options.rounding` cannot be
|
|
875
|
+
* applied, or when `options.measured` is not a pair of non-negative, finite
|
|
876
|
+
* millisecond counts with `voicedMs` inside `durationMs`.
|
|
877
|
+
* @throws TypeError when `response` is not a read-aloud response, or when a
|
|
878
|
+
* non-blank response is graded without an assessment or without a measurement.
|
|
879
|
+
* @throws ActivitySchemaError when `data` is not valid read-aloud data.
|
|
880
|
+
* @throws RedactedScoringError when `data` is a `redact()` projection. A
|
|
881
|
+
* read-aloud projection is a valid activity, so the authored feedback would
|
|
882
|
+
* simply be missing from the grade; grade against the full data server-side.
|
|
883
|
+
*/
|
|
884
|
+
declare function gradeReadAloud(data: ReadAloudData, response: ReadAloudLearnerResponse, assessment: SpeechAssessment | null, options: GradeReadAloudOptions): GradeRecord | SpeechUnscorable;
|
|
885
|
+
//#endregion
|
|
886
|
+
//#region src/scoring/speech/limits.d.ts
|
|
887
|
+
/**
|
|
888
|
+
* The longest reference text a read-aloud item accepts, in code points, before
|
|
889
|
+
* and after normalisation. It is the dictation transcript cap on purpose: the
|
|
890
|
+
* reference is tokenised as a dictation transcript is, and the dictation
|
|
891
|
+
* normaliser cuts a transcript at that many code points without saying so, so
|
|
892
|
+
* a longer text would be graded against words it had silently lost.
|
|
893
|
+
*/
|
|
894
|
+
declare const READ_ALOUD_MAX_REFERENCE_LENGTH = 2000;
|
|
895
|
+
/** The longest take a read-aloud item may allow, in seconds: the most `recording.maxSeconds` may be. */
|
|
896
|
+
declare const READ_ALOUD_MAX_SECONDS = 300;
|
|
897
|
+
/** The most takes a read-aloud item may allow: the most `recording.maxTakes` may be. */
|
|
898
|
+
declare const READ_ALOUD_MAX_TAKES = 20;
|
|
899
|
+
/**
|
|
900
|
+
* The largest weight a read-aloud dimension may carry. Weights are relative, so
|
|
901
|
+
* no rubric needs more, and the cap keeps a mistyped weight from overflowing
|
|
902
|
+
* the weighted total into a grade that cannot be computed.
|
|
903
|
+
*/
|
|
904
|
+
declare const READ_ALOUD_MAX_DIMENSION_WEIGHT = 1000;
|
|
905
|
+
/**
|
|
906
|
+
* The most words a speech assessment may carry. It bounds one side of the
|
|
907
|
+
* alignment, and one side alone bounds nothing: the aligner pairs TOKENS, and
|
|
908
|
+
* one word normalises to as many as its text holds. What bounds the quadratic
|
|
909
|
+
* work is this cap, the 200-unit cap on a single word's text, and the two rules
|
|
910
|
+
* `validateSpeechAssessment` states over the total of them — what the words are
|
|
911
|
+
* written with, and what they spell once normalised.
|
|
912
|
+
*/
|
|
913
|
+
declare const SPEECH_ASSESSMENT_MAX_WORDS = 1000;
|
|
914
|
+
//#endregion
|
|
915
|
+
//#region src/scoring/speech/wav.d.ts
|
|
916
|
+
/**
|
|
917
|
+
* Reads a 16-bit PCM WAV and measures it: `durationMs`, `sampleRate`,
|
|
918
|
+
* `channels`, the loudest sample as `peakDbfs`, and `voicedMs`, the time in
|
|
919
|
+
* windows whose RMS level reaches the policy's silence floor.
|
|
920
|
+
*
|
|
921
|
+
* Anything it cannot measure comes back as `{ valid: false, reason }` rather
|
|
922
|
+
* than a guess: `not_wav` for bytes that are not a RIFF/WAVE file, or whose
|
|
923
|
+
* chunks do not describe the audio before the audio arrives;
|
|
924
|
+
* `unsupported_encoding` for a WAV this reader does not read (compressed, or
|
|
925
|
+
* samples that are not 16-bit); `truncated` when the data chunk is missing or
|
|
926
|
+
* shorter than it declares.
|
|
927
|
+
*
|
|
928
|
+
* Both policy fields are required, because neither has an answer that is right
|
|
929
|
+
* for every microphone: `silenceDbfs` is where an application draws the line
|
|
930
|
+
* between silence and speech, and `frameMs` is how finely it looks.
|
|
931
|
+
*
|
|
932
|
+
* Pure and deterministic. It never throws on the bytes themselves — only on
|
|
933
|
+
* arguments it cannot use.
|
|
934
|
+
*
|
|
935
|
+
* @throws TypeError when `bytes` is not a `Uint8Array`.
|
|
936
|
+
* @throws RangeError when `silenceDbfs` is not a finite number at or below 0,
|
|
937
|
+
* or `frameMs` is not a finite number above 0.
|
|
938
|
+
*/
|
|
939
|
+
declare function inspectWav(bytes: Uint8Array, policy: WavInspectionPolicy): WavInspection;
|
|
940
|
+
//#endregion
|
|
941
|
+
//#region src/scoring/unscorable.d.ts
|
|
942
|
+
/**
|
|
943
|
+
* Lifts a result that cannot be graded — a `SpeechUnscorable` from
|
|
944
|
+
* `gradeReadAloud`, or anything else carrying a `code` and a `reason` — into
|
|
945
|
+
* the `unscorable` arm of {@link ItemOutcome}, keeping its code.
|
|
946
|
+
*
|
|
947
|
+
* The code is what an application branches on; the reason is a developer-facing
|
|
948
|
+
* sentence. Stored this way, the result composes exactly as the unscorable
|
|
949
|
+
* outcomes `evaluate` returns do. `evaluate` never writes a code itself.
|
|
950
|
+
*/
|
|
951
|
+
declare function outcomeFromUnscorable(result: {
|
|
952
|
+
code: string;
|
|
953
|
+
reason: string;
|
|
954
|
+
}): ItemOutcome;
|
|
955
|
+
//#endregion
|
|
956
|
+
//#region src/scoring/index.d.ts
|
|
957
|
+
/**
|
|
958
|
+
* Options for {@link score} and {@link evaluate}. Additive: with none, both
|
|
959
|
+
* behave exactly as they always have.
|
|
960
|
+
*/
|
|
961
|
+
interface ScoringOptions {
|
|
962
|
+
/**
|
|
963
|
+
* Compare the pass line the way the score is displayed — both sides rounded,
|
|
964
|
+
* via {@link computePassThreshold} — so an item shown as "70%" is not
|
|
965
|
+
* recorded as a fail at 69.995. `passed` AND the authored feedback selected by
|
|
966
|
+
* it follow the rounded comparison. Opt-in, for the reason
|
|
967
|
+
* {@link computePassThreshold} gives: switching it on moves item-level
|
|
968
|
+
* pass/fail for scores in the rounding band.
|
|
969
|
+
*
|
|
970
|
+
* Checked at the call: `null` reads as no policy, and a policy that cannot be
|
|
971
|
+
* applied — an unknown `mode`, or a `dp` that is not a whole number from 0 to
|
|
972
|
+
* 15 — throws a `RangeError` instead of quietly failing every comparison.
|
|
973
|
+
*/
|
|
974
|
+
rounding?: RoundingPolicy;
|
|
975
|
+
}
|
|
976
|
+
/**
|
|
977
|
+
* Options for {@link evaluate} and {@link evaluateTries}: {@link ScoringOptions},
|
|
978
|
+
* and the paper's scoring policy.
|
|
979
|
+
*/
|
|
980
|
+
interface EvaluateOptions extends ScoringOptions {
|
|
981
|
+
/**
|
|
982
|
+
* What hints and tries cost, and which try counts — see
|
|
983
|
+
* {@link ItemScoringPolicy}. Absent or `null`, nothing costs anything and the
|
|
984
|
+
* outcome is exactly what it was before policies existed.
|
|
985
|
+
*
|
|
986
|
+
* `evaluate` reads the hints from the response's `hintsRevealed`. The count
|
|
987
|
+
* is the client's: when your server knows better — it served the AI hints,
|
|
988
|
+
* say — write its own count there before you evaluate.
|
|
989
|
+
*
|
|
990
|
+
* Checked at the call: a policy `validateItemScoringPolicy` refuses throws a
|
|
991
|
+
* `RangeError`. A grade-moving setting has no safe way to be misread. Under a
|
|
992
|
+
* policy, a scored outcome whose numbers cannot be a grade — a registered
|
|
993
|
+
* scorer out of contract — has nothing to charge a cost to, and comes back
|
|
994
|
+
* `unscorable`.
|
|
995
|
+
*/
|
|
996
|
+
scoring?: ItemScoringPolicy | null;
|
|
997
|
+
}
|
|
998
|
+
/**
|
|
999
|
+
* Scores a learner response against activity data and returns a full
|
|
1000
|
+
* {@link ScoringResult}.
|
|
1001
|
+
*
|
|
1002
|
+
* Pure and deterministic with no side effects. It does **not** re-validate
|
|
1003
|
+
* `activityData` — schema validation is the component boundary's
|
|
1004
|
+
* responsibility; this is a low-level scoring primitive that trusts its typed
|
|
1005
|
+
* inputs. Dispatch is registry-backed: consumer-registered types with `sync`
|
|
1006
|
+
* scoring work here too. An unregistered type throws
|
|
1007
|
+
* {@link UnknownActivityTypeError}; a type whose grading is deferred (e.g.
|
|
1008
|
+
* `written-response`) throws {@link DeferredScoringError} — use
|
|
1009
|
+
* {@link evaluate}, which can express "not graded yet".
|
|
1010
|
+
*/
|
|
1011
|
+
declare function score(activityType: ActivityType, activityData: ActivityData, learnerResponse: LearnerResponse, options?: ScoringOptions): ScoringResult;
|
|
1012
|
+
/**
|
|
1013
|
+
* Evaluates a learner response against an activity and returns an
|
|
1014
|
+
* {@link ItemOutcome} — the resilient, forward-compatible alternative to
|
|
1015
|
+
* {@link score}:
|
|
1016
|
+
*
|
|
1017
|
+
* - Synchronously graded types return `{ status: 'scored', ... }` with the
|
|
1018
|
+
* same numbers `score()` produces.
|
|
1019
|
+
* - Asynchronously graded types (e.g. `written-response`) return
|
|
1020
|
+
* `{ status: 'deferred', reason, partial }` instead of a fake zero — "not
|
|
1021
|
+
* graded yet" is expressible in the type system, never conflated with
|
|
1022
|
+
* "wrong".
|
|
1023
|
+
* - An unregistered `data.type` returns `{ status: 'unscorable' }` rather
|
|
1024
|
+
* than throwing, so a mixed-version content bank cannot crash an exam run.
|
|
1025
|
+
* - With a scoring policy (`options.scoring`), a scored outcome is charged what
|
|
1026
|
+
* the response's hints cost, and `passed` is read from what is left. The
|
|
1027
|
+
* `details` and authored `feedback` stay the answer's. For several tries at
|
|
1028
|
+
* one question, use {@link evaluateTries}.
|
|
1029
|
+
*
|
|
1030
|
+
* The activity type is read from `data.type` — there is no separate type
|
|
1031
|
+
* parameter to disagree with the payload.
|
|
1032
|
+
*/
|
|
1033
|
+
declare function evaluate(data: ActivityData, response: LearnerResponse, options?: EvaluateOptions): ItemOutcome;
|
|
1034
|
+
/** A question's outcome over its tries: see {@link evaluateTries}. */
|
|
1035
|
+
interface ItemTriesOutcome {
|
|
1036
|
+
/**
|
|
1037
|
+
* The question's outcome under the policy. Scored: the counted try's
|
|
1038
|
+
* `details` and authored `feedback` — they describe that answer — with its
|
|
1039
|
+
* `score` after what it cost and `passed` read from that score. Anything
|
|
1040
|
+
* else — deferred, unscorable — is the first try's outcome as `evaluate`
|
|
1041
|
+
* gives it.
|
|
1042
|
+
*/
|
|
1043
|
+
outcome: ItemOutcome;
|
|
1044
|
+
/** 0-based: the try whose score counts. `null` when the outcome is not scored. */
|
|
1045
|
+
counted: number | null;
|
|
1046
|
+
/**
|
|
1047
|
+
* Every try the policy allows, as scored: before and after what it cost.
|
|
1048
|
+
* Empty when the outcome is not scored, or when, with no policy, it is an
|
|
1049
|
+
* outcome `evaluate` passes through that cannot be a grade.
|
|
1050
|
+
*/
|
|
1051
|
+
tries: ItemTryScore[];
|
|
1052
|
+
}
|
|
1053
|
+
/**
|
|
1054
|
+
* A question's outcome from every try a learner made at it, in order, under a
|
|
1055
|
+
* paper's scoring policy: what a server that scores practice itself calls,
|
|
1056
|
+
* with the tries it stored, to reach the grade the SDK's components reached.
|
|
1057
|
+
*
|
|
1058
|
+
* Each response is scored as {@link evaluate} scores it, and costed and chosen
|
|
1059
|
+
* as {@link scoreTries} does: hints from each response's `hintsRevealed`,
|
|
1060
|
+
* which counts from the start of the question; the tries before each one;
|
|
1061
|
+
* and the try `counts` names. Only the first `1 + retries` responses are read.
|
|
1062
|
+
*
|
|
1063
|
+
* With no policy it is `evaluate` of the first response: one try, as before.
|
|
1064
|
+
* With no response at all, the question was never answered —
|
|
1065
|
+
* `{ status: 'deferred', reason: 'no_response_recorded' }`, as a missing slot
|
|
1066
|
+
* is everywhere else.
|
|
1067
|
+
*/
|
|
1068
|
+
declare function evaluateTries(data: ActivityData, responses: readonly LearnerResponse[], options?: EvaluateOptions): ItemTriesOutcome;
|
|
1069
|
+
//#endregion
|
|
1070
|
+
export { SpeechAssessment as $, diffDictationChars as A, ItemTry as B, validateItemScoringPolicy as C, DICTATION_MAX_TEXT_LENGTH as D, DICTATION_MAX_EQUIVALENCE_LENGTH as E, dictationReferenceWords as F, CompositionPolicy as G, ResolvedItemScoringPolicy as H, ItemScoringCount as I, SectionScore as J, PassFailureReason as K, ItemScoringPolicy as L, DictationReference as M, DictationWordAlignment as N, DICTATION_MAX_TRANSCRIPT_LENGTH as O, alignDictation as P, ReadAloudWordState as Q, ItemScoringPolicyIssue as R, scoreTries as S, DICTATION_MAX_EQUIVALENCES as T, AssessmentScore as U, ItemTryScore as V, AssessmentSectionInput as W, GradeReadAloudOptions as X, composeAssessmentScore as Y, ReadAloudWordAlignment as Z, DEFAULT_PASS_THRESHOLD as _, evaluateTries as a, SpeechUnscorable as at, ITEM_SCORING_MAX_RETRIES as b, inspectWav as c, SpeechWordError as ct, READ_ALOUD_MAX_SECONDS as d, Band as dt, SpeechMeasurement as et, READ_ALOUD_MAX_TAKES as f, RoundingMode as ft, alignReadAloud as g, roundGrade as gt, validateSpeechAssessment as h, gte as ht, evaluate as i, SpeechSyllable as it, DictationAlignment as j, DictationCharOp as k, READ_ALOUD_MAX_DIMENSION_WEIGHT as l, WavInspection as lt, gradeReadAloud as m, classifyBand as mt, ItemTriesOutcome as n, SpeechPhonemeCandidate as nt, score as o, SpeechUnscorableCode as ot, SPEECH_ASSESSMENT_MAX_WORDS as p, RoundingPolicy as pt, ScoredItem as q, ScoringOptions as r, SpeechPlausibilityPolicy as rt, outcomeFromUnscorable as s, SpeechWord as st, EvaluateOptions as t, SpeechPhoneme as tt, READ_ALOUD_MAX_REFERENCE_LENGTH as u, WavInspectionPolicy as ut, computePassThreshold as v, DICTATION_MAX_ACCEPTED_TRANSCRIPTS as w, resolveItemScoringPolicy as x, DEFAULT_ITEM_SCORING_POLICY as y, ItemTriesScore as z };
|
|
1071
|
+
//# sourceMappingURL=index-BAfEHCnD.d.cts.map
|