@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.
Files changed (64) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/README.md +2 -1
  3. package/dist/{activity-MBOYLBjR.d.cts → activity-D705CXng.d.cts} +324 -2
  4. package/dist/{activity-MBOYLBjR.d.ts → activity-D705CXng.d.ts} +324 -2
  5. package/dist/ai-check.cjs +174 -12
  6. package/dist/ai-check.cjs.map +1 -1
  7. package/dist/ai-check.d.cts +15 -3
  8. package/dist/ai-check.d.ts +15 -3
  9. package/dist/ai-check.js +166 -4
  10. package/dist/ai-check.js.map +1 -1
  11. package/dist/{chunk-TJJFKKH2.cjs → chunk-46BXIGLG.cjs} +4 -4
  12. package/dist/{chunk-TJJFKKH2.cjs.map → chunk-46BXIGLG.cjs.map} +1 -1
  13. package/dist/{chunk-VSH7ECQL.cjs → chunk-4YANYTFN.cjs} +43 -43
  14. package/dist/{chunk-VSH7ECQL.cjs.map → chunk-4YANYTFN.cjs.map} +1 -1
  15. package/dist/chunk-7KOWU4RO.js +2207 -0
  16. package/dist/chunk-7KOWU4RO.js.map +1 -0
  17. package/dist/{chunk-DSDXL3JO.cjs → chunk-7THXD7AN.cjs} +657 -197
  18. package/dist/chunk-7THXD7AN.cjs.map +1 -0
  19. package/dist/chunk-CYCTIXUN.cjs +118 -0
  20. package/dist/chunk-CYCTIXUN.cjs.map +1 -0
  21. package/dist/chunk-CYPA5BW5.js +118 -0
  22. package/dist/chunk-CYPA5BW5.js.map +1 -0
  23. package/dist/{chunk-SJVRUPJT.js → chunk-JTNDIBZO.js} +2 -2
  24. package/dist/{chunk-XS6U2ARZ.js → chunk-MV2N2WNS.js} +6 -112
  25. package/dist/chunk-MV2N2WNS.js.map +1 -0
  26. package/dist/chunk-MXI26H62.cjs +2207 -0
  27. package/dist/chunk-MXI26H62.cjs.map +1 -0
  28. package/dist/{chunk-FBKTION6.js → chunk-QS7B42ZR.js} +640 -180
  29. package/dist/chunk-QS7B42ZR.js.map +1 -0
  30. package/dist/{chunk-IARLJ5MW.js → chunk-QX2ERL72.js} +2 -2
  31. package/dist/{chunk-2JT2WUQ2.cjs → chunk-ZWXQSKCX.cjs} +14 -120
  32. package/dist/chunk-ZWXQSKCX.cjs.map +1 -0
  33. package/dist/{index-DlcqkM_L.d.ts → index-BChYtrEe.d.ts} +1 -1
  34. package/dist/{index-DlCW0XaQ.d.cts → index-VXt_v3YI.d.cts} +1 -1
  35. package/dist/index.cjs +68 -593
  36. package/dist/index.cjs.map +1 -1
  37. package/dist/index.d.cts +245 -60
  38. package/dist/index.d.ts +245 -60
  39. package/dist/index.js +68 -593
  40. package/dist/index.js.map +1 -1
  41. package/dist/schemas.cjs +5 -3
  42. package/dist/schemas.cjs.map +1 -1
  43. package/dist/schemas.d.cts +2 -2
  44. package/dist/schemas.d.ts +2 -2
  45. package/dist/schemas.js +5 -3
  46. package/dist/scoring.cjs +3 -3
  47. package/dist/scoring.d.cts +2 -2
  48. package/dist/scoring.d.ts +2 -2
  49. package/dist/scoring.js +2 -2
  50. package/dist/xapi.cjs +3 -3
  51. package/dist/xapi.d.cts +1 -1
  52. package/dist/xapi.d.ts +1 -1
  53. package/dist/xapi.js +2 -2
  54. package/package.json +1 -1
  55. package/dist/chunk-2JT2WUQ2.cjs.map +0 -1
  56. package/dist/chunk-DSDXL3JO.cjs.map +0 -1
  57. package/dist/chunk-FBKTION6.js.map +0 -1
  58. package/dist/chunk-LDFQUZII.cjs +0 -950
  59. package/dist/chunk-LDFQUZII.cjs.map +0 -1
  60. package/dist/chunk-NQHETFDA.js +0 -950
  61. package/dist/chunk-NQHETFDA.js.map +0 -1
  62. package/dist/chunk-XS6U2ARZ.js.map +0 -1
  63. /package/dist/{chunk-SJVRUPJT.js.map → chunk-JTNDIBZO.js.map} +0 -0
  64. /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 GapSelectData as $, type ActivityAiPermissions as A, type AiReadingWordFact as B, type AiRefusal as C, type AiRubricCriterionFact as D, type AiSoundFact as E, type AiSupportedActivityType as F, type AiTextResult as G, type AiVerdict as H, type AiWordFact as I, type AiWritingCorrection as J, type AiWritingFacts as K, type AiWritingFeedback as L, type AiWritingFeedbackRequest as M, type AiWritingFeedbackResult as N, type BlankConfig as O, type CriterionScore as P, type DeferredReason as Q, type DeferredScoringPartial as R, type DictationData as S, type DictationEquivalence as T, type DictationLearnerResponse as U, type DictationSlowMedia as V, type DictationTolerance as W, type FillInTheBlanksData as X, type FillInTheBlanksLearnerResponse as Y, type GapSelectBank as Z, type GapSelectChoice as _, type ActivityData as a, type GapSelectGap as a0, type GapSelectLearnerResponse as a1, type GradeRecord as a2, type Grader as a3, type GraderKind as a4, type GraderUsage as a5, type GradingState as a6, type InlineCorrection as a7, type InteractionEvent as a8, type InteractionKind as a9, type WrittenResponseRubric as aA, type WrittenResponseRubricCriterion as aB, type XAPIActor as aC, type XAPIConfig as aD, type XAPIContext as aE, type XAPIContextActivities as aF, type XAPIError as aG, type XAPIObject as aH, type XAPIResult as aI, type XAPIScore as aJ, type XAPIStatement as aK, type XAPIVerbObject as aL, levenshteinDistance as aM, matchText as aN, 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 ActivityDataMap as b, 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 AiDictationFacts as n, type AiExplanationRequest as o, type AiFeature as p, type AiFillInTheBlanksFacts as q, type AiGapFact as r, type AiGapSelectFacts as s, type AiGrade as t, type AiHintRequest as u, type AiItemFacts as v, type AiMultipleChoiceFacts as w, type AiOptionFact as x, type AiProvenance as y, type AiReadingFacts as z };
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
- var _chunkVSH7ECQLcjs = require('./chunk-VSH7ECQL.cjs');
14
- require('./chunk-DSDXL3JO.cjs');
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 = _chunkLDFQUZIIcjs.aiExplanationRequest.call(void 0, { data, response });
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 = _chunkLDFQUZIIcjs.aiHintRequest.call(void 0, { data, response, previousHints });
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 = _chunkLDFQUZIIcjs.aiWritingFeedbackRequest.call(void 0, {
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 = _chunkVSH7ECQLcjs.gradeReadAloud.call(void 0,
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 = _chunkLDFQUZIIcjs.aiCoachingRequest.call(void 0, {
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 = _chunkLDFQUZIIcjs.checkAiCoaching.call(void 0, raw, one.request);
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 = _chunkLDFQUZIIcjs.checkAiWritingFeedback.call(void 0, raw, one.request);
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" ? _chunkLDFQUZIIcjs.checkAiExplanation.call(void 0, raw, one.request) : _chunkLDFQUZIIcjs.checkAiHint.call(void 0, raw, one.request);
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
  }