Skip to content

Commit a748809

Browse files
authored
Merge pull request #6 from NCATSTranslator/trapi-2.0
Trapi 2.0
2 parents e68b72d + 200210e commit a748809

304 files changed

Lines changed: 21926 additions & 1348 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

README.md

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,37 @@ Models based on Pydantic provide deserialize with basic validation, serialize, a
66

77
Allows for easy FastAPI standup.
88

9+
## TRAPI versions
10+
11+
`translator_tom` provides models for multiple TRAPI versions. When importing directly from `translator_tom`, you automatically import models for the latest version.
12+
13+
```python
14+
from translator_tom import Response # TRAPI 2.0 (latest)
15+
from translator_tom.model_dicts import ResponseDict # TRAPI 2.0 (latest)
16+
```
17+
18+
To pin a specific version, import it from its version subpackage:
19+
20+
```python
21+
from translator_tom.v2_0 import Response # TRAPI 2.0
22+
from translator_tom.v1_6 import Response # TRAPI 1.6
23+
from translator_tom.v1_6.model_dicts import ResponseDict
24+
```
25+
26+
Each version has the same general API: models, model_dicts, diff, semantic validation (WIP). Some items are version-agnostic (Biolink, CURIEs, `TOMBase`, etc.) and shared between the two (`translator_tom.utils`).
27+
28+
### Converting TRAPI versions
29+
30+
The TRAPI 2.0 package provides a utility for converting 1.6 models to 2.0 models:
31+
32+
```python
33+
from translator_tom import up_version
34+
35+
my_v1_response = ... # Some TRAPI 1.6 response
36+
37+
my_v2_response = up_version(my_v1_response)
38+
```
39+
940
## Model Usage
1041

1142
The main ways you interact with a Model are as follows:
@@ -344,6 +375,15 @@ This returns a list of warnings and errors with clear descriptions and tuples de
344375
> [!WARNING]
345376
> This feature is WIP and does not do every bit of semantic validation you might expect.
346377
378+
## Scripts
379+
380+
TOM provides some module-level scripts, for your convenience:
381+
382+
- `tom-parse`: Parse a given JSON into a given TOM model to check that it parses.
383+
- `tom-validate`: Run semantic validation (WIP) against a given JSON/TOM model.
384+
- `tom-up-version`: Upgrade a TRAPI 1.6 JSON to TRAPI 2.0.
385+
- `tom-diff`: Diff two JSONs of a given TOM model.
386+
347387
## Design Decisions
348388

349389
There are a view caveats to using TOM, listed below:

bench/test_sd.py

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,20 @@
11
"""Quick serdes benchmark: one file per size bucket, comparing TOM vs reasoner-pydantic.
22
3-
Streams per-file timings in an aligned format as they complete, then prints a
4-
summary table across files at the end.
3+
Runs against `data/example_trapi/<version>/`. Defaults to `1.6` because the pinned
4+
reasoner-pydantic models TRAPI 1.x; pass `--version 2.0` for TOM-only 2.0 timings
5+
(the reasoner-pydantic rows are not meaningful there). Streams per-file timings in an
6+
aligned format as they complete, then prints a summary table across files at the end.
57
"""
68

79
import time
810

911
import orjson
1012
from pydantic import TypeAdapter
1113

12-
from utils import CORPUS_ROOT, read_corpus_file
14+
from utils import corpus_root, import_version, parse_version, read_corpus_file
15+
16+
VERSION = parse_version(__doc__, default="1.6")
17+
CORPUS_ROOT = corpus_root(VERSION)
1318

1419
LABEL_WIDTH = 23
1520
VALUE_FMT = "{:>8.4f}s"
@@ -45,8 +50,7 @@ def section(title: str) -> None:
4550
# --- Imports ---
4651

4752
t0 = time.perf_counter()
48-
from translator_tom import Response # noqa: E402
49-
53+
Response = import_version(VERSION).Response
5054
t_tom = time.perf_counter() - t0
5155

5256
t0 = time.perf_counter()
@@ -55,7 +59,7 @@ def section(title: str) -> None:
5559
t_rp = time.perf_counter() - t0
5660

5761
section("Imports")
58-
print(f" {'translator_tom':<{LABEL_WIDTH}} {VALUE_FMT.format(t_tom)}")
62+
print(f" {f'translator_tom {VERSION}':<{LABEL_WIDTH}} {VALUE_FMT.format(t_tom)}")
5963
print(f" {'reasoner-pydantic':<{LABEL_WIDTH}} {VALUE_FMT.format(t_rp)}")
6064

6165

@@ -103,9 +107,7 @@ def section(title: str) -> None:
103107
pair_row("adapter.python", t_vp, t_dp, file_results)
104108

105109
# Combined dict-based pipeline (alternative to adapter.json single-pass).
106-
pair_row(
107-
"orjson + adapter.python", t_loads + t_vp, t_dp + t_dumps, file_results
108-
)
110+
pair_row("orjson + adapter.python", t_loads + t_vp, t_dp + t_dumps, file_results)
109111

110112
# --- adapter (json: bytes <-> model) ---
111113
t0 = time.perf_counter()
@@ -154,8 +156,7 @@ def section(title: str) -> None:
154156
section("Summary (seconds): from / to")
155157

156158
short_labels = {
157-
lbl: lbl.split("/")[-1].removesuffix(".gz").removesuffix(".json")
158-
for lbl in results
159+
lbl: lbl.split("/")[-1].removesuffix(".gz").removesuffix(".json") for lbl in results
159160
}
160161
ops = list(next(iter(results.values())).keys())
161162

bench/test_sd_tom.py

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,25 @@
11
"""TOM-only serdes benchmark across every example response.
22
3-
Walks `data/example_trapi/**` and runs the core (de)serialization paths for
4-
each file. Streams per-file timings in an aligned format as they complete,
5-
then prints a summary table across files at the end.
3+
Walks `data/example_trapi/<version>/**` (default `2.0`; pass `--version 1.6`) and
4+
runs the core (de)serialization paths for each file. Streams per-file timings in an
5+
aligned format as they complete, then prints a summary table across files at the end.
66
77
For a quicker comparison run that also benches reasoner-pydantic on one file
88
per size bucket, see `bench/test_sd.py`.
99
"""
1010

1111
import time
1212

13-
from utils import CORPUS_ROOT, discover_files, read_corpus_file
13+
from utils import (
14+
corpus_root,
15+
discover_files,
16+
import_version,
17+
parse_version,
18+
read_corpus_file,
19+
)
20+
21+
VERSION = parse_version(__doc__)
22+
CORPUS_ROOT = corpus_root(VERSION)
1423

1524
LABEL_WIDTH = 10
1625
VALUE_FMT = "{:>8.4f}s"
@@ -40,12 +49,11 @@ def section(title: str) -> None:
4049
# --- Import ---
4150

4251
t0 = time.perf_counter()
43-
from translator_tom import Response # noqa: E402
44-
52+
Response = import_version(VERSION).Response
4553
t_tom = time.perf_counter() - t0
4654

4755
section("Imports")
48-
print(f" translator_tom Response {VALUE_FMT.format(t_tom)}")
56+
print(f" translator_tom {VERSION} Response {VALUE_FMT.format(t_tom)}")
4957

5058

5159
TEST_FILES = discover_files(CORPUS_ROOT)

bench/test_sd_tom_dicts.py

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
"""Dict-util-only serdes benchmark across every example response.
22
3-
The `model_dicts` twin of `bench/test_sd_tom.py`: same corpus walk and output,
4-
but driving the `*DictUtil` serdes (raw orjson/ormsgpack over the TypedDict form,
5-
no model construction) instead of the `Response` model. Run both to see the cost
6-
the model layer adds over operating on plain dicts.
3+
The `model_dicts` twin of `bench/test_sd_tom.py`: same corpus walk and output
4+
(default `2.0`; pass `--version 1.6`), but driving the `*DictUtil` serdes (raw
5+
orjson/ormsgpack over the TypedDict form, no model construction) instead of the
6+
`Response` model. Run both to see the cost the model layer adds over operating on
7+
plain dicts.
78
89
The `+val` rows re-run the `from` path with `validate=True`, adding a pydantic
910
`TypeAdapter` pass over the parsed data; their `from` timing minus the plain
@@ -12,7 +13,16 @@
1213

1314
import time
1415

15-
from utils import CORPUS_ROOT, discover_files, read_corpus_file
16+
from utils import (
17+
corpus_root,
18+
discover_files,
19+
import_version,
20+
parse_version,
21+
read_corpus_file,
22+
)
23+
24+
VERSION = parse_version(__doc__)
25+
CORPUS_ROOT = corpus_root(VERSION)
1626

1727
LABEL_WIDTH = 12
1828
VALUE_FMT = "{:>8.4f}s"
@@ -42,12 +52,13 @@ def section(title: str) -> None:
4252
# --- Import ---
4353

4454
t0 = time.perf_counter()
45-
from translator_tom.model_dicts import ResponseDictUtil # noqa: E402
46-
55+
ResponseDictUtil = import_version(VERSION, "model_dicts").ResponseDictUtil
4756
t_tom = time.perf_counter() - t0
4857

4958
section("Imports")
50-
print(f" model_dicts ResponseDictUtil {VALUE_FMT.format(t_tom)}")
59+
print(
60+
f" translator_tom {VERSION} model_dicts ResponseDictUtil {VALUE_FMT.format(t_tom)}"
61+
)
5162

5263

5364
TEST_FILES = discover_files(CORPUS_ROOT)
@@ -101,8 +112,7 @@ def section(title: str) -> None:
101112
section("Summary (seconds): from / to")
102113

103114
short_labels = {
104-
lbl: lbl.split("/")[-1].removesuffix(".gz").removesuffix(".json")
105-
for lbl in results
115+
lbl: lbl.split("/")[-1].removesuffix(".gz").removesuffix(".json") for lbl in results
106116
}
107117
ops = list(next(iter(results.values())).keys())
108118

bench/test_semantic_validation.py

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,26 @@
11
"""Semantic-validation benchmark across every example response.
22
3-
Walks `data/example_trapi/**`, deserializes each file, and runs
4-
`semantic_validate` on the resulting `Response`. Streams per-file timings and
5-
the error/warning counts as they complete, then prints a summary table across
6-
files at the end.
3+
Walks `data/example_trapi/<version>/**` (default `2.0`; pass `--version 1.6`),
4+
deserializes each file, and runs `semantic_validate` on the resulting `Response`.
5+
Streams per-file timings and the error/warning counts as they complete, then prints
6+
a summary table across files at the end.
77
88
For the serdes benchmarks see `bench/test_sd_tom.py` (TOM-only, every file) and
99
`bench/test_sd.py` (one file per size bucket, TOM vs reasoner-pydantic).
1010
"""
1111

1212
import time
1313

14-
from utils import CORPUS_ROOT, discover_files, read_corpus_file
14+
from utils import (
15+
corpus_root,
16+
discover_files,
17+
import_version,
18+
parse_version,
19+
read_corpus_file,
20+
)
21+
22+
VERSION = parse_version(__doc__)
23+
CORPUS_ROOT = corpus_root(VERSION)
1524

1625
VALUE_FMT = "{:>8.4f}s"
1726

@@ -24,13 +33,14 @@ def section(title: str) -> None:
2433
# --- Import ---
2534

2635
t0 = time.perf_counter()
27-
from translator_tom import Response # noqa: E402
28-
from translator_tom.validation import semantic_validate # noqa: E402
29-
36+
_ttom = import_version(VERSION)
37+
_validation = import_version(VERSION, "validation")
3038
t_tom = time.perf_counter() - t0
39+
Response = _ttom.Response
40+
semantic_validate = _validation.semantic_validate
3141

3242
section("Imports")
33-
print(f" translator_tom + validation {VALUE_FMT.format(t_tom)}")
43+
print(f" translator_tom {VERSION} + validation {VALUE_FMT.format(t_tom)}")
3444

3545

3646
TEST_FILES = discover_files(CORPUS_ROOT)

0 commit comments

Comments
 (0)