RadGraph-IT 1.0.0__py3-none-any.whl
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.
- radgraph_it-1.0.0.dist-info/METADATA +159 -0
- radgraph_it-1.0.0.dist-info/RECORD +39 -0
- radgraph_it-1.0.0.dist-info/WHEEL +4 -0
- radgraph_it-1.0.0.dist-info/entry_points.txt +2 -0
- radgraph_it-1.0.0.dist-info/licenses/LICENSE +23 -0
- radgraph_it-1.0.0.dist-info/licenses/LICENSES/Third-Party.md +41 -0
- radgraphit/__init__.py +47 -0
- radgraphit/api.py +168 -0
- radgraphit/artifacts/__init__.py +5 -0
- radgraphit/artifacts/manifest.py +290 -0
- radgraphit/artifacts/registry.py +24 -0
- radgraphit/artifacts/resolver.py +200 -0
- radgraphit/cli.py +131 -0
- radgraphit/core/__init__.py +1 -0
- radgraphit/core/errors.py +46 -0
- radgraphit/core/models.py +116 -0
- radgraphit/inference/__init__.py +5 -0
- radgraphit/inference/backends/__init__.py +1 -0
- radgraphit/inference/backends/base.py +13 -0
- radgraphit/inference/backends/dygie_v2/__init__.py +11 -0
- radgraphit/inference/backends/dygie_v2/backend.py +32 -0
- radgraphit/inference/backends/dygie_v2/device.py +46 -0
- radgraphit/inference/backends/dygie_v2/feedforward.py +25 -0
- radgraphit/inference/backends/dygie_v2/loader.py +165 -0
- radgraphit/inference/backends/dygie_v2/model.py +101 -0
- radgraphit/inference/backends/dygie_v2/ner_head.py +75 -0
- radgraphit/inference/backends/dygie_v2/pruner.py +27 -0
- radgraphit/inference/backends/dygie_v2/relation_head.py +147 -0
- radgraphit/inference/backends/dygie_v2/span_extractor.py +51 -0
- radgraphit/inference/backends/dygie_v2/spans.py +20 -0
- radgraphit/inference/backends/dygie_v2/tokenizer_embedder.py +123 -0
- radgraphit/inference/backends/dygie_v2/transformer_block.py +62 -0
- radgraphit/inference/backends/dygie_v2/vocab.py +123 -0
- radgraphit/inference/decoder.py +53 -0
- radgraphit/inference/service.py +80 -0
- radgraphit/inference/tokenizer.py +60 -0
- radgraphit/py.typed +0 -0
- radgraphit/serialization/__init__.py +15 -0
- radgraphit/serialization/radgraph_xl.py +135 -0
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: RadGraph-IT
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: RadGraph graph inference for Italian radiology reports, with output in the canonical RadGraph-XL format.
|
|
5
|
+
Project-URL: Homepage, https://github.com/therabo/RadGraph-IT
|
|
6
|
+
Project-URL: Repository, https://github.com/therabo/RadGraph-IT
|
|
7
|
+
Project-URL: Issues, https://github.com/therabo/RadGraph-IT/issues
|
|
8
|
+
Project-URL: Changelog, https://github.com/therabo/RadGraph-IT/blob/main/CHANGELOG.md
|
|
9
|
+
Author: Daniel Rabottini, Edoardo Avenia, Rocco Angelella
|
|
10
|
+
License-Expression: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
License-File: LICENSES/Third-Party.md
|
|
13
|
+
Keywords: information-extraction,italian,nlp,radgraph,radiology
|
|
14
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
15
|
+
Classifier: Intended Audience :: Healthcare Industry
|
|
16
|
+
Classifier: Intended Audience :: Science/Research
|
|
17
|
+
Classifier: Natural Language :: Italian
|
|
18
|
+
Classifier: Operating System :: OS Independent
|
|
19
|
+
Classifier: Programming Language :: Python :: 3
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
24
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
25
|
+
Classifier: Typing :: Typed
|
|
26
|
+
Requires-Python: >=3.10
|
|
27
|
+
Requires-Dist: huggingface-hub>=0.23
|
|
28
|
+
Requires-Dist: nltk>=3.8
|
|
29
|
+
Requires-Dist: packaging>=23
|
|
30
|
+
Requires-Dist: torch>=2.1
|
|
31
|
+
Requires-Dist: transformers>=4.39
|
|
32
|
+
Provides-Extra: dev
|
|
33
|
+
Requires-Dist: build>=1.2; extra == 'dev'
|
|
34
|
+
Requires-Dist: mypy>=1.11; extra == 'dev'
|
|
35
|
+
Requires-Dist: pre-commit>=3.5; extra == 'dev'
|
|
36
|
+
Requires-Dist: pytest-cov>=5; extra == 'dev'
|
|
37
|
+
Requires-Dist: pytest>=8; extra == 'dev'
|
|
38
|
+
Requires-Dist: ruff==0.16.0; extra == 'dev'
|
|
39
|
+
Requires-Dist: safetensors>=0.4; extra == 'dev'
|
|
40
|
+
Requires-Dist: twine>=5; extra == 'dev'
|
|
41
|
+
Provides-Extra: safetensors
|
|
42
|
+
Requires-Dist: safetensors>=0.4; extra == 'safetensors'
|
|
43
|
+
Description-Content-Type: text/markdown
|
|
44
|
+
|
|
45
|
+
# RadGraph-IT 🇮🇹
|
|
46
|
+
|
|
47
|
+
**RadGraph** graph inference for **Italian** radiology reports, with output in the canonical
|
|
48
|
+
**RadGraph-XL** format.
|
|
49
|
+
|
|
50
|
+
`radgraphit` takes one or more Italian radiology reports, runs the Italian DyGIE v2 model
|
|
51
|
+
(PyTorch: shared encoder, span extractor, NER head, and relation head), and returns the graph of
|
|
52
|
+
entities and relations as the RadGraph-XL-compatible dictionary.
|
|
53
|
+
|
|
54
|
+
- **Package / import / CLI name:** `radgraphit` · **Python:** `>= 3.10` · **Device:** CPU or CUDA
|
|
55
|
+
- **Model:** downloaded automatically from pinned Hugging Face revisions on first use
|
|
56
|
+
(~445 MB / 424 MiB plus small tokenizer/config files, then cached)
|
|
57
|
+
|
|
58
|
+
## Installation
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
pip install radgraphit
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Usage — Python API
|
|
65
|
+
|
|
66
|
+
```python
|
|
67
|
+
from radgraphit import RadGraphIT
|
|
68
|
+
|
|
69
|
+
predictor = RadGraphIT()
|
|
70
|
+
annotations = predictor.predict(
|
|
71
|
+
"Non si evidenziano segni di pneumotorace dopo la rimozione del drenaggio toracico."
|
|
72
|
+
)
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
`predict` accepts a **string** or a **sequence of strings** and preserves order.
|
|
76
|
+
`predictor(report)` is an alias for `predictor.predict(report)`:
|
|
77
|
+
|
|
78
|
+
```python
|
|
79
|
+
annotations = predictor(["referto 1", "referto 2"]) # batch, order preserved
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Usage — CLI
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
# report as an argument
|
|
86
|
+
radgraphit predict "Non si evidenzia pneumotorace."
|
|
87
|
+
|
|
88
|
+
# from a file (one report per line), or from stdin
|
|
89
|
+
radgraphit predict --input-file reports.txt
|
|
90
|
+
echo "Nessuna evidenza di pneumotorace." | radgraphit predict
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
The result is written as JSON to stdout.
|
|
94
|
+
|
|
95
|
+
## Output
|
|
96
|
+
|
|
97
|
+
For the input `["Non si evidenzia pneumotorace ..."]` the output has this shape (top-level keys
|
|
98
|
+
`"0"`, `"1"`, … in input order):
|
|
99
|
+
|
|
100
|
+
```python
|
|
101
|
+
{
|
|
102
|
+
"0": {
|
|
103
|
+
"text": "<normalized tokens, space-separated>",
|
|
104
|
+
"entities": {
|
|
105
|
+
"1": {
|
|
106
|
+
"tokens": "<span tokens>",
|
|
107
|
+
"label": "Observation::definitely absent",
|
|
108
|
+
"start_ix": 3,
|
|
109
|
+
"end_ix": 3,
|
|
110
|
+
"relations": [["located_at", "2"]],
|
|
111
|
+
}
|
|
112
|
+
},
|
|
113
|
+
"data_source": None,
|
|
114
|
+
"data_split": "inference",
|
|
115
|
+
},
|
|
116
|
+
"1": {"...": "..."},
|
|
117
|
+
}
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
- The indices (`start_ix`, `end_ix`) are **token-level, zero-based, and inclusive**.
|
|
121
|
+
- Relations are directed *source → target* lists `[label, target_entity_id]`.
|
|
122
|
+
- The output is deterministic: entities and relations are stably ordered.
|
|
123
|
+
|
|
124
|
+
## Citation
|
|
125
|
+
|
|
126
|
+
The output format and the serializer derive from the official **RadGraph / RadGraph-XL** package
|
|
127
|
+
(see [`Third-Party`](LICENSES/Third-Party.md)). If you use this package, please cite
|
|
128
|
+
both this work and the RadGraph-XL paper:
|
|
129
|
+
|
|
130
|
+
```bibtex
|
|
131
|
+
@software{radgraphit,
|
|
132
|
+
author = "Daniel Rabottini and Edoardo Avenia and Rocco Angelella",
|
|
133
|
+
title = "{RadGraph-IT}: {RadGraph} graph inference for {Italian} radiology reports",
|
|
134
|
+
year = "2026",
|
|
135
|
+
version = "1.0.0",
|
|
136
|
+
url = "https://github.com/therabo/RadGraph-IT",
|
|
137
|
+
}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
```bibtex
|
|
141
|
+
@inproceedings{delbrouck-etal-2024-radgraph,
|
|
142
|
+
title = "{R}ad{G}raph-{XL}: A Large-Scale Expert-Annotated Dataset for Entity and Relation
|
|
143
|
+
Extraction from Radiology Reports",
|
|
144
|
+
author = "Delbrouck, Jean-Benoit and Chambon, Pierre and Chen, Zhihong and Varma, Maya and
|
|
145
|
+
Johnston, Andrew and Blankemeier, Louis and Van Veen, Dave and Bui, Tan and
|
|
146
|
+
Truong, Steven and Langlotz, Curtis",
|
|
147
|
+
booktitle = "Findings of the Association for Computational Linguistics ACL 2024",
|
|
148
|
+
year = "2024",
|
|
149
|
+
url = "https://aclanthology.org/2024.findings-acl.765",
|
|
150
|
+
pages = "12902--12915",
|
|
151
|
+
}
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
## License
|
|
155
|
+
|
|
156
|
+
See the [`MIT License`](LICENSE) and [`Third-Party`](LICENSES/Third-Party.md) notices.
|
|
157
|
+
|
|
158
|
+
> **Not a medical device.** The output is an automatic extraction of entities and relations from
|
|
159
|
+
> text and does not constitute a diagnosis, a report, or a clinical opinion.
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
radgraphit/__init__.py,sha256=axB-_vOQ0oxpkLfKcm-zugE9EAno__Q2Cs1vYpDuGz4,1253
|
|
2
|
+
radgraphit/api.py,sha256=gBSE9ghTwDGb_KZ9eMGQAMbEsjn_CIOkBKt1UqWByOg,6549
|
|
3
|
+
radgraphit/cli.py,sha256=Q7Ifzji1ae9NplUwtGc-kvnL3HAA1DHBISFNGBueUzM,4784
|
|
4
|
+
radgraphit/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
5
|
+
radgraphit/artifacts/__init__.py,sha256=9s6PKiGZW26zn6F_BBJ_ApsV-XeP1KDrcjlnhKhBcrE,247
|
|
6
|
+
radgraphit/artifacts/manifest.py,sha256=PGnZKb1f28dpvcRd8ssP516Ry5XCD1KNAjm-SySZfqE,12702
|
|
7
|
+
radgraphit/artifacts/registry.py,sha256=4n6DHuoi3qDLiONQUVxVYqDpboF3k3_sEym6HxG554k,929
|
|
8
|
+
radgraphit/artifacts/resolver.py,sha256=4AfDPUAEQOu13BLd8IS5rJ8XP90OXUiq9t_CD71C-so,8078
|
|
9
|
+
radgraphit/core/__init__.py,sha256=spw93ti5KhNBlUCSKIjDK74nWhmTp7O3Z418DZAJb1Q,86
|
|
10
|
+
radgraphit/core/errors.py,sha256=BJgE1FP2sVINBAw--0nvlqKntS5rLnLZTl8W5i75pHo,1577
|
|
11
|
+
radgraphit/core/models.py,sha256=WlWLwzQeoSNasGzYH0BnvUsxtS3L3oWGSHUSjS_JhYA,3843
|
|
12
|
+
radgraphit/inference/__init__.py,sha256=ZXwhdEOAJ-B1nH8ZGEzVOmJk3ocqWgQQllMqiogApXY,255
|
|
13
|
+
radgraphit/inference/decoder.py,sha256=ls79WFJ4HQbh_FKHhDpFpvkQWk-Bi8Aal5GRrBssGBg,1689
|
|
14
|
+
radgraphit/inference/service.py,sha256=xNpdpxip9BVGR-FxvXJ_cJu3f0F1JsInslnQahYkPes,3364
|
|
15
|
+
radgraphit/inference/tokenizer.py,sha256=Er2ooK8jy-JqmDRfwIjknTc4lD8bmRwvgEqSH0R_IMg,2692
|
|
16
|
+
radgraphit/inference/backends/__init__.py,sha256=fxImXnfQFeAaa8RuUueZslnHLgsdSC6OYyCdhHY6A0o,96
|
|
17
|
+
radgraphit/inference/backends/base.py,sha256=W5noZOMATRabbKBHk3lLKlauLHVCJdCScIMBkYScXXA,590
|
|
18
|
+
radgraphit/inference/backends/dygie_v2/__init__.py,sha256=EqFi85Wz4aajgDLRO85AGdVKKsca3yljeebTJ95bk9U,492
|
|
19
|
+
radgraphit/inference/backends/dygie_v2/backend.py,sha256=ZvgJsIjJBKgnsEjWQVvw3XIbbTwuv82Y17kLdMpWDVY,1073
|
|
20
|
+
radgraphit/inference/backends/dygie_v2/device.py,sha256=66TtM3sEbnLvTGJSPgYDLnd4M_mam-RB7R5f48L7Lzk,1917
|
|
21
|
+
radgraphit/inference/backends/dygie_v2/feedforward.py,sha256=bNR5LM0MbOfyqa2eVSFbmOckdVDGgJbaRSFzBBYJwkk,797
|
|
22
|
+
radgraphit/inference/backends/dygie_v2/loader.py,sha256=PHh6mB6NGDog7vPB7JMzMKX_ZwjwpDPLKd-SBPQTjLw,6847
|
|
23
|
+
radgraphit/inference/backends/dygie_v2/model.py,sha256=JTLEORUALbn9sE33_pwgGki6K1VMezmadyAacgNeV-c,3689
|
|
24
|
+
radgraphit/inference/backends/dygie_v2/ner_head.py,sha256=797nBLytn5VmwWK1EEDOMunl_TvWhqaq05SFNng1QF4,3082
|
|
25
|
+
radgraphit/inference/backends/dygie_v2/pruner.py,sha256=nd7lH7d7Y82Sb2XtdG8sp9AwNV-Cy9pwYhPdJgpQ-DM,1022
|
|
26
|
+
radgraphit/inference/backends/dygie_v2/relation_head.py,sha256=XD1es7h57Z4AEpSuMYMXVSlmwNKWsqiK8OD2Txc7apc,6449
|
|
27
|
+
radgraphit/inference/backends/dygie_v2/span_extractor.py,sha256=ekB0BDtRlRzqmErM55UFPIBgqqXcaQk8s-0Rn8bubsY,2284
|
|
28
|
+
radgraphit/inference/backends/dygie_v2/spans.py,sha256=7i5MnVC5UK9yf1WBUlMB7E_WxAy1Z2EG3iyCf5UwHBg,804
|
|
29
|
+
radgraphit/inference/backends/dygie_v2/tokenizer_embedder.py,sha256=YQtTlIDNmMU6PjY8GQzz0DOygAU-cH_gkTUIt2iECbg,4948
|
|
30
|
+
radgraphit/inference/backends/dygie_v2/transformer_block.py,sha256=yHoOcemeR9eDiExD7WlP-nTWX-geNcAxrVmnYgPvxkE,2272
|
|
31
|
+
radgraphit/inference/backends/dygie_v2/vocab.py,sha256=xiHU0Tzij0fbDJUSp2ErB1n48pfFV9BWblGttR0ZpBY,5332
|
|
32
|
+
radgraphit/serialization/__init__.py,sha256=QCd8PjDTXaeO9ayJHNiE4CiSYPEFDug8hN7kR7ZqKOk,364
|
|
33
|
+
radgraphit/serialization/radgraph_xl.py,sha256=g3E7368G_-0Ue-N7Xad_e0CyundjDPDuQ51a47402ck,5965
|
|
34
|
+
radgraph_it-1.0.0.dist-info/METADATA,sha256=Zl4fb4tByGUgIfNgcJW79i02f_OYjIYl2uDsc6-wevs,5682
|
|
35
|
+
radgraph_it-1.0.0.dist-info/WHEEL,sha256=lCkmxWfQsSc9CfIClYeavTdQeEX2toPqufh9gI35EQA,87
|
|
36
|
+
radgraph_it-1.0.0.dist-info/entry_points.txt,sha256=xxBQ-WlmPXumfg3tNJNKk7VIpamQX4v8UmIpmFnYNxQ,51
|
|
37
|
+
radgraph_it-1.0.0.dist-info/licenses/LICENSE,sha256=5KhNHP1ryGIP_aYYWPUNBQ0PUnLPKGznCAb-95svTbc,1185
|
|
38
|
+
radgraph_it-1.0.0.dist-info/licenses/LICENSES/Third-Party.md,sha256=k63tiwV1ZJTDPC4cX20jEBjbzV_1xDjIj_svU16XTJU,2019
|
|
39
|
+
radgraph_it-1.0.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Daniel Rabottini (therabo)
|
|
4
|
+
Copyright (c) 2026 Edoardo Avenia (edoardoavenia)
|
|
5
|
+
Copyright (c) 2026 Rocco Angelella (roccoangelella)
|
|
6
|
+
|
|
7
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
8
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
9
|
+
in the Software without restriction, including without limitation the rights
|
|
10
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
11
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
12
|
+
furnished to do so, subject to the following conditions:
|
|
13
|
+
|
|
14
|
+
The above copyright notice and this permission notice shall be included in all
|
|
15
|
+
copies or substantial portions of the Software.
|
|
16
|
+
|
|
17
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
18
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
19
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
20
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
21
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
22
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
23
|
+
SOFTWARE.
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# Third-Party
|
|
2
|
+
|
|
3
|
+
This file collects provenance and attributions. It does **not** declare weights, datasets, or
|
|
4
|
+
clinical texts redistributable, and it does **not** automatically copy any third-party license. The
|
|
5
|
+
package code itself is licensed under MIT (see the root `LICENSE` file); this file concerns the
|
|
6
|
+
third-party components it builds on.
|
|
7
|
+
|
|
8
|
+
## RadGraph-XL output format and serializer
|
|
9
|
+
|
|
10
|
+
The output dictionary and the serialization logic (`postprocess_reports` / `get_entity`) derive from
|
|
11
|
+
the official **RadGraph / RadGraph-XL** package from Stanford AIMI / StanfordMIMI.
|
|
12
|
+
|
|
13
|
+
- Upstream repository: `radgraph` package (Stanford), distributed under the MIT license.
|
|
14
|
+
|
|
15
|
+
Citation:
|
|
16
|
+
|
|
17
|
+
> Delbrouck et al. "RadGraph-XL: A Large-Scale Expert-Annotated Dataset for Entity and Relation
|
|
18
|
+
> Extraction from Radiology Reports." Findings of the ACL 2024, pp. 12902–12915.
|
|
19
|
+
> https://aclanthology.org/2024.findings-acl.765
|
|
20
|
+
|
|
21
|
+
## Inference backend (`dygie_v2`)
|
|
22
|
+
|
|
23
|
+
An inference-only PyTorch port of the Italian v2 model (internal project "RadGraph-XL-IT",
|
|
24
|
+
`training_v2`), which is itself a pure-PyTorch reimplementation of the DyGIE++ architecture
|
|
25
|
+
(NER + relations) without AllenNLP.
|
|
26
|
+
|
|
27
|
+
- DyGIE / DyGIE++ architecture: Wadden et al., "Entity, Relation, and Event Extraction with
|
|
28
|
+
Contextualized Span Representations", EMNLP 2019.
|
|
29
|
+
- Pre-trained encoder: fetched at runtime from the Hugging Face Hub (e.g. `IVN-RIN/medBIT-r3-plus`);
|
|
30
|
+
subject to the license of its own repository on the Hub. Not redistributed by this package.
|
|
31
|
+
|
|
32
|
+
## Model, weights, and data
|
|
33
|
+
|
|
34
|
+
The model weights, the datasets, and any clinical text are **not** included in this repository or in
|
|
35
|
+
the wheel/sdist, and are not declared redistributable. The model bundle is hosted separately on the
|
|
36
|
+
Hugging Face Hub and is subject to the licenses/terms declared in that repository.
|
|
37
|
+
|
|
38
|
+
## Runtime dependencies
|
|
39
|
+
|
|
40
|
+
`torch`, `transformers`, `nltk`, `huggingface_hub` are subject to their respective licenses
|
|
41
|
+
(BSD-3-Clause, Apache-2.0, Apache-2.0, Apache-2.0 respectively, unless changed upstream).
|
radgraphit/__init__.py
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"""radgraphit — RadGraph-XL inference for Italian radiology reports.
|
|
2
|
+
|
|
3
|
+
Public API::
|
|
4
|
+
|
|
5
|
+
from radgraphit import RadGraphIT
|
|
6
|
+
|
|
7
|
+
Importing this package has no side effects: no network I/O, no model download, no logging to
|
|
8
|
+
stdout, and no torch import. Everything model-related is loaded lazily on first use.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
import logging
|
|
14
|
+
|
|
15
|
+
from .api import RadGraphIT
|
|
16
|
+
from .core.errors import (
|
|
17
|
+
BackendError,
|
|
18
|
+
CheckpointError,
|
|
19
|
+
DeviceError,
|
|
20
|
+
InvalidReportError,
|
|
21
|
+
ManifestError,
|
|
22
|
+
ModelConfigurationError,
|
|
23
|
+
ModelNotConfiguredError,
|
|
24
|
+
ModelNotFoundError,
|
|
25
|
+
RadGraphITError,
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
# A library must not configure logging handlers for the application; attach a NullHandler so that
|
|
29
|
+
# emitting logs never prints a "No handlers could be found" warning if the app hasn't configured
|
|
30
|
+
# logging (see https://docs.python.org/3/howto/logging.html#library-config).
|
|
31
|
+
logging.getLogger("radgraphit").addHandler(logging.NullHandler())
|
|
32
|
+
|
|
33
|
+
__version__ = "1.0.0"
|
|
34
|
+
|
|
35
|
+
__all__ = [
|
|
36
|
+
"BackendError",
|
|
37
|
+
"CheckpointError",
|
|
38
|
+
"DeviceError",
|
|
39
|
+
"InvalidReportError",
|
|
40
|
+
"ManifestError",
|
|
41
|
+
"ModelConfigurationError",
|
|
42
|
+
"ModelNotConfiguredError",
|
|
43
|
+
"ModelNotFoundError",
|
|
44
|
+
"RadGraphIT",
|
|
45
|
+
"RadGraphITError",
|
|
46
|
+
"__version__",
|
|
47
|
+
]
|
radgraphit/api.py
ADDED
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
"""The public facade: :class:`RadGraphIT`.
|
|
2
|
+
|
|
3
|
+
Small, typed, stable, and free of import-time side effects. Constructing a ``RadGraphIT`` performs
|
|
4
|
+
no network I/O; the model artifact is resolved and loaded lazily on first ``predict`` (or an
|
|
5
|
+
explicit :meth:`load`). Importing this module does not import torch or huggingface_hub.
|
|
6
|
+
|
|
7
|
+
Usage::
|
|
8
|
+
|
|
9
|
+
from radgraphit import RadGraphIT
|
|
10
|
+
|
|
11
|
+
predictor = RadGraphIT.from_local("/path/to/model", device="auto")
|
|
12
|
+
annotations = predictor.predict([
|
|
13
|
+
"Non si evidenziano segni di pneumotorace dopo la rimozione del drenaggio toracico."
|
|
14
|
+
])
|
|
15
|
+
|
|
16
|
+
The standard Hub-backed path is::
|
|
17
|
+
|
|
18
|
+
predictor = RadGraphIT(device="auto") # or RadGraphIT.from_pretrained(...)
|
|
19
|
+
annotations = predictor.predict("Nessuna evidenza di pneumotorace.")
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
from __future__ import annotations
|
|
23
|
+
|
|
24
|
+
import functools
|
|
25
|
+
import logging
|
|
26
|
+
from collections.abc import Callable, Sequence
|
|
27
|
+
from typing import TYPE_CHECKING
|
|
28
|
+
|
|
29
|
+
from .artifacts import registry
|
|
30
|
+
from .core.errors import BackendError
|
|
31
|
+
from .core.models import InferenceBackend, ReportPrediction
|
|
32
|
+
from .inference.service import InferenceService
|
|
33
|
+
|
|
34
|
+
if TYPE_CHECKING:
|
|
35
|
+
from .artifacts.resolver import ResolvedModel
|
|
36
|
+
|
|
37
|
+
logger = logging.getLogger("radgraphit")
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def _resolve_pretrained(
|
|
41
|
+
model_id: str, revision: str, cache_dir: str | None, local_files_only: bool
|
|
42
|
+
) -> ResolvedModel:
|
|
43
|
+
# Imported lazily so `import radgraphit` never pulls in huggingface_hub.
|
|
44
|
+
from .artifacts import resolver
|
|
45
|
+
|
|
46
|
+
return resolver.resolve_pretrained(
|
|
47
|
+
model_id, revision=revision, cache_dir=cache_dir, local_files_only=local_files_only
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def _resolve_local(path: str) -> ResolvedModel:
|
|
52
|
+
from .artifacts import resolver
|
|
53
|
+
|
|
54
|
+
return resolver.resolve_local(path)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def _build_backend(resolved: ResolvedModel, device: str) -> InferenceBackend:
|
|
58
|
+
backend_name = resolved.manifest.backend
|
|
59
|
+
if backend_name == "dygie_v2":
|
|
60
|
+
# Imported lazily so torch/transformers load only when a model is actually built.
|
|
61
|
+
from .inference.backends.dygie_v2 import load_backend
|
|
62
|
+
|
|
63
|
+
return load_backend(
|
|
64
|
+
config_path=resolved.config_path,
|
|
65
|
+
vocab_path=resolved.vocab_path,
|
|
66
|
+
weights_path=resolved.weights_path,
|
|
67
|
+
device=device,
|
|
68
|
+
encoder_revision=resolved.manifest.encoder_revision,
|
|
69
|
+
)
|
|
70
|
+
raise BackendError(
|
|
71
|
+
f"Model manifest requests backend {backend_name!r}, which this version of radgraphit does "
|
|
72
|
+
f"not support (supported: {sorted(registry.SUPPORTED_BACKENDS)})."
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
class RadGraphIT:
|
|
77
|
+
"""Predict RadGraph-XL graphs for Italian radiology reports."""
|
|
78
|
+
|
|
79
|
+
def __init__(
|
|
80
|
+
self,
|
|
81
|
+
*,
|
|
82
|
+
device: str = "auto",
|
|
83
|
+
model_id: str | None = None,
|
|
84
|
+
revision: str | None = None,
|
|
85
|
+
cache_dir: str | None = None,
|
|
86
|
+
local_files_only: bool = False,
|
|
87
|
+
):
|
|
88
|
+
"""Configure a predictor backed by the default (Hugging Face Hub) model.
|
|
89
|
+
|
|
90
|
+
No network I/O happens here — the artifact is downloaded and the model built lazily on the
|
|
91
|
+
first :meth:`predict`. Passing ``model_id``/``revision`` overrides the registry defaults.
|
|
92
|
+
The default model is a public, ungated Hub repository, so no authentication is required.
|
|
93
|
+
"""
|
|
94
|
+
self._device = device
|
|
95
|
+
self._service: InferenceService | None = None
|
|
96
|
+
resolved_model_id = model_id if model_id is not None else registry.DEFAULT_MODEL_ID
|
|
97
|
+
resolved_revision = revision if revision is not None else registry.DEFAULT_REVISION
|
|
98
|
+
self._resolve: Callable[[], ResolvedModel] = functools.partial(
|
|
99
|
+
_resolve_pretrained, resolved_model_id, resolved_revision, cache_dir, local_files_only
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
@classmethod
|
|
103
|
+
def from_pretrained(
|
|
104
|
+
cls,
|
|
105
|
+
model_id: str = registry.DEFAULT_MODEL_ID,
|
|
106
|
+
*,
|
|
107
|
+
revision: str = registry.DEFAULT_REVISION,
|
|
108
|
+
cache_dir: str | None = None,
|
|
109
|
+
device: str = "auto",
|
|
110
|
+
local_files_only: bool = False,
|
|
111
|
+
) -> RadGraphIT:
|
|
112
|
+
"""Standard path: resolve the model from the Hugging Face Hub (lazily, on first predict).
|
|
113
|
+
|
|
114
|
+
``revision`` is pinned explicitly (commit sha or tag). The default model is a public,
|
|
115
|
+
ungated Hub repository, so no authentication is required.
|
|
116
|
+
"""
|
|
117
|
+
return cls(
|
|
118
|
+
device=device,
|
|
119
|
+
model_id=model_id,
|
|
120
|
+
revision=revision,
|
|
121
|
+
cache_dir=cache_dir,
|
|
122
|
+
local_files_only=local_files_only,
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
@classmethod
|
|
126
|
+
def from_local(cls, path: str, *, device: str = "auto") -> RadGraphIT:
|
|
127
|
+
"""Development / offline path: load a manifest-verified bundle from a local directory."""
|
|
128
|
+
instance = cls(device=device)
|
|
129
|
+
instance._resolve = functools.partial(_resolve_local, path)
|
|
130
|
+
return instance
|
|
131
|
+
|
|
132
|
+
@classmethod
|
|
133
|
+
def from_service(cls, service: InferenceService, *, device: str = "auto") -> RadGraphIT:
|
|
134
|
+
"""Advanced/testing: build a predictor around a pre-constructed service (e.g. a custom or
|
|
135
|
+
fake backend). Skips all artifact resolution and loading."""
|
|
136
|
+
instance = cls(device=device)
|
|
137
|
+
instance._service = service
|
|
138
|
+
return instance
|
|
139
|
+
|
|
140
|
+
def load(self) -> RadGraphIT:
|
|
141
|
+
"""Eagerly resolve and load the model. Returns ``self`` for chaining. Optional — the model
|
|
142
|
+
is otherwise loaded on first :meth:`predict`."""
|
|
143
|
+
self._ensure_loaded()
|
|
144
|
+
return self
|
|
145
|
+
|
|
146
|
+
def _ensure_loaded(self) -> InferenceService:
|
|
147
|
+
if self._service is None:
|
|
148
|
+
resolved = self._resolve()
|
|
149
|
+
backend = _build_backend(resolved, self._device)
|
|
150
|
+
self._service = InferenceService(backend)
|
|
151
|
+
logger.debug("RadGraphIT model loaded (backend=%s)", resolved.manifest.backend)
|
|
152
|
+
return self._service
|
|
153
|
+
|
|
154
|
+
def predict(self, reports: str | Sequence[str]) -> dict[str, object]:
|
|
155
|
+
"""Annotate one report (``str``) or several (``Sequence[str]``).
|
|
156
|
+
|
|
157
|
+
Returns the canonical RadGraph-XL dictionary, keyed by input order (``"0"``, ``"1"``, …).
|
|
158
|
+
See the README for the exact output shape.
|
|
159
|
+
"""
|
|
160
|
+
return self._ensure_loaded().predict(reports)
|
|
161
|
+
|
|
162
|
+
def predict_graphs(self, reports: str | Sequence[str]) -> list[ReportPrediction]:
|
|
163
|
+
"""Advanced: return the typed internal graphs instead of the RadGraph-XL dict."""
|
|
164
|
+
return self._ensure_loaded().predict_graphs(reports)
|
|
165
|
+
|
|
166
|
+
def __call__(self, reports: str | Sequence[str]) -> dict[str, object]:
|
|
167
|
+
"""Alias for :meth:`predict`, for compatibility with the upstream callable interface."""
|
|
168
|
+
return self.predict(reports)
|