masterytrace-cli 0.1.0__tar.gz
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.
- masterytrace_cli-0.1.0/.gitignore +9 -0
- masterytrace_cli-0.1.0/LICENSE +21 -0
- masterytrace_cli-0.1.0/PKG-INFO +362 -0
- masterytrace_cli-0.1.0/README.md +326 -0
- masterytrace_cli-0.1.0/examples/README.md +27 -0
- masterytrace_cli-0.1.0/pyproject.toml +66 -0
- masterytrace_cli-0.1.0/src/masterytrace/__init__.py +68 -0
- masterytrace_cli-0.1.0/src/masterytrace/adapters/__init__.py +0 -0
- masterytrace_cli-0.1.0/src/masterytrace/adapters/generic_adapter.py +134 -0
- masterytrace_cli-0.1.0/src/masterytrace/cli/__init__.py +0 -0
- masterytrace_cli-0.1.0/src/masterytrace/cli/commands/__init__.py +0 -0
- masterytrace_cli-0.1.0/src/masterytrace/cli/commands/init.py +57 -0
- masterytrace_cli-0.1.0/src/masterytrace/cli/commands/record.py +51 -0
- masterytrace_cli-0.1.0/src/masterytrace/cli/commands/report.py +102 -0
- masterytrace_cli-0.1.0/src/masterytrace/cli/commands/score.py +63 -0
- masterytrace_cli-0.1.0/src/masterytrace/cli/format.py +17 -0
- masterytrace_cli-0.1.0/src/masterytrace/cli/index.py +177 -0
- masterytrace_cli-0.1.0/src/masterytrace/cli/json_encode.py +52 -0
- masterytrace_cli-0.1.0/src/masterytrace/cli/types.py +21 -0
- masterytrace_cli-0.1.0/src/masterytrace/core/__init__.py +0 -0
- masterytrace_cli-0.1.0/src/masterytrace/core/config.py +62 -0
- masterytrace_cli-0.1.0/src/masterytrace/core/engine.py +54 -0
- masterytrace_cli-0.1.0/src/masterytrace/core/event_schema.py +131 -0
- masterytrace_cli-0.1.0/src/masterytrace/core/scoring_model.py +68 -0
- masterytrace_cli-0.1.0/src/masterytrace/data/__init__.py +0 -0
- masterytrace_cli-0.1.0/src/masterytrace/data/sample_events.py +78 -0
- masterytrace_cli-0.1.0/src/masterytrace/models/__init__.py +0 -0
- masterytrace_cli-0.1.0/src/masterytrace/models/bkt.py +275 -0
- masterytrace_cli-0.1.0/src/masterytrace/models/irt.py +285 -0
- masterytrace_cli-0.1.0/src/masterytrace/py.typed +0 -0
- masterytrace_cli-0.1.0/tests/__init__.py +0 -0
- masterytrace_cli-0.1.0/tests/cli/__init__.py +0 -0
- masterytrace_cli-0.1.0/tests/cli/test_init.py +50 -0
- masterytrace_cli-0.1.0/tests/cli/test_record.py +45 -0
- masterytrace_cli-0.1.0/tests/cli/test_report.py +48 -0
- masterytrace_cli-0.1.0/tests/cli/test_score.py +41 -0
- masterytrace_cli-0.1.0/tests/test_bkt.py +159 -0
- masterytrace_cli-0.1.0/tests/test_cli_e2e.py +50 -0
- masterytrace_cli-0.1.0/tests/test_config.py +12 -0
- masterytrace_cli-0.1.0/tests/test_engine.py +49 -0
- masterytrace_cli-0.1.0/tests/test_event_schema.py +69 -0
- masterytrace_cli-0.1.0/tests/test_generic_adapter.py +108 -0
- masterytrace_cli-0.1.0/tests/test_irt.py +165 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Rudrendu Paul
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,362 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: masterytrace-cli
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Mastery Measurement API and CLI: fits Bayesian Knowledge Tracing (BKT) and Item Response Theory (2PL IRT) models to learner response logs and reports per-learner, per-skill mastery estimates.
|
|
5
|
+
Project-URL: Homepage, https://github.com/RudrenduPaul/MasteryTrace
|
|
6
|
+
Project-URL: Repository, https://github.com/RudrenduPaul/MasteryTrace
|
|
7
|
+
Project-URL: Bug Tracker, https://github.com/RudrenduPaul/MasteryTrace/issues
|
|
8
|
+
Project-URL: Changelog, https://github.com/RudrenduPaul/MasteryTrace/blob/main/CHANGELOG.md
|
|
9
|
+
Project-URL: Documentation, https://github.com/RudrenduPaul/MasteryTrace/blob/main/docs/getting-started.md
|
|
10
|
+
Project-URL: Author - Rudrendu Paul, https://github.com/RudrenduPaul
|
|
11
|
+
Project-URL: Author - Sourav Nandy, https://github.com/Sourav-nandy-ai
|
|
12
|
+
Author: Rudrendu Paul, Sourav Nandy
|
|
13
|
+
License-Expression: MIT
|
|
14
|
+
License-File: LICENSE
|
|
15
|
+
Keywords: agent-native,bayesian-knowledge-tracing,bkt,cli,edtech,education,irt,item-response-theory,learning-analytics,psychometrics
|
|
16
|
+
Classifier: Development Status :: 3 - Alpha
|
|
17
|
+
Classifier: Environment :: Console
|
|
18
|
+
Classifier: Intended Audience :: Developers
|
|
19
|
+
Classifier: Intended Audience :: Education
|
|
20
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
21
|
+
Classifier: Operating System :: OS Independent
|
|
22
|
+
Classifier: Programming Language :: Python :: 3
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
24
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
25
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
26
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
27
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
28
|
+
Classifier: Topic :: Education
|
|
29
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
30
|
+
Requires-Python: >=3.9
|
|
31
|
+
Provides-Extra: dev
|
|
32
|
+
Requires-Dist: build<2,>=1.0; extra == 'dev'
|
|
33
|
+
Requires-Dist: pytest<9,>=7.0; extra == 'dev'
|
|
34
|
+
Requires-Dist: twine<7,>=5.0; extra == 'dev'
|
|
35
|
+
Description-Content-Type: text/markdown
|
|
36
|
+
|
|
37
|
+
# masterytrace-cli (Python)
|
|
38
|
+
|
|
39
|
+
Mastery Measurement API and CLI: fits Bayesian Knowledge Tracing (BKT) and
|
|
40
|
+
Item Response Theory (2-parameter logistic IRT) models to learner response
|
|
41
|
+
logs and reports per-learner, per-skill mastery estimates.
|
|
42
|
+
|
|
43
|
+
[](https://github.com/RudrenduPaul/MasteryTrace/blob/main/LICENSE)
|
|
44
|
+
[](https://github.com/RudrenduPaul/MasteryTrace/actions/workflows/ci.yml)
|
|
45
|
+
|
|
46
|
+
**PyPI status**: publishing this package as `masterytrace-cli` is in
|
|
47
|
+
progress. The first publish attempt hit PyPI's account-level
|
|
48
|
+
`429 Too many new projects created` anti-abuse rate limit -- a
|
|
49
|
+
platform-side throttle on new package names, unrelated to this code or
|
|
50
|
+
to any account security issue -- so `pip install masterytrace-cli` is not
|
|
51
|
+
live yet. The PyPI version/Python-versions badges above will be restored
|
|
52
|
+
once the package is live; install from source below in the meantime.
|
|
53
|
+
|
|
54
|
+
## Why this exists
|
|
55
|
+
|
|
56
|
+
Most tutoring products and AI tutoring agents track raw percent-correct,
|
|
57
|
+
which conflates a lucky guess with real mastery and never says how
|
|
58
|
+
confident the estimate actually is. BKT and IRT are the two psychometric
|
|
59
|
+
models built to fix that, but they live almost entirely as Python-only
|
|
60
|
+
libraries with no CLI a non-Python tool (or an agent shelling out to a
|
|
61
|
+
subprocess) can call directly. MasteryTrace is a CLI and library that
|
|
62
|
+
turns a log of `{learnerId, skillId, correct, timestamp}` response events
|
|
63
|
+
into a posterior mastery probability (BKT) and an ability estimate (IRT)
|
|
64
|
+
per learner per skill. This package is the Python distribution -- a
|
|
65
|
+
genuine, independent port of the npm package's TypeScript source, not a
|
|
66
|
+
wrapper around a Node binary.
|
|
67
|
+
|
|
68
|
+
## Install
|
|
69
|
+
|
|
70
|
+
Once published:
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
pip install masterytrace-cli
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
or with [uv](https://docs.astral.sh/uv/):
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
uv add masterytrace-cli
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Until then, install from source (this checkout builds and installs
|
|
83
|
+
identically to how the PyPI package will):
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
git clone https://github.com/RudrenduPaul/MasteryTrace.git
|
|
87
|
+
cd MasteryTrace/python
|
|
88
|
+
pip install -e .
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
This installs a `masterytrace` console script and the `masterytrace`
|
|
92
|
+
importable package. No runtime dependencies beyond the Python standard
|
|
93
|
+
library -- the forward-recursion and gradient-ascent math in this port
|
|
94
|
+
are both plain scalar/list arithmetic, exactly what the TypeScript
|
|
95
|
+
original does with `Float64Array` and plain numbers, so a numpy/scipy
|
|
96
|
+
dependency would not simplify anything here.
|
|
97
|
+
|
|
98
|
+
**Honest note on the npm side:** the complementary JS/TS distribution
|
|
99
|
+
(`masterytrace-cli` on npm) is not yet published -- that is a deliberate,
|
|
100
|
+
unrelated decision by the maintainer, not a reflection of the npm
|
|
101
|
+
package's readiness (it passes CI and has a working `dist/` build). This
|
|
102
|
+
PyPI package is independent of that decision and installs and runs on its
|
|
103
|
+
own.
|
|
104
|
+
|
|
105
|
+
## Quickstart
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
masterytrace init
|
|
109
|
+
masterytrace record events.json
|
|
110
|
+
masterytrace score
|
|
111
|
+
masterytrace report
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
`init` scaffolds a sample `events.json` (3 learners, 3 skills, several
|
|
115
|
+
responses each) and a default `masterytrace.config.json` in the current
|
|
116
|
+
directory. Real output from that flow:
|
|
117
|
+
|
|
118
|
+
```
|
|
119
|
+
$ masterytrace init
|
|
120
|
+
Created: events.json, masterytrace.config.json
|
|
121
|
+
Next: run 'masterytrace record events.json' to load it, then 'masterytrace score'.
|
|
122
|
+
|
|
123
|
+
$ masterytrace record events.json
|
|
124
|
+
Stored 58 event(s) to /path/to/.masterytrace/events.json
|
|
125
|
+
(record replaces any previously stored event log; see --help for details.)
|
|
126
|
+
|
|
127
|
+
$ masterytrace score
|
|
128
|
+
Scored 58 event(s) with model(s): both
|
|
129
|
+
Wrote /path/to/.masterytrace/scores.json
|
|
130
|
+
|
|
131
|
+
$ masterytrace report
|
|
132
|
+
learner skill model metric value responses
|
|
133
|
+
------------- --------------------- ----- ----------------------------- ------- ---------
|
|
134
|
+
learner-ada fractions bkt posterior_mastery_probability 0.9994 6
|
|
135
|
+
learner-ada fractions irt ability_theta 0.7349 6
|
|
136
|
+
learner-ada linear-equations bkt posterior_mastery_probability 0.9746 7
|
|
137
|
+
learner-brook fractions bkt posterior_mastery_probability 0.0612 6
|
|
138
|
+
learner-cyrus reading-comprehension bkt posterior_mastery_probability 0.9947 7
|
|
139
|
+
...
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
`report` also takes `--format markdown` or `--format json`, and every
|
|
143
|
+
command accepts a global `--json` flag for machine-readable output on
|
|
144
|
+
stdout, with a real exit code contract (`0` success, `1` general/usage
|
|
145
|
+
error, `2` bad event data) so a script or agent invoking this CLI can
|
|
146
|
+
branch on the result without parsing text.
|
|
147
|
+
|
|
148
|
+
Your own event log is a JSON array of `{learnerId, skillId, correct,
|
|
149
|
+
timestamp}` objects, or a CSV with header
|
|
150
|
+
`learner_id,skill_id,correct,timestamp`. `timestamp` must be ISO 8601;
|
|
151
|
+
`correct` is a boolean (JSON) or `true`/`false`/`1`/`0` (CSV), and any
|
|
152
|
+
other value in a CSV `correct` cell is rejected as a validation error
|
|
153
|
+
rather than silently treated as false. Event log files over 100 MB are
|
|
154
|
+
rejected up front with a clear error.
|
|
155
|
+
|
|
156
|
+
## CLI command reference
|
|
157
|
+
|
|
158
|
+
| Command | Arguments | Options | Does |
|
|
159
|
+
| --- | --- | --- | --- |
|
|
160
|
+
| `masterytrace init` | | `--force` | Scaffolds a sample `events.json` and `masterytrace.config.json` in the current directory. Skips files that already exist unless `--force` is passed. |
|
|
161
|
+
| `masterytrace record <path>` | `<path>`: JSON or CSV event log | | Validates an event log and stores it to `.masterytrace/events.json`. Always replaces any previously stored log. |
|
|
162
|
+
| `masterytrace score` | | `--model <bkt\|irt\|both>` (default `both`) | Fits and scores the stored event log, writing the result to `.masterytrace/scores.json`. |
|
|
163
|
+
| `masterytrace report` | | `--format <table\|json\|markdown>` (default `table`) | Reads `.masterytrace/scores.json` and prints a per-learner, per-skill mastery table. |
|
|
164
|
+
|
|
165
|
+
Global option: `--json` forces machine-readable JSON on stdout for any
|
|
166
|
+
command, overriding `--format` on `report`.
|
|
167
|
+
|
|
168
|
+
Exit codes: `0` success, `1` general or usage error (bad flag, missing
|
|
169
|
+
file), `2` validation error (the event log itself is malformed).
|
|
170
|
+
|
|
171
|
+
## Library API
|
|
172
|
+
|
|
173
|
+
```python
|
|
174
|
+
from masterytrace import (
|
|
175
|
+
# Event schema and validation
|
|
176
|
+
ResponseEvent, parse_response_events, EventValidationError,
|
|
177
|
+
|
|
178
|
+
# Engine: runs one or both models
|
|
179
|
+
run_scoring, EngineConfig, EngineResult,
|
|
180
|
+
|
|
181
|
+
# BKT
|
|
182
|
+
BktModel, BKT_DEFAULT_PARAMS, run_forward_recursion, fit_skill_params_by_grid_search,
|
|
183
|
+
BktParams, BktConfig, BktFittedModel,
|
|
184
|
+
|
|
185
|
+
# IRT
|
|
186
|
+
IrtModel, probability_correct,
|
|
187
|
+
IrtItemParams, IrtLearnerResult, IrtConfig, IrtFittedModel,
|
|
188
|
+
|
|
189
|
+
# Generic JSON/CSV event log adapter
|
|
190
|
+
generic_adapter, parse_csv, GenericAdapter,
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
events = parse_response_events([
|
|
194
|
+
{"learnerId": "l1", "skillId": "fractions", "correct": True, "timestamp": "2026-01-01T00:00:00Z"},
|
|
195
|
+
{"learnerId": "l1", "skillId": "fractions", "correct": False, "timestamp": "2026-01-02T00:00:00Z"},
|
|
196
|
+
])
|
|
197
|
+
|
|
198
|
+
result = run_scoring(events, "both")
|
|
199
|
+
# result.reports[0].model == "bkt", result.reports[1].model == "irt"
|
|
200
|
+
# each learner's report.learners[i].skills[j].value is the mastery estimate
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
`BktModel` and `IrtModel` both implement the same `fit(events)` then
|
|
204
|
+
`score(fitted_model)` two-step interface, so the engine, and your own
|
|
205
|
+
code, can treat them interchangeably.
|
|
206
|
+
|
|
207
|
+
**A deliberate naming divergence from the npm package**: this Python
|
|
208
|
+
port's JSON output and dataclass field names are `snake_case`
|
|
209
|
+
(`learner_id`, `response_count`, `item_discrimination`) rather than the
|
|
210
|
+
npm CLI's `camelCase` (`learnerId`, `responseCount`,
|
|
211
|
+
`itemDiscrimination`), matching each language's own convention. The data
|
|
212
|
+
shape -- which fields exist and what they mean -- is identical.
|
|
213
|
+
|
|
214
|
+
## How BKT and IRT work
|
|
215
|
+
|
|
216
|
+
MasteryTrace implements two independent psychometric models. They answer
|
|
217
|
+
different questions and produce different kinds of numbers, so
|
|
218
|
+
`masterytrace score --model both` runs them side by side rather than
|
|
219
|
+
picking one.
|
|
220
|
+
|
|
221
|
+
### Bayesian Knowledge Tracing (BKT)
|
|
222
|
+
|
|
223
|
+
BKT models one learner's mastery of one skill as a hidden binary state
|
|
224
|
+
(knows it / does not know it yet) and updates a probability of "knows it"
|
|
225
|
+
after every response, using four parameters: `p_init` (prior probability
|
|
226
|
+
the learner already knows the skill), `p_transit` (probability of
|
|
227
|
+
learning the skill between one attempt and the next), `p_slip`
|
|
228
|
+
(probability of an incorrect answer despite knowing the skill), and
|
|
229
|
+
`p_guess` (probability of a correct answer despite not knowing the
|
|
230
|
+
skill). For each response, the forward recursion first updates the belief
|
|
231
|
+
given the observed outcome (Bayes' rule), then advances it for possible
|
|
232
|
+
learning before the next attempt:
|
|
233
|
+
|
|
234
|
+
```
|
|
235
|
+
after correct: P(know | obs) = P(know) * (1 - p_slip) / [P(know) * (1 - p_slip) + (1 - P(know)) * p_guess]
|
|
236
|
+
after incorrect: P(know | obs) = P(know) * p_slip / [P(know) * p_slip + (1 - P(know)) * (1 - p_guess)]
|
|
237
|
+
P(know)_next = P(know | obs) + (1 - P(know | obs)) * p_transit
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
MasteryTrace runs this recursion per learner per skill, in chronological
|
|
241
|
+
order, and reports the final posterior as that learner's mastery
|
|
242
|
+
probability for that skill. If you set `"bkt": {"fit": true}` in
|
|
243
|
+
`masterytrace.config.json`, each skill's four parameters are fit from
|
|
244
|
+
your own data by a coarse grid search (7 x 7 x 5 x 5 candidate
|
|
245
|
+
combinations) that minimizes squared error between predicted and observed
|
|
246
|
+
correctness, instead of using the textbook defaults (`p_init=0.4,
|
|
247
|
+
p_transit=0.3, p_slip=0.1, p_guess=0.2`). This is the same coarse,
|
|
248
|
+
dependency-free grid search the TypeScript original implements -- not a
|
|
249
|
+
full EM/Baum-Welch fit -- ported line-for-line, including its parameter
|
|
250
|
+
grid values.
|
|
251
|
+
|
|
252
|
+
### Item Response Theory (2PL IRT)
|
|
253
|
+
|
|
254
|
+
IRT models one continuous learner ability (`theta`) per learner and two
|
|
255
|
+
parameters per skill treated as an "item": discrimination (`a`, how
|
|
256
|
+
sharply the item separates high- and low-ability learners) and difficulty
|
|
257
|
+
(`b`). The probability of a correct response under the 2-parameter
|
|
258
|
+
logistic model is:
|
|
259
|
+
|
|
260
|
+
```
|
|
261
|
+
P(correct) = sigmoid(a * (theta - b))
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
MasteryTrace fits all of these jointly by gradient ascent on the
|
|
265
|
+
log-likelihood (joint MLE), with a small L2 penalty pulling `theta`/`b`
|
|
266
|
+
toward 0 and `a` toward 1. That penalty is what keeps the fit finite for
|
|
267
|
+
a learner or skill with an all-correct or all-incorrect record, where the
|
|
268
|
+
unregularized likelihood would otherwise be maximized at infinity.
|
|
269
|
+
Because the 2PL model is only identified up to a shift and scale of
|
|
270
|
+
`theta` (shifting `theta` and `b` by the same constant, or scaling
|
|
271
|
+
`theta`/`b` while dividing `a` accordingly, leaves every predicted
|
|
272
|
+
probability unchanged), the fit re-centers `theta` to mean 0 and standard
|
|
273
|
+
deviation 1 after every iteration -- the standard way to pin down a
|
|
274
|
+
single solution. This port mirrors the TypeScript original's hand-rolled
|
|
275
|
+
batch gradient ascent line for line (same update rule, same per-iteration
|
|
276
|
+
gauge fix, same default 500 iterations / 0.5 learning rate / 0.01
|
|
277
|
+
regularization), rather than calling into an external optimizer -- there
|
|
278
|
+
is no scipy.optimize equivalent in play to port to, since the original
|
|
279
|
+
does not use one either.
|
|
280
|
+
|
|
281
|
+
### A real recovery check
|
|
282
|
+
|
|
283
|
+
`tests/test_irt.py` fits the model against a synthetic dataset built from
|
|
284
|
+
known ground-truth `theta`/`a`/`b` values (4,000 responses across 5
|
|
285
|
+
learners and 4 skills, same synthetic setup as the TypeScript suite's own
|
|
286
|
+
recovery test) and checks that the recovered parameters land close to the
|
|
287
|
+
true ones once put through the same gauge normalization, within the same
|
|
288
|
+
0.3 absolute-error tolerance the TypeScript test uses.
|
|
289
|
+
|
|
290
|
+
## Benchmark
|
|
291
|
+
|
|
292
|
+
Run locally against synthetic event logs (single core, in-process library
|
|
293
|
+
calls, no subprocess/CLI startup overhead):
|
|
294
|
+
|
|
295
|
+
| Dataset | Events | BKT fit+score | IRT fit+score (500 iterations) |
|
|
296
|
+
| --- | --- | --- | --- |
|
|
297
|
+
| Small | 10,000 (50 learners x 20 skills x 10 responses) | 0.05s | 2.86s |
|
|
298
|
+
| Large | 100,000 (100 learners x 50 skills x 20 responses) | 0.09s | 14.42s |
|
|
299
|
+
|
|
300
|
+
BKT with grid-search fitting (`"bkt": {"fit": true}`, a 1,225-combination
|
|
301
|
+
grid search per skill) on the 10,000-event dataset took 5.20s.
|
|
302
|
+
|
|
303
|
+
**Honest comparison with the TypeScript package**: the npm package's own
|
|
304
|
+
documented benchmark reports the 100,000-event IRT fit at 0.54s (as part
|
|
305
|
+
of a `--model both` subprocess run) -- roughly 27x faster than this
|
|
306
|
+
Python port's 14.42s for the same fit alone. This gap is expected and
|
|
307
|
+
disclosed, not a porting bug: IRT's gradient-ascent fit is a tight
|
|
308
|
+
numeric loop, and V8's JIT compiles that loop far more aggressively than
|
|
309
|
+
CPython's interpreter executes the equivalent Python loop. The fitted
|
|
310
|
+
*values* are what this port keeps faithful (see the recovery check
|
|
311
|
+
above, and the identical-to-4-decimal-places output in the Quickstart
|
|
312
|
+
section); wall-clock fit time on large datasets is not. See
|
|
313
|
+
[SECURITY.md](https://github.com/RudrenduPaul/MasteryTrace/blob/main/SECURITY.md)'s
|
|
314
|
+
"known limitation" section for what this means for very large,
|
|
315
|
+
untrusted event logs.
|
|
316
|
+
|
|
317
|
+
## CI integration
|
|
318
|
+
|
|
319
|
+
```yaml
|
|
320
|
+
- uses: actions/checkout@v4
|
|
321
|
+
- uses: actions/setup-python@v5
|
|
322
|
+
with:
|
|
323
|
+
python-version: '3.12'
|
|
324
|
+
- run: pip install masterytrace-cli
|
|
325
|
+
- run: masterytrace init
|
|
326
|
+
- run: masterytrace record events.json
|
|
327
|
+
- run: masterytrace score --model both
|
|
328
|
+
- run: masterytrace report --format json > mastery-report.json
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
Full walkthrough in
|
|
332
|
+
[docs/integrations/ci.md](https://github.com/RudrenduPaul/MasteryTrace/blob/main/docs/integrations/ci.md).
|
|
333
|
+
|
|
334
|
+
## Security
|
|
335
|
+
|
|
336
|
+
MasteryTrace never `eval()`s or `exec()`s anything read from an event
|
|
337
|
+
log; event data is only ever parsed as JSON/CSV and pattern-matched
|
|
338
|
+
against the validation schema. The generic adapter refuses symlinked
|
|
339
|
+
paths and files over 100 MB before reading them, closing the two most
|
|
340
|
+
likely ways a maliciously crafted event-log path could cause unexpected
|
|
341
|
+
behavior. **Honest note**: this project does not currently publish SLSA
|
|
342
|
+
provenance, Sigstore signatures, or an SBOM, and has no OpenSSF Scorecard
|
|
343
|
+
badge set up -- none of that infrastructure exists yet for either
|
|
344
|
+
distribution, so it isn't claimed here. See
|
|
345
|
+
[SECURITY.md](https://github.com/RudrenduPaul/MasteryTrace/blob/main/SECURITY.md)
|
|
346
|
+
for the vulnerability disclosure process.
|
|
347
|
+
|
|
348
|
+
## Contributing
|
|
349
|
+
|
|
350
|
+
See [CONTRIBUTING.md](https://github.com/RudrenduPaul/MasteryTrace/blob/main/CONTRIBUTING.md)
|
|
351
|
+
for the full guide, covering both the TypeScript and Python codebases.
|
|
352
|
+
|
|
353
|
+
```bash
|
|
354
|
+
cd python
|
|
355
|
+
python3 -m venv .venv && source .venv/bin/activate
|
|
356
|
+
pip install -e ".[dev]"
|
|
357
|
+
pytest
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
## License
|
|
361
|
+
|
|
362
|
+
MIT, see [LICENSE](https://github.com/RudrenduPaul/MasteryTrace/blob/main/LICENSE).
|