@intellectif/lk-core 1.1.0 → 1.2.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 +15 -0
- package/README.md +2 -1
- package/dist/{activity-MBOYLBjR.d.cts → activity-D705CXng.d.cts} +324 -2
- package/dist/{activity-MBOYLBjR.d.ts → activity-D705CXng.d.ts} +324 -2
- package/dist/ai-check.cjs +174 -12
- package/dist/ai-check.cjs.map +1 -1
- package/dist/ai-check.d.cts +15 -3
- package/dist/ai-check.d.ts +15 -3
- package/dist/ai-check.js +166 -4
- package/dist/ai-check.js.map +1 -1
- package/dist/{chunk-TJJFKKH2.cjs → chunk-46BXIGLG.cjs} +4 -4
- package/dist/{chunk-TJJFKKH2.cjs.map → chunk-46BXIGLG.cjs.map} +1 -1
- package/dist/{chunk-VSH7ECQL.cjs → chunk-4YANYTFN.cjs} +43 -43
- package/dist/{chunk-VSH7ECQL.cjs.map → chunk-4YANYTFN.cjs.map} +1 -1
- package/dist/chunk-7KOWU4RO.js +2207 -0
- package/dist/chunk-7KOWU4RO.js.map +1 -0
- package/dist/{chunk-DSDXL3JO.cjs → chunk-7THXD7AN.cjs} +657 -197
- package/dist/chunk-7THXD7AN.cjs.map +1 -0
- package/dist/chunk-CYCTIXUN.cjs +118 -0
- package/dist/chunk-CYCTIXUN.cjs.map +1 -0
- package/dist/chunk-CYPA5BW5.js +118 -0
- package/dist/chunk-CYPA5BW5.js.map +1 -0
- package/dist/{chunk-SJVRUPJT.js → chunk-JTNDIBZO.js} +2 -2
- package/dist/{chunk-XS6U2ARZ.js → chunk-MV2N2WNS.js} +6 -112
- package/dist/chunk-MV2N2WNS.js.map +1 -0
- package/dist/chunk-MXI26H62.cjs +2207 -0
- package/dist/chunk-MXI26H62.cjs.map +1 -0
- package/dist/{chunk-FBKTION6.js → chunk-QS7B42ZR.js} +640 -180
- package/dist/chunk-QS7B42ZR.js.map +1 -0
- package/dist/{chunk-IARLJ5MW.js → chunk-QX2ERL72.js} +2 -2
- package/dist/{chunk-2JT2WUQ2.cjs → chunk-ZWXQSKCX.cjs} +14 -120
- package/dist/chunk-ZWXQSKCX.cjs.map +1 -0
- package/dist/{index-DlcqkM_L.d.ts → index-BChYtrEe.d.ts} +1 -1
- package/dist/{index-DlCW0XaQ.d.cts → index-VXt_v3YI.d.cts} +1 -1
- package/dist/index.cjs +68 -593
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +245 -60
- package/dist/index.d.ts +245 -60
- package/dist/index.js +68 -593
- package/dist/index.js.map +1 -1
- package/dist/schemas.cjs +5 -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 +5 -3
- package/dist/scoring.cjs +3 -3
- package/dist/scoring.d.cts +2 -2
- package/dist/scoring.d.ts +2 -2
- package/dist/scoring.js +2 -2
- 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/dist/chunk-2JT2WUQ2.cjs.map +0 -1
- package/dist/chunk-DSDXL3JO.cjs.map +0 -1
- package/dist/chunk-FBKTION6.js.map +0 -1
- package/dist/chunk-LDFQUZII.cjs +0 -950
- package/dist/chunk-LDFQUZII.cjs.map +0 -1
- package/dist/chunk-NQHETFDA.js +0 -950
- package/dist/chunk-NQHETFDA.js.map +0 -1
- package/dist/chunk-XS6U2ARZ.js.map +0 -1
- /package/dist/{chunk-SJVRUPJT.js.map → chunk-JTNDIBZO.js.map} +0 -0
- /package/dist/{chunk-IARLJ5MW.js.map → chunk-QX2ERL72.js.map} +0 -0
|
@@ -63,6 +63,81 @@ declare function levenshteinDistance(a: string, b: string, max: number): number;
|
|
|
63
63
|
*/
|
|
64
64
|
declare function matchText(input: string, accepted: string | readonly string[], policy?: TextMatchPolicy): TextMatchResult;
|
|
65
65
|
|
|
66
|
+
/**
|
|
67
|
+
* Why a draft is not an activity yet.
|
|
68
|
+
*
|
|
69
|
+
* - `incomplete` — something the author has not written yet: an empty title, an
|
|
70
|
+
* option with no text, no option marked correct. The fix is to add it.
|
|
71
|
+
* - `invalid` — something is wrong in a way that writing more cannot fix: two
|
|
72
|
+
* correct options on a single-selection question, a maximum word count below
|
|
73
|
+
* the minimum, a media address with a scheme the SDK refuses, a `null` in an
|
|
74
|
+
* optional field that accepts none. The fix is to change it.
|
|
75
|
+
*/
|
|
76
|
+
type DraftSeverity = 'incomplete' | 'invalid';
|
|
77
|
+
/**
|
|
78
|
+
* One problem with a draft. A {@link ValidationError} with a severity, so code
|
|
79
|
+
* that already lists `validateActivity` errors can list these unchanged.
|
|
80
|
+
*/
|
|
81
|
+
interface DraftIssue extends ValidationError {
|
|
82
|
+
severity: DraftSeverity;
|
|
83
|
+
}
|
|
84
|
+
/** A draft that is a valid activity and has nothing left to write. */
|
|
85
|
+
interface DraftComplete<T> {
|
|
86
|
+
status: 'complete';
|
|
87
|
+
/** The validated activity, exactly as `validateActivity` returns it. */
|
|
88
|
+
data: T;
|
|
89
|
+
/** Always empty. Present so that every result can be read the same way. */
|
|
90
|
+
issues: DraftIssue[];
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* A draft that is not an activity yet: `invalid` when any issue is invalid,
|
|
94
|
+
* otherwise `incomplete`. It carries no `data`, because nothing here may be
|
|
95
|
+
* stored or rendered as an activity.
|
|
96
|
+
*/
|
|
97
|
+
interface DraftNotComplete {
|
|
98
|
+
status: 'incomplete' | 'invalid';
|
|
99
|
+
issues: DraftIssue[];
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* The result of `validateDraft`.
|
|
103
|
+
*
|
|
104
|
+
* There is deliberately no `success` boolean. A boolean has to call an
|
|
105
|
+
* incomplete draft either a success, which lets a half-written answer key
|
|
106
|
+
* through anything that checks it, or a failure, which is the conflation this
|
|
107
|
+
* result exists to remove. Branch on `status`.
|
|
108
|
+
*/
|
|
109
|
+
type DraftValidationResult<T> = DraftComplete<T> | DraftNotComplete;
|
|
110
|
+
/**
|
|
111
|
+
* How much a finding of the item critic matters. Neither makes a draft
|
|
112
|
+
* incomplete or invalid: an item with findings is a valid item, stored and
|
|
113
|
+
* served like any other, and whether to change it is the author's call.
|
|
114
|
+
*
|
|
115
|
+
* - `warning` — a known flaw a learner can exploit or trip over: the right
|
|
116
|
+
* option is the longest, an answer printed in the passage, a hint that gives
|
|
117
|
+
* the answer away, two options that read the same.
|
|
118
|
+
* - `advice` — an item-writing guideline the item departs from: "all of the
|
|
119
|
+
* above", two accepted answers the matcher already treats as one.
|
|
120
|
+
*/
|
|
121
|
+
type ItemFindingSeverity = 'warning' | 'advice';
|
|
122
|
+
/**
|
|
123
|
+
* One thing the item critic found. A {@link ValidationError} with a severity,
|
|
124
|
+
* as a {@link DraftIssue} is, so an editor lists both in one list — sorted by
|
|
125
|
+
* severity: `invalid`, `incomplete`, `warning`, `advice`.
|
|
126
|
+
*/
|
|
127
|
+
interface ItemFinding extends ValidationError {
|
|
128
|
+
severity: ItemFindingSeverity;
|
|
129
|
+
}
|
|
130
|
+
/** What `createDraft` hands an activity type's `authoring.createDraft`. */
|
|
131
|
+
interface DraftContext {
|
|
132
|
+
/**
|
|
133
|
+
* Returns a new id on every call. The SDK invents none: an id made here would
|
|
134
|
+
* need `crypto`, which plain-http origins do not have, or would be positional —
|
|
135
|
+
* and option ids reach the learner, so ids like `"a"` and `"b"` would tell them
|
|
136
|
+
* which option was written first.
|
|
137
|
+
*/
|
|
138
|
+
newId: () => string;
|
|
139
|
+
}
|
|
140
|
+
|
|
66
141
|
/**
|
|
67
142
|
* The return trip for deferred grading.
|
|
68
143
|
*
|
|
@@ -413,8 +488,10 @@ interface AiTextResult {
|
|
|
413
488
|
* corrects words the learner did not write: a correction whose quote is not in
|
|
414
489
|
* the draft, or not where it claims to be. `contradicts-marks` is coaching on
|
|
415
490
|
* a word the engine did not mark, or on a sound it did not report.
|
|
491
|
+
* `contradicts-item` is a critique of an item that points at a field the item
|
|
492
|
+
* does not have, or quotes words that field does not contain.
|
|
416
493
|
*/
|
|
417
|
-
type AiRefusal = 'malformed' | 'empty' | 'too-long' | 'contradicts-grade' | 'reveals-answer' | 'misquotes-answer' | 'contradicts-marks';
|
|
494
|
+
type AiRefusal = 'malformed' | 'empty' | 'too-long' | 'contradicts-grade' | 'reveals-answer' | 'misquotes-answer' | 'contradicts-marks' | 'contradicts-item';
|
|
418
495
|
/** One criterion of a written response's rubric, as the author wrote it. */
|
|
419
496
|
interface AiRubricCriterionFact {
|
|
420
497
|
name: string;
|
|
@@ -624,6 +701,251 @@ interface AiCoaching {
|
|
|
624
701
|
provenance?: AiProvenance;
|
|
625
702
|
usage?: GraderUsage;
|
|
626
703
|
}
|
|
704
|
+
/**
|
|
705
|
+
* What a model may find wrong with an item, in the SDK's words:
|
|
706
|
+
*
|
|
707
|
+
* - `ambiguous` — a stem, passage or blank that reads two ways;
|
|
708
|
+
* - `second-answer` — a distractor that is defensibly right, or an answer the
|
|
709
|
+
* key does not accept;
|
|
710
|
+
* - `wrong-key` — the answer marked right is not;
|
|
711
|
+
* - `implausible-distractor` — an option nobody who read the question would pick;
|
|
712
|
+
* - `cue` — grammar or wording that points at the answer;
|
|
713
|
+
* - `level` — language above or below the level the item is for;
|
|
714
|
+
* - `language` — a mistake in the item's own language;
|
|
715
|
+
* - `sensitivity` — content a learner may find loaded or unfamiliar for
|
|
716
|
+
* reasons that have nothing to do with what is assessed;
|
|
717
|
+
* - `other`.
|
|
718
|
+
*/
|
|
719
|
+
type AiCritiqueKind = 'ambiguous' | 'second-answer' | 'wrong-key' | 'implausible-distractor' | 'cue' | 'level' | 'language' | 'sensitivity' | 'other';
|
|
720
|
+
/** One field of an item a critique may point at, and what it says. */
|
|
721
|
+
interface AiCritiqueField {
|
|
722
|
+
path: string[];
|
|
723
|
+
text: string;
|
|
724
|
+
}
|
|
725
|
+
/**
|
|
726
|
+
* What a host's model is given to review an item. Built by
|
|
727
|
+
* `aiCritiqueRequest`; for an author, never a learner, so it carries the
|
|
728
|
+
* answer key.
|
|
729
|
+
*/
|
|
730
|
+
interface AiCritiqueRequest {
|
|
731
|
+
feature: 'item-critique';
|
|
732
|
+
facts: {
|
|
733
|
+
activityType: string;
|
|
734
|
+
/** The item as the author wrote it, answer key included. */
|
|
735
|
+
item: Readonly<Record<string, unknown>>;
|
|
736
|
+
/** Every text field a finding may point at, with its text: the only paths a finding may name. */
|
|
737
|
+
fields: AiCritiqueField[];
|
|
738
|
+
/**
|
|
739
|
+
* What the SDK's own critic already found, so a model does not say it
|
|
740
|
+
* again: code, path and message.
|
|
741
|
+
*/
|
|
742
|
+
findings: {
|
|
743
|
+
path: string[];
|
|
744
|
+
code: string;
|
|
745
|
+
message: string;
|
|
746
|
+
}[];
|
|
747
|
+
/** The level the item is for, as the host names it (`A2`, `B1`), when it gave one. */
|
|
748
|
+
level?: string;
|
|
749
|
+
};
|
|
750
|
+
/** The language to write findings in: the author's. */
|
|
751
|
+
authorLocale?: string;
|
|
752
|
+
}
|
|
753
|
+
/** One finding as a host's port returns it. */
|
|
754
|
+
interface AiCritiqueFindingResult {
|
|
755
|
+
/** A path from `facts.fields`, exactly. */
|
|
756
|
+
path: (string | number)[];
|
|
757
|
+
kind: AiCritiqueKind;
|
|
758
|
+
/** What is wrong, and what to do, addressed to the author. */
|
|
759
|
+
message: string;
|
|
760
|
+
/** Words copied from that field, when the finding is about some of them. */
|
|
761
|
+
quote?: string;
|
|
762
|
+
}
|
|
763
|
+
/** What a host's critique port returns. */
|
|
764
|
+
interface AiCritiqueResult {
|
|
765
|
+
findings: AiCritiqueFindingResult[];
|
|
766
|
+
provenance?: AiProvenance;
|
|
767
|
+
usage?: GraderUsage;
|
|
768
|
+
}
|
|
769
|
+
/**
|
|
770
|
+
* A model's finding, as the SDK accepted it: an `ItemFinding` — always
|
|
771
|
+
* `advice`, since it is a model's opinion — whose code is `ai_` and the kind
|
|
772
|
+
* (`ai_second_answer`), with the quote it pointed at.
|
|
773
|
+
*/
|
|
774
|
+
interface AiCritiqueFinding extends ItemFinding {
|
|
775
|
+
severity: 'advice';
|
|
776
|
+
quote?: string;
|
|
777
|
+
}
|
|
778
|
+
/** A critique, as the SDK accepted it. */
|
|
779
|
+
interface AiCritique {
|
|
780
|
+
findings: AiCritiqueFinding[];
|
|
781
|
+
provenance?: AiProvenance;
|
|
782
|
+
usage?: GraderUsage;
|
|
783
|
+
}
|
|
784
|
+
/**
|
|
785
|
+
* The types a model can draft from a source. An interactive video takes the
|
|
786
|
+
* first five; a written response is for a quiz of its own.
|
|
787
|
+
*/
|
|
788
|
+
type AiDraftType = 'multiple-choice' | 'fill-in-the-blanks' | 'gap-select' | 'dictation' | 'read-aloud' | 'written-response';
|
|
789
|
+
/**
|
|
790
|
+
* What drafts are written from: a passage or a script as text — an item
|
|
791
|
+
* group's `stimulus.transcript` is one — or a video's captions, each with its
|
|
792
|
+
* start and end in seconds, so a question can be placed where its caption ends.
|
|
793
|
+
*/
|
|
794
|
+
type AiDraftSource = {
|
|
795
|
+
kind: 'passage' | 'transcript';
|
|
796
|
+
text: string;
|
|
797
|
+
} | {
|
|
798
|
+
kind: 'captions';
|
|
799
|
+
cues: {
|
|
800
|
+
start: number;
|
|
801
|
+
end: number;
|
|
802
|
+
text: string;
|
|
803
|
+
}[];
|
|
804
|
+
};
|
|
805
|
+
/** A caption as a model is given it: numbered, so a draft can name the one it is about. */
|
|
806
|
+
interface AiDraftCaption {
|
|
807
|
+
index: number;
|
|
808
|
+
start: number;
|
|
809
|
+
end: number;
|
|
810
|
+
text: string;
|
|
811
|
+
}
|
|
812
|
+
/** One problem with a draft, as a model is told it on a repair. */
|
|
813
|
+
interface AiDraftProblem {
|
|
814
|
+
path: string[];
|
|
815
|
+
code: string;
|
|
816
|
+
message: string;
|
|
817
|
+
severity: 'incomplete' | 'invalid' | 'warning';
|
|
818
|
+
}
|
|
819
|
+
/**
|
|
820
|
+
* Fields of the host's for every draft of a type — settings a model never
|
|
821
|
+
* writes: a read-aloud's `recording`, `scoring` and region-tagged `locale`, a
|
|
822
|
+
* question's `shuffle` or `scoringStrategy`. They are put on each draft of
|
|
823
|
+
* that type before it is checked, and never override what the model wrote, nor
|
|
824
|
+
* the draft's `id`, `type` or `schemaVersion`.
|
|
825
|
+
*/
|
|
826
|
+
type AiDraftSettings = Partial<Record<AiDraftType, Readonly<Record<string, unknown>>>>;
|
|
827
|
+
/**
|
|
828
|
+
* What a host's model is given to draft items. Built by `aiDraftsRequest`, and
|
|
829
|
+
* by `aiDraftsRepairRequest` for a second pass over the drafts that came back
|
|
830
|
+
* unfinished or flawed in ways the model can fix.
|
|
831
|
+
*/
|
|
832
|
+
interface AiDraftsRequest {
|
|
833
|
+
feature: 'draft-generation';
|
|
834
|
+
facts: {
|
|
835
|
+
/**
|
|
836
|
+
* The types the author chose, in the author's order: the ones to lean on
|
|
837
|
+
* first, and the order questions are shown in where several share a moment
|
|
838
|
+
* of a video — or share the end of one.
|
|
839
|
+
*/
|
|
840
|
+
activityTypes: AiDraftType[];
|
|
841
|
+
source: {
|
|
842
|
+
kind: 'passage' | 'transcript';
|
|
843
|
+
text: string;
|
|
844
|
+
} | {
|
|
845
|
+
kind: 'captions';
|
|
846
|
+
captions: AiDraftCaption[];
|
|
847
|
+
};
|
|
848
|
+
/** How many drafts to write, at most. Absent: as many as the source is worth, up to `AI_DRAFTS_MAX_COUNT`. */
|
|
849
|
+
count?: number;
|
|
850
|
+
/** The language the items are written in. */
|
|
851
|
+
locale?: string;
|
|
852
|
+
/** The level they are for, as the host names it (`A2`, `B1`). */
|
|
853
|
+
level?: string;
|
|
854
|
+
/** What the author asked for, in their words. */
|
|
855
|
+
instructions?: string;
|
|
856
|
+
};
|
|
857
|
+
/**
|
|
858
|
+
* The JSON Schema of a reply — `{ drafts: [...] }` — for a model's structured
|
|
859
|
+
* output, or its prompt. Plain JSON Schema a model can follow: types, required
|
|
860
|
+
* fields, enums and descriptions, nothing a provider's structured output
|
|
861
|
+
* commonly refuses. Each draft names its `type`, and fills the fields that
|
|
862
|
+
* type uses.
|
|
863
|
+
*/
|
|
864
|
+
shape: Readonly<Record<string, unknown>>;
|
|
865
|
+
/** The host's settings for every draft of a type. Not for the model; `checkAiDrafts` applies them. */
|
|
866
|
+
settings?: AiDraftSettings;
|
|
867
|
+
/**
|
|
868
|
+
* On a repair: the drafts to fix, each as the model wrote it, with what is
|
|
869
|
+
* wrong with it that the model can fix. The reply holds one draft for each,
|
|
870
|
+
* in this order.
|
|
871
|
+
*/
|
|
872
|
+
repair?: {
|
|
873
|
+
index: number;
|
|
874
|
+
draft: Readonly<Record<string, unknown>>;
|
|
875
|
+
problems: AiDraftProblem[];
|
|
876
|
+
}[];
|
|
877
|
+
}
|
|
878
|
+
/** A draft as the SDK made it from a reply: ids minted, settings applied, checked, and critiqued. */
|
|
879
|
+
interface AiGeneratedDraft {
|
|
880
|
+
/** Its position in the first reply; a repaired draft keeps the position it replaces. */
|
|
881
|
+
index: number;
|
|
882
|
+
type: AiDraftType;
|
|
883
|
+
/** The draft, an activity of its type once `validation` says `complete`. */
|
|
884
|
+
draft: Record<string, unknown>;
|
|
885
|
+
/** What `validateDraft` says of it. `complete` means valid — never approved. */
|
|
886
|
+
validation: DraftValidationResult<unknown>;
|
|
887
|
+
/** What the item critic says of it. */
|
|
888
|
+
findings: ItemFinding[];
|
|
889
|
+
/**
|
|
890
|
+
* From captions: where the question goes, in seconds — the end of the
|
|
891
|
+
* caption it named. Absent when it named no caption the source has: it
|
|
892
|
+
* belongs at the end of the video.
|
|
893
|
+
*/
|
|
894
|
+
at?: number;
|
|
895
|
+
/**
|
|
896
|
+
* A dictation from captions whose transcript is words of the caption it
|
|
897
|
+
* named: that caption's start and end, in seconds — the stretch of the
|
|
898
|
+
* video that says it, for a host that cuts the recording from the video.
|
|
899
|
+
*/
|
|
900
|
+
clip?: {
|
|
901
|
+
start: number;
|
|
902
|
+
end: number;
|
|
903
|
+
};
|
|
904
|
+
/** The draft as the model wrote it, for a repair. */
|
|
905
|
+
generated: Readonly<Record<string, unknown>>;
|
|
906
|
+
}
|
|
907
|
+
/** What `checkAiDrafts` makes of a reply. */
|
|
908
|
+
interface AiDrafts {
|
|
909
|
+
/** The drafts in the author's order of types, and in the order the model wrote them within a type. */
|
|
910
|
+
drafts: AiGeneratedDraft[];
|
|
911
|
+
/** What only the set shows: every right option in one place (`set_key_position_same`). */
|
|
912
|
+
findings: ItemFinding[];
|
|
913
|
+
provenance?: AiProvenance;
|
|
914
|
+
usage?: GraderUsage;
|
|
915
|
+
}
|
|
916
|
+
/** One call `generateDrafts` made: what it cost, or why it went no further. */
|
|
917
|
+
type AiDraftsCall = {
|
|
918
|
+
ok: true;
|
|
919
|
+
provenance?: AiProvenance;
|
|
920
|
+
usage?: GraderUsage;
|
|
921
|
+
} | {
|
|
922
|
+
ok: false;
|
|
923
|
+
refusal?: AiRefusal;
|
|
924
|
+
error?: string;
|
|
925
|
+
};
|
|
926
|
+
/** What `generateDrafts` came to. */
|
|
927
|
+
interface AiDraftsRun {
|
|
928
|
+
/** The drafts, in the author's order of types, each the best pass of it. */
|
|
929
|
+
drafts: AiGeneratedDraft[];
|
|
930
|
+
/** What only the set shows, over the drafts as they ended. */
|
|
931
|
+
findings: ItemFinding[];
|
|
932
|
+
/** Every call, in order: the first, then each repair. */
|
|
933
|
+
calls: AiDraftsCall[];
|
|
934
|
+
}
|
|
935
|
+
/**
|
|
936
|
+
* An interactive video made of drafts: an item group whose stimulus is the
|
|
937
|
+
* video and whose timeline opens a quiz at each moment a draft was placed —
|
|
938
|
+
* and one at the end for every draft placed nowhere. A draft, like every
|
|
939
|
+
* draft: checked, critiqued, and approved by a person, never by the SDK.
|
|
940
|
+
*/
|
|
941
|
+
interface AiVideoDraft {
|
|
942
|
+
/** The item group, an interactive video once `validation` says `complete`. */
|
|
943
|
+
group: Record<string, unknown>;
|
|
944
|
+
/** What `validateItemGroupDraft` says of it. */
|
|
945
|
+
validation: DraftValidationResult<unknown>;
|
|
946
|
+
/** What `critiqueItemGroupDraft` says of it. */
|
|
947
|
+
findings: ItemFinding[];
|
|
948
|
+
}
|
|
627
949
|
|
|
628
950
|
/**
|
|
629
951
|
* An xAPI Activity object — the thing a statement is about.
|
|
@@ -1837,4 +2159,4 @@ interface InteractionEvent {
|
|
|
1837
2159
|
payload: Record<string, unknown>;
|
|
1838
2160
|
}
|
|
1839
2161
|
|
|
1840
|
-
export { type
|
|
2162
|
+
export { type AiWritingCorrection as $, type ActivityAiPermissions as A, type AiDrafts as B, type AiDraftsCall as C, type AiDraftsRequest as D, type AiDraftsRun as E, type AiExplanationRequest as F, type AiFeature as G, type AiFillInTheBlanksFacts as H, type AiGapFact as I, type AiGapSelectFacts as J, type AiGeneratedDraft as K, type AiGrade as L, type AiHintRequest as M, type AiItemFacts as N, type AiMultipleChoiceFacts as O, type AiOptionFact as P, type AiProvenance as Q, type AiReadingFacts as R, type AiReadingWordFact as S, type AiRefusal as T, type AiRubricCriterionFact as U, type AiSoundFact as V, type AiSupportedActivityType as W, type AiTextResult as X, type AiVerdict as Y, type AiVideoDraft as Z, type AiWordFact as _, type ActivityData as a, type WrittenResponseRubricCriterion as a$, type AiWritingFacts as a0, type AiWritingFeedback as a1, type AiWritingFeedbackRequest as a2, type AiWritingFeedbackResult as a3, type BlankConfig as a4, type CriterionScore as a5, type DeferredReason as a6, type DeferredScoringPartial as a7, type DictationData as a8, type DictationEquivalence as a9, type ItemOutcome as aA, type LearnerResponse as aB, type LearnerResponseMap as aC, type MediaPlaybackPolicy as aD, type MediaTrack as aE, type MultipleChoiceData as aF, type MultipleChoiceLearnerResponse as aG, type MultipleChoiceOption as aH, type MultipleChoiceOptionMedia as aI, type NativeControlHint as aJ, type ReadAloudData as aK, type ReadAloudDimension as aL, type ReadAloudDimensionWeight as aM, type ReadAloudLearnerResponse as aN, type ReadAloudSlowMedia as aO, type RecordingBounds as aP, type RecordingRef as aQ, type ScoringDetail as aR, type ScoringOutcome as aS, type ScoringResult as aT, type TextMatchPolicy as aU, type TextMatchResult as aV, type ValidationError as aW, type ValidationResult as aX, type WrittenResponseData as aY, type WrittenResponseLearnerResponse as aZ, type WrittenResponseRubric as a_, type DictationLearnerResponse as aa, type DictationSlowMedia as ab, type DictationTolerance as ac, type DraftComplete as ad, type DraftContext as ae, type DraftIssue as af, type DraftNotComplete as ag, type DraftSeverity as ah, type DraftValidationResult as ai, type FillInTheBlanksData as aj, type FillInTheBlanksLearnerResponse as ak, type GapSelectBank as al, type GapSelectChoice as am, type GapSelectData as an, type GapSelectGap as ao, type GapSelectLearnerResponse as ap, type GradeRecord as aq, type Grader as ar, type GraderKind as as, type GraderUsage as at, type GradingState as au, type InlineCorrection as av, type InteractionEvent as aw, type InteractionKind as ax, type ItemFinding as ay, type ItemFindingSeverity as az, type ActivityDataMap as b, type XAPIActor as b0, type XAPIConfig as b1, type XAPIContext as b2, type XAPIContextActivities as b3, type XAPIError as b4, type XAPIObject as b5, type XAPIResult as b6, type XAPIScore as b7, type XAPIStatement as b8, type XAPIVerbObject as b9, levenshteinDistance as ba, matchText as bb, type ActivityFeedback as c, type ActivityMedia as d, type ActivityResult as e, type ActivityType as f, type AiActivityInput as g, type AiBlankFact as h, type AiCoaching as i, type AiCoachingRequest as j, type AiCoachingResult as k, type AiCoachingWord as l, type AiCriterionJudgement as m, type AiCritique as n, type AiCritiqueField as o, type AiCritiqueFinding as p, type AiCritiqueFindingResult as q, type AiCritiqueKind as r, type AiCritiqueRequest as s, type AiCritiqueResult as t, type AiDictationFacts as u, type AiDraftCaption as v, type AiDraftProblem as w, type AiDraftSettings as x, type AiDraftSource as y, type AiDraftType as z };
|
package/dist/ai-check.cjs
CHANGED
|
@@ -7,11 +7,16 @@
|
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
|
|
10
|
-
var _chunkLDFQUZIIcjs = require('./chunk-LDFQUZII.cjs');
|
|
11
10
|
|
|
12
11
|
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
var _chunkMXI26H62cjs = require('./chunk-MXI26H62.cjs');
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
var _chunk4YANYTFNcjs = require('./chunk-4YANYTFN.cjs');
|
|
18
|
+
require('./chunk-ZWXQSKCX.cjs');
|
|
19
|
+
require('./chunk-7THXD7AN.cjs');
|
|
15
20
|
|
|
16
21
|
// src/ai-check/cases.ts
|
|
17
22
|
var capital = {
|
|
@@ -171,22 +176,83 @@ var drafted = (text) => ({
|
|
|
171
176
|
text,
|
|
172
177
|
wordCount: 0
|
|
173
178
|
});
|
|
179
|
+
var fruit = {
|
|
180
|
+
schemaVersion: "1.0",
|
|
181
|
+
type: "multiple-choice",
|
|
182
|
+
id: "ai-check-fruit",
|
|
183
|
+
title: "Food words",
|
|
184
|
+
question: "Which of these is a fruit?",
|
|
185
|
+
mode: "single",
|
|
186
|
+
scoringStrategy: "all-or-nothing",
|
|
187
|
+
options: [
|
|
188
|
+
{ id: "f1", text: "Apple", isCorrect: true },
|
|
189
|
+
{ id: "f2", text: "Carrot", isCorrect: false },
|
|
190
|
+
{ id: "f3", text: "Tomato", isCorrect: false }
|
|
191
|
+
]
|
|
192
|
+
};
|
|
193
|
+
var tooHard = {
|
|
194
|
+
schemaVersion: "1.0",
|
|
195
|
+
type: "written-response",
|
|
196
|
+
id: "ai-check-too-hard",
|
|
197
|
+
title: "Cities",
|
|
198
|
+
prompt: "Discuss the socioeconomic ramifications of rapid urbanisation in developing economies.",
|
|
199
|
+
minWords: 40,
|
|
200
|
+
maxWords: 80
|
|
201
|
+
};
|
|
202
|
+
var cleanCloze = {
|
|
203
|
+
schemaVersion: "1.0",
|
|
204
|
+
type: "fill-in-the-blanks",
|
|
205
|
+
id: "ai-check-clean-cloze",
|
|
206
|
+
title: "Daily routine",
|
|
207
|
+
passage: "Every morning I {{1}} up at seven and {{2}} a shower.",
|
|
208
|
+
blanks: [
|
|
209
|
+
{ id: "1", acceptedAnswers: ["get", "wake"] },
|
|
210
|
+
{ id: "2", acceptedAnswers: ["take", "have"] }
|
|
211
|
+
],
|
|
212
|
+
scoringStrategy: "partial"
|
|
213
|
+
};
|
|
214
|
+
function critiqueCase(id, about, type, draft, extra = {}) {
|
|
215
|
+
const request = _chunkMXI26H62cjs.aiCritiqueRequest.call(void 0, { type, draft, ...extra });
|
|
216
|
+
if (request === null) {
|
|
217
|
+
throw new Error(`ai-check: no critique request for "${id}"`);
|
|
218
|
+
}
|
|
219
|
+
return { id, feature: "item-critique", about, request };
|
|
220
|
+
}
|
|
221
|
+
var MARKET = "Maria lives in Seville, in the south of Spain. Every Saturday she walks to the market with her grandmother. They buy oranges, fresh bread and a little cheese. On the way home they stop at a caf\xE9, where Maria drinks hot chocolate and her grandmother reads the newspaper.";
|
|
222
|
+
var MARKET_CAPTIONS = [
|
|
223
|
+
{ start: 0, end: 4.2, text: "Maria lives in Seville, in the south of Spain." },
|
|
224
|
+
{ start: 4.2, end: 8.9, text: "Every Saturday she walks to the market with her grandmother." },
|
|
225
|
+
{ start: 8.9, end: 12.6, text: "They buy oranges, fresh bread and a little cheese." },
|
|
226
|
+
{ start: 12.6, end: 18.1, text: "On the way home they stop at a caf\xE9." }
|
|
227
|
+
];
|
|
228
|
+
function draftsCase(id, about, types, source, extra = {}) {
|
|
229
|
+
const request = _chunkMXI26H62cjs.aiDraftsRequest.call(void 0, { types, source, ...extra });
|
|
230
|
+
if (request === null) {
|
|
231
|
+
throw new Error(`ai-check: no drafts request for "${id}"`);
|
|
232
|
+
}
|
|
233
|
+
return { id, feature: "draft-generation", about, request };
|
|
234
|
+
}
|
|
235
|
+
var READ_ALOUD_SETTINGS = {
|
|
236
|
+
locale: "en-US",
|
|
237
|
+
recording: { maxSeconds: 20 },
|
|
238
|
+
scoring: { dimensions: [{ name: "accuracy", weight: 1 }] }
|
|
239
|
+
};
|
|
174
240
|
function explanationCase(id, about, data, response) {
|
|
175
|
-
const request =
|
|
241
|
+
const request = _chunkMXI26H62cjs.aiExplanationRequest.call(void 0, { data, response });
|
|
176
242
|
if (request === null) {
|
|
177
243
|
throw new Error(`ai-check: no explanation request for "${id}"`);
|
|
178
244
|
}
|
|
179
245
|
return { id, feature: "explanation", about, request };
|
|
180
246
|
}
|
|
181
247
|
function hintCase(id, about, data, response, previousHints = []) {
|
|
182
|
-
const request =
|
|
248
|
+
const request = _chunkMXI26H62cjs.aiHintRequest.call(void 0, { data, response, previousHints });
|
|
183
249
|
if (request === null) {
|
|
184
250
|
throw new Error(`ai-check: no hint request for "${id}"`);
|
|
185
251
|
}
|
|
186
252
|
return { id, feature: "hint", about, request };
|
|
187
253
|
}
|
|
188
254
|
function writingCase(id, about, text, previousFeedback = [], learnerLocale) {
|
|
189
|
-
const request =
|
|
255
|
+
const request = _chunkMXI26H62cjs.aiWritingFeedbackRequest.call(void 0, {
|
|
190
256
|
data: weekend,
|
|
191
257
|
response: drafted(text),
|
|
192
258
|
previousFeedback,
|
|
@@ -199,7 +265,7 @@ function writingCase(id, about, text, previousFeedback = [], learnerLocale) {
|
|
|
199
265
|
}
|
|
200
266
|
function coachingCase(id, about, marks, learnerLocale) {
|
|
201
267
|
const assessment = _nullishCoalesce(marks.assessment, () => ( forecast(true)));
|
|
202
|
-
const grade =
|
|
268
|
+
const grade = _chunk4YANYTFNcjs.gradeReadAloud.call(void 0,
|
|
203
269
|
weather,
|
|
204
270
|
{ type: "read-aloud", recording: { key: "ai-check-take", mimeType: "audio/wav" } },
|
|
205
271
|
assessment,
|
|
@@ -211,7 +277,7 @@ function coachingCase(id, about, marks, learnerLocale) {
|
|
|
211
277
|
if ("unscorable" in grade) {
|
|
212
278
|
throw new Error(`ai-check: the reading for "${id}" did not grade: ${grade.reason}`);
|
|
213
279
|
}
|
|
214
|
-
const request =
|
|
280
|
+
const request = _chunkMXI26H62cjs.aiCoachingRequest.call(void 0, {
|
|
215
281
|
data: weather,
|
|
216
282
|
...marks.fromGradeOnly === true ? {} : { assessment },
|
|
217
283
|
grade,
|
|
@@ -328,6 +394,83 @@ function aiCheckCases() {
|
|
|
328
394
|
"The same slips, with coaching asked for in Spanish",
|
|
329
395
|
{ assessment: forecast(true) },
|
|
330
396
|
"es"
|
|
397
|
+
),
|
|
398
|
+
critiqueCase(
|
|
399
|
+
"critique-second-answer",
|
|
400
|
+
'A question whose distractor "Tomato" is botanically a fruit',
|
|
401
|
+
"multiple-choice",
|
|
402
|
+
fruit,
|
|
403
|
+
{ level: "A1" }
|
|
404
|
+
),
|
|
405
|
+
critiqueCase(
|
|
406
|
+
"critique-level",
|
|
407
|
+
"A writing task far above the A1 level it is set for",
|
|
408
|
+
"written-response",
|
|
409
|
+
tooHard,
|
|
410
|
+
{ level: "A1" }
|
|
411
|
+
),
|
|
412
|
+
critiqueCase(
|
|
413
|
+
"critique-clean",
|
|
414
|
+
"A cloze with nothing wrong in it",
|
|
415
|
+
"fill-in-the-blanks",
|
|
416
|
+
cleanCloze,
|
|
417
|
+
{
|
|
418
|
+
level: "A2"
|
|
419
|
+
}
|
|
420
|
+
),
|
|
421
|
+
critiqueCase(
|
|
422
|
+
"critique-in-spanish",
|
|
423
|
+
"The fruit question, reviewed in Spanish for its author",
|
|
424
|
+
"multiple-choice",
|
|
425
|
+
fruit,
|
|
426
|
+
{ level: "A1", authorLocale: "es" }
|
|
427
|
+
),
|
|
428
|
+
draftsCase(
|
|
429
|
+
"drafts-questions",
|
|
430
|
+
"Four multiple-choice questions on a short reading",
|
|
431
|
+
["multiple-choice"],
|
|
432
|
+
{ kind: "passage", text: MARKET },
|
|
433
|
+
{ count: 4, level: "A2", locale: "en" }
|
|
434
|
+
),
|
|
435
|
+
draftsCase(
|
|
436
|
+
"drafts-cloze",
|
|
437
|
+
"A cloze on the same reading, from its transcript",
|
|
438
|
+
["fill-in-the-blanks"],
|
|
439
|
+
{ kind: "transcript", text: MARKET },
|
|
440
|
+
{ count: 1, level: "A2", locale: "en", instructions: "Blank the verbs." }
|
|
441
|
+
),
|
|
442
|
+
draftsCase(
|
|
443
|
+
"drafts-gaps",
|
|
444
|
+
"A choose-the-word item on the same reading",
|
|
445
|
+
["gap-select"],
|
|
446
|
+
{ kind: "passage", text: MARKET },
|
|
447
|
+
{ count: 1, level: "A2", locale: "en" }
|
|
448
|
+
),
|
|
449
|
+
draftsCase(
|
|
450
|
+
"drafts-writing",
|
|
451
|
+
"A writing task that follows on from the reading",
|
|
452
|
+
["written-response"],
|
|
453
|
+
{ kind: "passage", text: MARKET },
|
|
454
|
+
{ count: 1, level: "A2", locale: "en" }
|
|
455
|
+
),
|
|
456
|
+
draftsCase(
|
|
457
|
+
"drafts-video",
|
|
458
|
+
"A video quiz from timed captions: multiple choice, then dictation, then read-aloud, as many as the video is worth",
|
|
459
|
+
["multiple-choice", "dictation", "read-aloud"],
|
|
460
|
+
{ kind: "captions", cues: MARKET_CAPTIONS },
|
|
461
|
+
{ level: "A2", locale: "en", settings: { "read-aloud": READ_ALOUD_SETTINGS } }
|
|
462
|
+
),
|
|
463
|
+
draftsCase(
|
|
464
|
+
"drafts-script",
|
|
465
|
+
"The same video from its script alone, with no times, and what the author asked for",
|
|
466
|
+
["multiple-choice", "dictation", "read-aloud"],
|
|
467
|
+
{ kind: "transcript", text: MARKET },
|
|
468
|
+
{
|
|
469
|
+
level: "A2",
|
|
470
|
+
locale: "en",
|
|
471
|
+
instructions: "Focus on what Maria and her grandmother buy.",
|
|
472
|
+
settings: { "read-aloud": READ_ALOUD_SETTINGS }
|
|
473
|
+
}
|
|
331
474
|
)
|
|
332
475
|
];
|
|
333
476
|
}
|
|
@@ -340,7 +483,8 @@ var REFUSALS = [
|
|
|
340
483
|
"contradicts-grade",
|
|
341
484
|
"reveals-answer",
|
|
342
485
|
"misquotes-answer",
|
|
343
|
-
"contradicts-marks"
|
|
486
|
+
"contradicts-marks",
|
|
487
|
+
"contradicts-item"
|
|
344
488
|
];
|
|
345
489
|
function portFor(ports, one) {
|
|
346
490
|
switch (one.request.feature) {
|
|
@@ -350,6 +494,10 @@ function portFor(ports, one) {
|
|
|
350
494
|
return ports.hint;
|
|
351
495
|
case "pronunciation-coaching":
|
|
352
496
|
return ports.pronunciationCoaching;
|
|
497
|
+
case "item-critique":
|
|
498
|
+
return ports.critique;
|
|
499
|
+
case "draft-generation":
|
|
500
|
+
return ports.drafts;
|
|
353
501
|
default:
|
|
354
502
|
return ports.writingFeedback;
|
|
355
503
|
}
|
|
@@ -373,8 +521,22 @@ async function runCase(ports, one) {
|
|
|
373
521
|
};
|
|
374
522
|
}
|
|
375
523
|
const ms = now() - started;
|
|
524
|
+
if (one.request.feature === "draft-generation") {
|
|
525
|
+
let next = 0;
|
|
526
|
+
const checked2 = _chunkMXI26H62cjs.checkAiDrafts.call(void 0, raw, one.request, {
|
|
527
|
+
newId: () => {
|
|
528
|
+
next += 1;
|
|
529
|
+
return `ai-check-${next}`;
|
|
530
|
+
}
|
|
531
|
+
});
|
|
532
|
+
return checked2.ok ? { case: one, ok: true, drafts: checked2.drafts, ms } : refused(one, raw, checked2.refusal, ms);
|
|
533
|
+
}
|
|
534
|
+
if (one.request.feature === "item-critique") {
|
|
535
|
+
const checked2 = _chunkMXI26H62cjs.checkAiCritique.call(void 0, raw, one.request);
|
|
536
|
+
return checked2.ok ? { case: one, ok: true, critique: checked2.critique, ms } : refused(one, raw, checked2.refusal, ms);
|
|
537
|
+
}
|
|
376
538
|
if (one.request.feature === "pronunciation-coaching") {
|
|
377
|
-
const checked2 =
|
|
539
|
+
const checked2 = _chunkMXI26H62cjs.checkAiCoaching.call(void 0, raw, one.request);
|
|
378
540
|
if (checked2.ok) {
|
|
379
541
|
const { text, provenance, usage } = checked2.coaching;
|
|
380
542
|
return {
|
|
@@ -392,7 +554,7 @@ async function runCase(ports, one) {
|
|
|
392
554
|
return refused(one, raw, checked2.refusal, ms);
|
|
393
555
|
}
|
|
394
556
|
if (one.request.feature === "writing-feedback") {
|
|
395
|
-
const checked2 =
|
|
557
|
+
const checked2 = _chunkMXI26H62cjs.checkAiWritingFeedback.call(void 0, raw, one.request);
|
|
396
558
|
if (checked2.ok) {
|
|
397
559
|
const { text, provenance, usage } = checked2.feedback;
|
|
398
560
|
return {
|
|
@@ -409,7 +571,7 @@ async function runCase(ports, one) {
|
|
|
409
571
|
}
|
|
410
572
|
return refused(one, raw, checked2.refusal, ms);
|
|
411
573
|
}
|
|
412
|
-
const checked = one.request.feature === "explanation" ?
|
|
574
|
+
const checked = one.request.feature === "explanation" ? _chunkMXI26H62cjs.checkAiExplanation.call(void 0, raw, one.request) : _chunkMXI26H62cjs.checkAiHint.call(void 0, raw, one.request);
|
|
413
575
|
if (checked.ok) {
|
|
414
576
|
return { case: one, ok: true, result: checked.result, ms };
|
|
415
577
|
}
|