@pharmatools/opengate 0.2.0 → 0.3.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.
package/README.md CHANGED
@@ -124,7 +124,7 @@ Scorers never talk to a system directly — they go through an adapter, injected
124
124
  OPENGATE_ADAPTER=./adapters/my-system.mjs npm run eval:online
125
125
  ```
126
126
 
127
- An adapter is one file with four required exports — `splitClaims(text)`, `analyzeBatch(payload)`, `onlineAvailable()`, `onlineConfigHint()` plus optional timing/token/model-label hooks that unlock latency and cost columns in the scorecard. Adapters are validated at load: a malformed one fails fast with a message listing every missing export.
127
+ An adapter is one file: two base exports — `onlineAvailable()`, `onlineConfigHint()` — plus at least one complete **capability**: `qa` (`splitClaims` + `analyzeBatch`) or `redaction` (`redact`). Scorers check `adapter.capabilities` and skip cleanly across the boundary. Optional timing/token/model-label hooks unlock latency and cost columns in the scorecard. Adapters are validated at load: a malformed one fails fast with a message naming every missing export and incomplete capability.
128
128
 
129
129
  For REST-backed systems there's a no-code path: the bundled **generic HTTP adapter** (`src/adapters/http.mjs`) reads endpoint paths and headers from `opengate.http.json` (see `opengate.http.example.json`), with `${ENV}` interpolation and built-in latency/token capture.
130
130
 
@@ -137,7 +137,7 @@ Full contract, minimal skeleton, and verdict-mapping notes: **[ADAPTERS.md](ADAP
137
137
  Use OpenGATE as a drop-in regression gate in any repository. Keep your gold set and committed `baseline.json` in your own tree; any metric that drops fails the build:
138
138
 
139
139
  ```yaml
140
- - uses: nickjlamb/opengate@main
140
+ - uses: nickjlamb/opengate@v0
141
141
  with:
142
142
  datasets: ./evals/datasets # your cases/ + fixtures/
143
143
  results: ./evals/results # where baseline.json lives
@@ -169,7 +169,7 @@ npm install --no-save @pharmatools/redacta
169
169
  node src/runner.mjs --online --adapter ./src/adapters/redacta.mjs
170
170
  ```
171
171
 
172
- On its first run against the new gold set, the eval found two real engine bugs — a relative name directly followed by a parenthesis escapes the name pattern, and apostrophe surnames are dropped from titled-name capture both now documented as tracked known gaps in the case files. Current scorecard: **100% recall on 23 gold identifiers, 0 leaks, 3 tracked gaps**.
172
+ On its first run against the new gold set, the eval found two real engine bugs — relation phrases like "Next of kin:" swallowed nested name matches, and apostrophe surnames (O'Brien) were dropped from name capture. Both were fixed in `@pharmatools/redacta` 1.2.1 and confirmed by the eval (`knownGap_closed: 2`), then promoted to gold. Street-line address detection followed in 1.3.0, closing the last tracked gap. Current scorecard: **100% recall on 25 gold identifiers, 0 leaks, no open gaps**.
173
173
 
174
174
  ## Layout
175
175
 
@@ -191,7 +191,7 @@ opengate/
191
191
 
192
192
  ## Roadmap
193
193
 
194
- - **Redacta engine fixes** — two bugs found by the redaction eval (paren-adjacent relative names; apostrophe surnames in titled-name capture) are tracked as known gaps; fixing them in `@pharmatools/redacta` flips `knownGap_closed` and promotes the cases to gold
194
+
195
195
  - **Third adapter** — Patiently AI (faithfulness evaluation for patient-language simplification)
196
196
  - **Author-year in RefCheckr production** — `detectAuthorYear()` now lands "Smith 2020"-style keys in the reference implementation; adopting them in RefCheckr's numeric-keyed citation mapping is tracked separately
197
197
  - **Number-adjacent superscript** — `week 24.1` is genuinely ambiguous with decimals; remains a tracked known gap
@@ -7,7 +7,7 @@ One JSON file per case in `datasets/cases/` (files starting with `_` are ignored
7
7
  | `id` | yes | all | Unique slug. |
8
8
  | `title` / `notes` | no | — | Human context / provenance. |
9
9
  | `manuscript` | yes | claim-extraction | The pasted section, with citation markers exactly as authored. |
10
- | `goldClaims[]` | yes | citation-detection, claim-extraction | The verifiable claims a reviewer should extract. Each has `originalText` (with markers), `text` (clean), and `citations` (number array). |
10
+ | `goldClaims[]` | yes | citation-detection, claim-extraction | The verifiable claims a reviewer should extract. Each has `originalText` (with markers), `text` (clean), and `citations` — an array of numbers and/or author-year string keys (e.g. "Smith 2020", "Meyer 2020a"). Numeric [N] markers are stripped from `text`; author-year mentions are grammatical prose and are never stripped, so for pure author-year claims `text === originalText`. |
11
11
  | `goldNonClaims[]` | no | claim-extraction | Sentences that should **not** be extracted (background, aims, transitions). Drives precision/leakage. |
12
12
  | `references{}` | online only | verdict-accuracy | Map of citation-number → `{ name, text }`. |
13
13
  | `goldVerdicts[]` | online only | verdict-accuracy | `{ claimText, citation, verdict }` where `verdict` ∈ the six-point scale. Mark `_requires: "online"`. |
@@ -0,0 +1,56 @@
1
+ {
2
+ "id": "case-authoryear-harvard",
3
+ "title": "Author-year (Harvard) citation style — thyroid eye disease",
4
+ "notes": "SYNTHETIC manuscript exercising pure author-year citations. Unlike numeric [N] markers, author-year mentions are grammatical prose and are never stripped: text === originalText.",
5
+ "manuscript": "Teprotumumab reduced proptosis by 2.82 mm versus placebo (Douglas et al., 2020). Smith and Jones (2019) reported durable responses at 52 weeks in an open-label extension. The safety profile was consistent with earlier findings (Kim, 2023). This study aimed to characterise long-term outcomes in routine practice.",
6
+ "goldClaims": [
7
+ {
8
+ "originalText": "Teprotumumab reduced proptosis by 2.82 mm versus placebo (Douglas et al., 2020).",
9
+ "text": "Teprotumumab reduced proptosis by 2.82 mm versus placebo (Douglas et al., 2020).",
10
+ "citations": [
11
+ "Douglas 2020"
12
+ ]
13
+ },
14
+ {
15
+ "originalText": "Smith and Jones (2019) reported durable responses at 52 weeks in an open-label extension.",
16
+ "text": "Smith and Jones (2019) reported durable responses at 52 weeks in an open-label extension.",
17
+ "citations": [
18
+ "Smith 2019"
19
+ ]
20
+ },
21
+ {
22
+ "originalText": "The safety profile was consistent with earlier findings (Kim, 2023).",
23
+ "text": "The safety profile was consistent with earlier findings (Kim, 2023).",
24
+ "citations": [
25
+ "Kim 2023"
26
+ ]
27
+ }
28
+ ],
29
+ "goldNonClaims": [
30
+ "This study aimed to characterise long-term outcomes in routine practice."
31
+ ],
32
+ "references": {
33
+ "Douglas 2020": {
34
+ "name": "douglas-2020-nejm.txt",
35
+ "text": "In this randomised trial of teprotumumab for thyroid eye disease, the least-squares mean reduction in proptosis was 2.82 mm greater with teprotumumab than with placebo at week 24."
36
+ },
37
+ "Smith 2019": {
38
+ "name": "smith-2019-extension.txt",
39
+ "text": "In the 52-week open-label extension, proptosis responses were maintained in the majority of participants, indicating durable treatment effect."
40
+ }
41
+ },
42
+ "goldVerdicts": [
43
+ {
44
+ "_requires": "online",
45
+ "claimText": "Teprotumumab reduced proptosis by 2.82 mm versus placebo (Douglas et al., 2020).",
46
+ "citation": "Douglas 2020",
47
+ "verdict": "strong_support"
48
+ },
49
+ {
50
+ "_requires": "online",
51
+ "claimText": "Smith and Jones (2019) reported durable responses at 52 weeks in an open-label extension.",
52
+ "citation": "Smith 2019",
53
+ "verdict": "strong_support"
54
+ }
55
+ ]
56
+ }
@@ -0,0 +1,48 @@
1
+ {
2
+ "id": "case-authoryear-mixed",
3
+ "title": "Mixed numeric + author-year citations in one manuscript",
4
+ "notes": "SYNTHETIC. Exercises numeric superscript and author-year styles side by side, an apostrophe surname (O'Connor), and a 2020a/2020b suffix pair that must stay distinct.",
5
+ "manuscript": "Adalimumab improved ACR20 response rates versus placebo.1,2 O'Connor et al. (2021) confirmed the effect in a real-world cohort. Retention was higher with combination therapy (Meyer 2020a; Meyer 2020b). Baseline characteristics are summarised in Table 1.",
6
+ "goldClaims": [
7
+ {
8
+ "originalText": "Adalimumab improved ACR20 response rates versus placebo.1,2",
9
+ "text": "Adalimumab improved ACR20 response rates versus placebo.",
10
+ "citations": [
11
+ 1,
12
+ 2
13
+ ]
14
+ },
15
+ {
16
+ "originalText": "O'Connor et al. (2021) confirmed the effect in a real-world cohort.",
17
+ "text": "O'Connor et al. (2021) confirmed the effect in a real-world cohort.",
18
+ "citations": [
19
+ "O'Connor 2021"
20
+ ]
21
+ },
22
+ {
23
+ "originalText": "Retention was higher with combination therapy (Meyer 2020a; Meyer 2020b).",
24
+ "text": "Retention was higher with combination therapy (Meyer 2020a; Meyer 2020b).",
25
+ "citations": [
26
+ "Meyer 2020a",
27
+ "Meyer 2020b"
28
+ ]
29
+ }
30
+ ],
31
+ "goldNonClaims": [
32
+ "Baseline characteristics are summarised in Table 1."
33
+ ],
34
+ "references": {
35
+ "O'Connor 2021": {
36
+ "name": "oconnor-2021-cohort.txt",
37
+ "text": "In this prospective real-world cohort of 412 patients with rheumatoid arthritis, adalimumab was associated with significantly improved ACR20 response rates compared with historical placebo controls."
38
+ }
39
+ },
40
+ "goldVerdicts": [
41
+ {
42
+ "_requires": "online",
43
+ "claimText": "O'Connor et al. (2021) confirmed the effect in a real-world cohort.",
44
+ "citation": "O'Connor 2021",
45
+ "verdict": "strong_support"
46
+ }
47
+ ]
48
+ }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "id": "redact-cardio-clinic",
3
3
  "kind": "redaction",
4
- "description": "Cardiology clinic letter — titled patient name, NHS number, DOB, phone, email, postcode. SYNTHETIC: all identifiers are test-range values; no real persons.",
4
+ "description": "Cardiology clinic letter — titled patient name, NHS number, DOB, phone, email, postcode. SYNTHETIC: all identifiers are test-range values; no real persons. The street address was a tracked engine gap; street-line detection landed in @pharmatools/redacta 1.3.0 and the entity was promoted to gold.",
5
5
  "text": "Mr John Smith (NHS 999 000 0018, DOB 12/03/1958) attended cardiology clinic today. He was seen by Dr Patel for review of atrial fibrillation. Blood pressure was 132/84 mmHg and apixaban was continued at 5 mg BD. Contact him on 07700 900123 or john.smith58@example.com. Address on file: 42 Maple Road, Oxford OX2 6JX. Follow-up in 12 weeks.",
6
6
  "goldEntities": [
7
7
  {
@@ -27,12 +27,11 @@
27
27
  {
28
28
  "type": "POSTCODE",
29
29
  "value": "OX2 6JX"
30
- }
31
- ],
32
- "knownGapEntities": [
30
+ },
33
31
  {
34
32
  "type": "STREET_ADDRESS",
35
33
  "value": "42 Maple Road"
36
34
  }
37
- ]
38
- }
35
+ ],
36
+ "knownGapEntities": []
37
+ }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "id": "redact-discharge-summary",
3
3
  "kind": "redaction",
4
- "description": "Discharge summary with a relative as next of kin. SYNTHETIC.",
4
+ "description": "Discharge summary with a relative as next of kin. SYNTHETIC. The relative name was a tracked engine gap (a 'Next of kin:' phrase swallowed the inner 'daughter Anita' match — not the parenthesis, as first suspected); fixed in @pharmatools/redacta >1.2.0 and promoted to gold.",
5
5
  "text": "Discharge summary for Mrs Priya Sharma, NHS 999 000 0026, DOB 04/11/1947. Admitted with community-acquired pneumonia; completed 5 days of co-amoxiclav and improved steadily. Next of kin: her daughter Anita (07700 900456) was updated by phone. Discharged to home address, postcode LS8 2QT, with district nurse follow-up.",
6
6
  "goldEntities": [
7
7
  {
@@ -23,13 +23,11 @@
23
23
  {
24
24
  "type": "POSTCODE",
25
25
  "value": "LS8 2QT"
26
- }
27
- ],
28
- "knownGapEntities": [
26
+ },
29
27
  {
30
28
  "type": "RELATIVE_NAME",
31
- "value": "Anita",
32
- "comment": "Engine bug (tracked): a relative name directly followed by an opening parenthesis escapes the relative-name pattern — 'her daughter Anita (07700...' leaks, while 'her daughter Anita was' is caught. The phone match also consumes the opening paren, leaving an orphan ')'."
29
+ "value": "Anita"
33
30
  }
34
- ]
31
+ ],
32
+ "knownGapEntities": []
35
33
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "id": "redact-ward-round",
3
3
  "kind": "redaction",
4
- "description": "Ward round entry — apostrophe surname, NHS number without spaces, carer mention. SYNTHETIC.",
4
+ "description": "Ward round entry — apostrophe surname, NHS number without spaces, carer mention. SYNTHETIC. The apostrophe surname was a tracked engine gap (NAME pattern required a lowercase letter after the initial capital); fixed in @pharmatools/redacta >1.2.0.",
5
5
  "text": "Ward round: Mrs Eileen O'Brien, NHS 9990000034, remains stable post hip hemiarthroplasty. Mobilising with frame. Her son Daniel visited and raised concerns about home support; referred to social work. Plan: continue enoxaparin, orthogeriatric review tomorrow. Contact for family updates: 07700 900321.",
6
6
  "goldEntities": [
7
7
  {
@@ -21,11 +21,5 @@
21
21
  "value": "07700 900321"
22
22
  }
23
23
  ],
24
- "knownGapEntities": [
25
- {
26
- "type": "PATIENT_SURNAME",
27
- "value": "O'Brien",
28
- "comment": "Engine bug (tracked): apostrophe surnames are dropped from titled-name capture — 'Mrs Eileen O'Brien' tokenises as 'Mrs Eileen' only, leaving the surname in the text."
29
- }
30
- ]
24
+ "knownGapEntities": []
31
25
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pharmatools/opengate",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "type": "module",
5
5
  "description": "OpenGATE — Open Grounded AI Testing & Evaluation. An open-source framework for evaluating evidence-grounded AI systems.",
6
6
  "license": "MIT",
@@ -69,7 +69,9 @@ const ETAL = String.raw`(?:et al\.?|and colleagues|and coworkers)`;
69
69
  /** Detect author-year citations in raw text; returns sorted unique keys. */
70
70
  export function detectAuthorYear(text) {
71
71
  const found = new Set();
72
- const add = (name, year) => found.add(`${name} ${year.slice(0, 4)}`);
72
+ // Year suffixes are kept: "Smith 2020a" and "Smith 2020b" are distinct
73
+ // references in author-year styles.
74
+ const add = (name, year) => found.add(`${name} ${year}`);
73
75
 
74
76
  // 1. Narrative with parenthetical year: "Smith et al. (2020)",
75
77
  // "Smith and Jones (2019)", "Smith (2020)".