Skip to content

Commit 4f77afd

Browse files
docs+ui: cite every source in use; surface behavior & safety in the demo (#59)
Sources (SOURCES.md): full attribution for everything we actually pull in/use — PubChem (name/CAS resolution + experimental BP/VP + CAS→InChIKey; public domain, no commercial restriction), FDA SAF (GRAS, public domain), thermo (Joback eval, rejected; 33 °C mean error), NIST GC/RI (paid, not used), Tox21 (public domain, clean candidate, not yet built). BitterDB corrected: CC-BY-NC → academic only. FEMA use levels noted as copyrighted (data-gated). Workbench: new "Behavior & safety" card surfaces what predict.py already returns but the UI hid — MW/logP, measured boiling point / vapor pressure (when the PubChem table is built), volatility tier, food-use (GRAS) status, preliminary TTC tier, and structural-alert caution chips. Defensive framing kept ("flags for review, never a clearance"). Signed-off-by: Austin L. <86896075+rvnminers-A-and-N@users.noreply.github.com>
1 parent d090340 commit 4f77afd

2 files changed

Lines changed: 50 additions & 7 deletions

File tree

docs/SOURCES.md

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,9 @@ record of provenance, not legal advice. Get an IP/OSS-license review before ship
1818
- **NumPy**, **pandas** — arrays + dataframes. (BSD-3-Clause.)
1919
- **UMAP** — 2D odor/flavor-space map. (BSD-3-Clause.)
2020
- **skl2onnx** — exports the sklearn taste models to ONNX. (Apache-2.0.)
21-
- *(thermo — evaluated for Joback boiling point, then REJECTED for accuracy; not shipped. MIT.)*
21+
- **thermo** — used to evaluate Joback structure-based boiling point; **REJECTED** (mean
22+
abs error 33 °C across 12 flavor molecules, max 89 °C). Not shipped. (MIT.)
23+
- **PubChem / FDA SAF** — data sources for measured properties + GRAS; cited under *Data sources*.
2224

2325
**Product / serving side**
2426
- **ONNX** + **ONNX Runtime** — run taste models in-process in .NET. (MIT.)
@@ -39,17 +41,30 @@ record of provenance, not legal advice. Get an IP/OSS-license review before ship
3941
- **FartDB** — FartLabs (HuggingFace), ~31k tastants behind the 2025 *npj Science of Food* "FART" model — the largest public tastant set. Repo labeled **MIT**, but it **composites FlavorDB (CC-BY-NC-SA) + SciFinder (CAS, proprietary) + Tas2R-DB (ACS)**; the MIT badge cannot relicense data the authors don't own. **Excluded** (NonCommercial/proprietary upstream) — the same "downloadable ≠ usable" trap as OpenPOM's GS-LF.
4042
- **SweetenersDB v2.0** — Bouysset et al. (2020) *Food Chem.*, building on Chéron et al. (2017); relative-to-sucrose sweetness *intensity* for the regressor. Released **MIT** by the authors' own lab (ChemSenSim, `github.com/chemosim-lab/SweetenersDB`) — the paywall is only on the journal article, not the authors' own data. **In use.**
4143
- **PlantMolecularTasteDB** — Gradinaru et al. (2022), *Frontiers in Pharmacology* (DOI 10.3389/fphar.2021.751712); 1,527 taste-active phytochemicals. The **article** is CC-BY, but the **database** is web-only (plantmoleculartastedb.org) with **no open data license** and an **i-Depot** IP/authorship registration by the authors — article-CC-BY does **not** extend to the database. **Excluded** pending an explicit commercial grant; article-CC-BY ≠ database-CC-BY (same lesson as Pyrfume/OpenPOM).
42-
- **BitterDB** *(future)* — bitterness intensity, if/when added.
44+
- **BitterDB** — Niv lab (Hebrew U), >2,200 bitter compounds. License **CC BY-NC 3.0
45+
(NonCommercial)** — commercial use needs a separate license. **Excluded from the
46+
commercial edition**; a candidate for the **academic edition's** bitterness-intensity head.
4347
- **UMP442 / BIOPEP-UWM** — umami references. BIOPEP-UWM is web-only; the GitHub repost `Shoombuatong/Dataset-Code` carries **no license** (all-rights-reserved) and is umami *peptide* data (a different class from our small-molecule head). **Not used.**
4448

4549
**Aroma**
4650
- **Pyrfume** + **Leffingwell / GoodScents (GS-LF)** odor datasets — the usual training data behind OpenPOM, but **RESTRICTED and NOT USED**: Leffingwell's manifest cites use restrictions (*John Leffingwell & Google*); GoodScents/Arctander/Flavornet (© Datu Inc.) are likewise proprietary. We **exclude all of them** (the demo may go to a customer / commercial use). The aroma model will use only commercial-clean **open** odor data (CC-BY sets like `keller_2016`; smaller — see `DATA-SOURCES.md`). The OpenPOM *code* is MIT.
4751
- **keller_2016** — Keller & Vosshall (2016), *BMC Neuroscience*, **CC-BY-4.0**; ~480 molecules with naive-subject odor-descriptor ratings. The only commercially-clean odor-descriptor set — evaluated for the aroma model and found too noisy to learn from (CV-R² ≤ 0 across all 20 descriptors; see `docs/AROMA.md`).
4852

49-
**Safety / regulatory (lookups — data-gated)**
50-
- **FEMA GRAS list** — usual/maximum use levels for the dosing analyzer. (FEMA.)
53+
**Safety / regulatory / properties**
54+
- **FDA "Substances Added to Food" (SAF)** — the GRAS / recognized-food-ingredient
55+
cross-check. **US-government work, public domain.** In use via `build_gras_reference.py`.
56+
- **FEMA usual/maximum use levels** — for *quantitative* dosing. Published in FEMA's
57+
copyrighted GRAS papers — **not freely available in bulk**; data-gated (customer/licensed).
5158
- **EU declarable fragrance/flavor allergen annex** — the labeling flags. (EU regulation.)
52-
- **PubChem** — name↔SMILES↔CID resolution. (Public domain data; confirm API terms.)
59+
- **PubChem** (NIH/NCBI) — used three ways: name/CAS↔SMILES↔CID resolution; experimental
60+
**boiling point / vapor pressure** (`build_properties.py`); and CAS→InChIKey for GRAS
61+
(`build_gras_reference.py`). **Public domain — no restriction on use, incl. commercial**
62+
(attribution appreciated).
63+
- **NIST GC/RI library** — Kovats retention indices. **PAID** (~$595 data-only FTP to
64+
~$2,850 single-seat) — *not used* (paywall); RI deferred to customer/licensed data. (RI
65+
is **not** in PubChem — verified by probe.)
66+
- **Tox21** (NIH/NCATS · EPA · FDA · NTP) — public toxicity-assay data (public domain, on
67+
PubChem / data.gov). A **clean candidate** for caution-only tox flags; **not yet built**.
5368

5469
---
5570

training/workbench.html

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,9 @@
6666
.aroma-lead b{color:var(--aroma)}
6767
.aroma-note{font-size:13px;color:var(--muted);margin:0;line-height:1.55;max-width:760px}
6868
.aroma-note b{color:var(--ink)}
69+
.kv{display:grid;grid-template-columns:auto 1fr;gap:6px 16px;margin-bottom:10px}
70+
.kv .k{font-weight:600;font-size:13px}
71+
.kv .v{font-family:var(--mono);font-size:13px;color:var(--muted)}
6972
</style>
7073
</head>
7174
<body>
@@ -97,6 +100,11 @@ <h2>Substitution candidates</h2>
97100
</div>
98101
</div>
99102

103+
<div class="card" id="behaviorCard" style="display:none">
104+
<h2>Behavior &amp; safety</h2>
105+
<div id="behavior"></div>
106+
</div>
107+
100108
<div class="card aroma-card" id="aromaCard" style="display:none">
101109
<h2>Aroma</h2>
102110
<div class="aroma-lead">Aroma prediction unlocks with <b>your</b> data.</div>
@@ -152,7 +160,7 @@ <h2>Aroma</h2>
152160
if(p.error){ throw new Error(p.error); }
153161
render(p, n.neighbors||[]);
154162
}catch(e){
155-
$('results').style.display='none'; $('aromaCard').style.display='none'; $('footnote').style.display='none';
163+
$('results').style.display='none'; $('behaviorCard').style.display='none'; $('aromaCard').style.display='none'; $('footnote').style.display='none';
156164
$('err').textContent = e.message; $('err').style.display='block';
157165
}finally{
158166
go.disabled=false; go.textContent='Read flavor';
@@ -191,12 +199,32 @@ <h2>Aroma</h2>
191199
</div>`).join('');
192200
}
193201

194-
$('results').style.display='grid'; $('aromaCard').style.display='block'; $('footnote').style.display='block';
202+
renderBehavior(p);
203+
$('results').style.display='grid'; $('behaviorCard').style.display='block'; $('aromaCard').style.display='block'; $('footnote').style.display='block';
195204
// animate bar fills
196205
requestAnimationFrame(()=>document.querySelectorAll('.fill')
197206
.forEach(f=>f.style.width=f.dataset.w+'%'));
198207
}
199208

209+
function renderBehavior(p){
210+
const pc = p.physchem||{}, sf = p.safety||{};
211+
const comp = pc.computed||{}, meas = pc.measured||{}, qual = pc.qualitative||{};
212+
const MEAS = ' <span class="tag measured">measured</span>';
213+
const rows = [];
214+
if(typeof comp.mol_weight==='number') rows.push(['MW', comp.mol_weight+' g/mol']);
215+
if(typeof comp.logP==='number') rows.push(['logP', comp.logP]);
216+
if(typeof meas.boiling_point_c==='number') rows.push(['Boiling point', meas.boiling_point_c+' °C'+MEAS]);
217+
if(qual.aroma_volatility) rows.push(['Volatility', qual.aroma_volatility]);
218+
if(typeof meas.vapor_pressure_pa==='number') rows.push(['Vapor pressure', meas.vapor_pressure_pa+' Pa'+MEAS]);
219+
if(sf.gras_status) rows.push(['Food-use status', sf.gras_status]);
220+
if(sf.ttc_hint && sf.ttc_hint.preliminary_tier) rows.push(['TTC tier (prelim.)', sf.ttc_hint.preliminary_tier]);
221+
let html = '<div class="kv">'+rows.map(r=>`<div class="k">${r[0]}</div><div class="v">${r[1]}</div>`).join('')+'</div>';
222+
const alerts = sf.structural_alerts||[];
223+
if(alerts.length) html += '<div class="chips">'+alerts.map(a=>`<span class="chip" style="background:#FBEDE9;color:#8A3A22;border-color:#E7C4B8">⚠ ${a}</span>`).join('')+'</div>';
224+
html += '<div class="empty" style="font-size:12px">Defensive screen — flags for review, never a clearance.</div>';
225+
$('behavior').innerHTML = html;
226+
}
227+
200228
function chip(key, on, color, label){
201229
const style = on && color ? `style="background:${color}"` : '';
202230
return `<span class="chip ${on?'on':''}" ${style}>${label}</span>`;

0 commit comments

Comments
 (0)