|
| 1 | +============================================================================= |
| 2 | +ScanCode-Toolkit: Mark required phrases for rules automatically using NLP/AI |
| 3 | +============================================================================= |
| 4 | + |
| 5 | +| Mentee: **Kaushik Kumar R** |
| 6 | +| GitHub: `Kaushik-Kumar-CEG <https://github.com/Kaushik-Kumar-CEG>`_ |
| 7 | +| LinkedIn: `Profile <https://www.linkedin.com/in/kaushik-kumar-ceg/>`_ |
| 8 | +| Repository: `ScanCode Required Phrases |
| 9 | + <https://github.com/aboutcode-org/scancode-required-phrases>`_ |
| 10 | +| Official GSoC project page: `Project Link |
| 11 | + <https://summerofcode.withgoogle.com/programs/2026/projects/K6CQtnjP>`_ |
| 12 | +| GSoC Proposal: `Proposal Link |
| 13 | + <https://docs.google.com/document/d/1qXYTrIoF4Tn5q-dPMQicCFZ0NKbLj9jYN18ipyPyqZI/edit?usp=sharing>`_ |
| 14 | +| Model: `Hugging Face |
| 15 | + <https://huggingface.co/Kaushik-Kumar-CEG/scancode-required-phrases-deberta-bioes-crf-hardened>`_ |
| 16 | +
|
| 17 | +Overview |
| 18 | +-------- |
| 19 | + |
| 20 | +Required phrases help ScanCode avoid weak or partial license matches. They are |
| 21 | +marked with ``{{...}}`` inside a license rule. If the phrase is missing from the |
| 22 | +scanned text, that rule does not match. |
| 23 | + |
| 24 | +Many rules do not have these markers yet. Existing ScanCode commands can copy |
| 25 | +known phrases to similar rules, but they need a phrase to start with. The work |
| 26 | +in `scancode-toolkit#3924 |
| 27 | +<https://github.com/aboutcode-org/scancode-toolkit/pull/3924>`_ provides the |
| 28 | +rule APIs used here. This project adds an NLP model that can suggest a phrase |
| 29 | +directly from rule text. |
| 30 | + |
| 31 | +A wrong phrase can hide a valid license match, so model output cannot be written |
| 32 | +to rules without checks and review. The work includes dataset generation, model |
| 33 | +training, prediction validation, a maintainer review command and a small |
| 34 | +ScanCode Toolkit wrapper. |
| 35 | + |
| 36 | +The two main flows are: |
| 37 | + |
| 38 | +.. code-block:: text |
| 39 | +
|
| 40 | + marked ScanCode rules -> dataset -> training -> public model |
| 41 | + unmarked rule -> prediction -> ScanCode checks -> review -> rule update |
| 42 | +
|
| 43 | +Work Completed |
| 44 | +-------------- |
| 45 | + |
| 46 | +Dataset and composite rules |
| 47 | +^^^^^^^^^^^^^^^^^^^^^^^^^^^ |
| 48 | + |
| 49 | +The dataset command reads eligible ScanCode ``.RULE`` files and converts |
| 50 | +existing ``{{...}}`` markers into BIOES token labels. Text outside the markers |
| 51 | +is labelled ``O``. |
| 52 | + |
| 53 | +The split is deterministic. Rules with rarer license expressions stay together |
| 54 | +in one split. Rules from common expressions are assigned by a hash of their |
| 55 | +identifiers to keep the splits balanced. After tokenizer alignment checks, the |
| 56 | +training run used 8,265 training rules, 1,044 validation rules and 1,004 test |
| 57 | +rules. |
| 58 | + |
| 59 | +A separate command handles composite expressions. It uses phrases already known |
| 60 | +for the individual license keys and only updates a composite rule when every |
| 61 | +key has a safe, non-overlapping match. |
| 62 | + |
| 63 | +Model training and inference |
| 64 | +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ |
| 65 | + |
| 66 | +The selected model is based on DeBERTa v3 large with BIOES labels and a constrained |
| 67 | +conditional random field (CRF) decoder. BIOES marks the start, inside, end and |
| 68 | +single-token cases for each phrase. The CRF keeps the decoded label sequence |
| 69 | +valid. |
| 70 | + |
| 71 | +Training and test evaluation are kept separate. Test evaluation is opt-in and |
| 72 | +was run only for the selected model. The training command also reloads and |
| 73 | +checks the saved model before treating the output as complete. |
| 74 | + |
| 75 | +The model is public on Hugging Face. It is pinned to revision |
| 76 | +``11215925b0f9b64cfcfbbb5492b52d6aeb5a572b`` for the default command. |
| 77 | + |
| 78 | +.. list-table:: Model results |
| 79 | + :widths: 40 30 30 |
| 80 | + :width: 100% |
| 81 | + :header-rows: 1 |
| 82 | + |
| 83 | + * - Metric |
| 84 | + - Validation |
| 85 | + - Test |
| 86 | + * - Precision |
| 87 | + - 0.8981 |
| 88 | + - 0.8748 |
| 89 | + * - Recall |
| 90 | + - 0.9035 |
| 91 | + - 0.8784 |
| 92 | + * - F1 |
| 93 | + - 0.9008 |
| 94 | + - 0.8766 |
| 95 | + * - Exact match |
| 96 | + - 0.8563 |
| 97 | + - 0.8197 |
| 98 | + |
| 99 | +Precision, recall and F1 are strict span metrics. The validation and test runs |
| 100 | +had no invalid BIOES paths. |
| 101 | + |
| 102 | +Review and rule updates |
| 103 | +^^^^^^^^^^^^^^^^^^^^^^^ |
| 104 | + |
| 105 | +The main maintainer command is ``add-model-required-phrases``. It can review one |
| 106 | +rule, a directory of rules or eligible rules installed with ScanCode Toolkit. |
| 107 | +It shows the prediction, score, validation result, nearby text and the exact |
| 108 | +rule diff before asking for a decision. |
| 109 | + |
| 110 | +A review can approve, reject, edit or defer a phrase. Sessions are saved as |
| 111 | +JSONL and can be resumed without loading the model again. Predict-only mode can |
| 112 | +write machine-readable JSON without creating a review session or changing any |
| 113 | +rule. |
| 114 | + |
| 115 | +Batch mode requires explicit score thresholds. It does not write unless |
| 116 | +``--yes`` is supplied. ``--dry-run`` always prevents rule-file changes. After |
| 117 | +installed rules are changed, the command tells the maintainer to rebuild the |
| 118 | +ScanCode license index. |
| 119 | + |
| 120 | +Safety checks |
| 121 | +^^^^^^^^^^^^^ |
| 122 | + |
| 123 | +Every prediction is treated as a candidate. ScanCode's own rule APIs decide |
| 124 | +whether the phrase can be inserted. The command blocks phrases that overlap |
| 125 | +protected content such as URLs and referenced filenames. It also rejects |
| 126 | +ambiguous repeated text, conflicting spans and predictions cut by model input |
| 127 | +limits. |
| 128 | + |
| 129 | +Before applying a reviewed session, the command checks every path and file hash |
| 130 | +again. It prepares the complete update first, then writes each changed rule once |
| 131 | +using atomic replacement. File permissions and line endings are kept. A rule |
| 132 | +with a pending phrase is left unchanged. |
| 133 | + |
| 134 | +Toolkit integration |
| 135 | +^^^^^^^^^^^^^^^^^^^ |
| 136 | + |
| 137 | +The model code and its optional dependencies live in the |
| 138 | +``scancode-required-phrases`` package. ScanCode Toolkit only gets a small source |
| 139 | +checkout wrapper. This keeps normal ScanCode installation and license scanning |
| 140 | +free from ML dependencies. The wrapper imports the package command and passes |
| 141 | +its arguments through. |
| 142 | + |
| 143 | +A second Toolkit pull request contains ten manually reviewed rule changes from |
| 144 | +a fixed sample of 30 rules. The sample was selected before prediction and was |
| 145 | +checked against the final train, validation and test data. |
| 146 | + |
| 147 | +The model returned 37 candidate phrases across 22 of the 30 rules. Twenty-one |
| 148 | +passed the insertion checks and 16 were blocked. Ten clear license-specific |
| 149 | +phrases were kept. Each updated rule still matched its original full text, while |
| 150 | +text missing the required phrase did not match that rule. |
| 151 | + |
| 152 | +Using the Command |
| 153 | +----------------- |
| 154 | + |
| 155 | +Install the inference dependencies from a package checkout: |
| 156 | + |
| 157 | +.. code-block:: console |
| 158 | +
|
| 159 | + python -m pip install ".[inference]" |
| 160 | +
|
| 161 | +Review one rule: |
| 162 | + |
| 163 | +.. code-block:: console |
| 164 | +
|
| 165 | + add-model-required-phrases --rule path/to/example.RULE |
| 166 | +
|
| 167 | +Run read-only prediction and save JSON: |
| 168 | + |
| 169 | +.. code-block:: console |
| 170 | +
|
| 171 | + add-model-required-phrases --rule path/to/example.RULE \ |
| 172 | + --predict-only --json predictions.json |
| 173 | +
|
| 174 | +Preview a batch without writing rule files: |
| 175 | + |
| 176 | +.. code-block:: console |
| 177 | +
|
| 178 | + add-model-required-phrases --all --batch \ |
| 179 | + --auto-score 0.90 --review-score 0.70 --dry-run |
| 180 | +
|
| 181 | +Linked Pull Requests |
| 182 | +-------------------- |
| 183 | + |
| 184 | +.. list-table:: |
| 185 | + :widths: 8 62 30 |
| 186 | + :width: 100% |
| 187 | + :header-rows: 1 |
| 188 | + |
| 189 | + * - No. |
| 190 | + - Work |
| 191 | + - Link |
| 192 | + * - 1 |
| 193 | + - Dataset and composite required phrase commands |
| 194 | + - `scancode-required-phrases#1 |
| 195 | + <https://github.com/aboutcode-org/scancode-required-phrases/pull/1>`_ |
| 196 | + * - 2 |
| 197 | + - Model training, export and read-only inference |
| 198 | + - `scancode-required-phrases#2 |
| 199 | + <https://github.com/aboutcode-org/scancode-required-phrases/pull/2>`_ |
| 200 | + * - 3 |
| 201 | + - Model prediction and rule integration |
| 202 | + - `scancode-required-phrases#3 |
| 203 | + <https://github.com/aboutcode-org/scancode-required-phrases/pull/3>`_ |
| 204 | + * - 4 |
| 205 | + - Human review workflow for model predictions |
| 206 | + - `scancode-required-phrases#4 |
| 207 | + <https://github.com/aboutcode-org/scancode-required-phrases/pull/4>`_ |
| 208 | + * - 5 |
| 209 | + - ScanCode Toolkit command wrapper |
| 210 | + - `scancode-toolkit#5267 |
| 211 | + <https://github.com/aboutcode-org/scancode-toolkit/pull/5267>`_ |
| 212 | + * - 6 |
| 213 | + - Reviewed model predictions for ten sample rules |
| 214 | + - `scancode-toolkit#5330 |
| 215 | + <https://github.com/aboutcode-org/scancode-toolkit/pull/5330>`_ |
| 216 | + |
| 217 | +Testing |
| 218 | +------- |
| 219 | + |
| 220 | +The package tests cover dataset generation, model loading, prediction decoding, |
| 221 | +rule validation, review sessions, stale files, dry runs, batch thresholds, |
| 222 | +atomic writes and command output. The complete package suite passed 322 tests. |
| 223 | +Documentation, style, source distributions and wheels were also checked. |
| 224 | + |
| 225 | +The Toolkit wrapper passed its focused tests with a fake package and with the |
| 226 | +real public model. The ten evidence rules passed ScanCode rule validation and |
| 227 | +the focused required-phrase tests. |
| 228 | + |
| 229 | +Earlier Work |
| 230 | +------------ |
| 231 | + |
| 232 | +Before GSoC, I worked on a few ScanCode license detection cases. These helped me |
| 233 | +understand rule data and how small rule changes affect matching. |
| 234 | + |
| 235 | +* `Add a rule for CKSource Holding copyright |
| 236 | + <https://github.com/aboutcode-org/scancode-toolkit/pull/4649>`_ |
| 237 | +* `Fix incorrect license detection in dragon4.c |
| 238 | + <https://github.com/aboutcode-org/scancode-toolkit/pull/4654>`_ |
| 239 | +* `Fix JCalendar LGPL detected as GPL |
| 240 | + <https://github.com/aboutcode-org/scancode-toolkit/pull/4880>`_ |
| 241 | + |
| 242 | +Current Limits and Next Steps |
| 243 | +----------------------------- |
| 244 | + |
| 245 | +The model can miss a phrase or suggest a boundary that is not useful. Scores are |
| 246 | +not enough to approve a change, which is why review and ScanCode validation stay |
| 247 | +part of the workflow. Long rules may also be truncated by the model and are not |
| 248 | +approved automatically. |
| 249 | + |
| 250 | +The package pull requests and both Toolkit pull requests are still open. After |
| 251 | +they are reviewed and merged, I plan to prepare version ``0.1.0`` and help with |
| 252 | +the PyPI release. I will keep maintaining the dataset tools, training pipeline, |
| 253 | +model and review command. Maintainer feedback and reviewed rules can be used for |
| 254 | +future training runs. |
| 255 | + |
| 256 | +One later area to study is `scancode-toolkit#3945 |
| 257 | +<https://github.com/aboutcode-org/scancode-toolkit/issues/3945>`_. It considers |
| 258 | +ranking matches with required phrases instead of always filtering matches that |
| 259 | +miss them. The phrases produced by this project can still be used if that policy |
| 260 | +changes. |
| 261 | + |
| 262 | +Closing Thoughts |
| 263 | +---------------- |
| 264 | + |
| 265 | +GSoC taught me much more than how to train an NLP model. I learned how ScanCode's |
| 266 | +license rules and matching code work, how to turn an experiment into a package |
| 267 | +and why review matters for even a small rule change. |
| 268 | + |
| 269 | +The weekly community calls were one of my favourite parts of the project. I |
| 270 | +could show my progress, hear where an idea was weak and fix it before going too |
| 271 | +far. Thanks to `Ayan Sinha Mahapatra |
| 272 | +<https://github.com/AyanSinhaMahapatra>`_, `Philippe Ombredanne |
| 273 | +<https://github.com/pombredanne>`_ and `Dennis Clark |
| 274 | +<https://github.com/DennisClark>`_ for their time, feedback and patience. |
| 275 | + |
| 276 | +I am very glad I got to work with the `AboutCode community |
| 277 | +<https://github.com/aboutcode-org>`_. I learned a lot from the reviews and the |
| 278 | +technical discussions. I plan to keep contributing to AboutCode and continue |
| 279 | +maintaining this work after GSoC. |
0 commit comments