Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@ jobs:
- name: Install ruff
run: pip install "ruff==0.15.15"
- name: Ruff check
run: ruff check src/bluenamer
run: ruff check src/openclatura
- name: Ruff format check
run: ruff format --check src/bluenamer
run: ruff format --check src/openclatura

test:
runs-on: ubuntu-latest
Expand Down Expand Up @@ -107,17 +107,17 @@ jobs:
context: .
push: false
load: true
tags: bluenamer:ci
tags: openclatura:ci

- name: Smoke-test the image
run: |
docker run -d --name bluenamer-ci -p 8000:8000 bluenamer:ci
docker run -d --name openclatura-ci -p 8000:8000 openclatura:ci
for i in $(seq 1 30); do
if curl -fsS http://127.0.0.1:8000/healthz > /tmp/health.json; then
cat /tmp/health.json
exit 0
fi
sleep 2
done
docker logs bluenamer-ci
docker logs openclatura-ci
exit 1
6 changes: 3 additions & 3 deletions .github/workflows/docker-image.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,14 @@ jobs:
with:
context: .
load: true
tags: bluenamer:test
tags: openclatura:test

- name: Smoke test Docker image
run: |
docker run --rm -d -p 8000:8000 --name bluenamer-test bluenamer:test
docker run --rm -d -p 8000:8000 --name openclatura-test openclatura:test
sleep 5
curl --fail http://localhost:8000/healthz
docker stop bluenamer-test
docker stop openclatura-test

- name: Build and push image from main
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
Expand Down
8 changes: 4 additions & 4 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,13 @@ RUN apt-get update -qq \
COPY --from=builder /install /usr/local

# Non-root user.
RUN useradd --create-home --shell /bin/bash bluenamer
USER bluenamer
WORKDIR /home/bluenamer
RUN useradd --create-home --shell /bin/bash openclatura
USER openclatura
WORKDIR /home/openclatura

EXPOSE 8000

HEALTHCHECK --interval=30s --timeout=5s --retries=3 \
CMD curl -fsS http://127.0.0.1:8000/healthz || exit 1

CMD ["python", "-m", "bluenamer.web", "--host", "0.0.0.0", "--port", "8000"]
CMD ["python", "-m", "openclatura.web", "--host", "0.0.0.0", "--port", "8000"]
49 changes: 24 additions & 25 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
# bluenamer
# openclatura

A deterministic SMILES → IUPAC name generator derived from the rules of the
IUPAC Blue Book (2013 recommendations).
**Open Nomenclature Framework**

The package walks the molecular graph (parsed via RDKit), perceives functional
groups and ring systems, selects a principal parent, assigns locants, and
assembles the substitutive name. Every step is recorded in an inspectable
`openclatura` is a deterministic SMILES-to-IUPAC name generator inspired by the IUPAC Blue Book 2013 recommendations.

Built on top of RDKit, the package walks the molecular graph, detects functional groups and ring systems, selects the principal parent, assigns locants, and constructs the corresponding substitutive IUPAC name. Every step is recorded in an inspectable
decision trace so the *why* of a name is recoverable, not just the *what*.

> **Status:** alpha. The naming engine handles a broad slice of organic
Expand All @@ -16,7 +15,7 @@ decision trace so the *why* of a name is recoverable, not just the *what*.
## Install

```bash
pip install bluenamer
pip install openclatura
```

Optional extras:
Expand All @@ -29,13 +28,13 @@ Optional extras:
| `[dev]` | pytest, ruff, pre-commit, hypothesis, py2opsin |

```bash
pip install "bluenamer[opsin,datasets]"
pip install "openclatura[opsin,datasets]"
```

## Quick start

```python
from bluenamer import name_smiles
from openclatura import name_smiles

name_smiles("CCO") # 'ethanol'
name_smiles("c1ccccc1") # 'benzene'
Expand All @@ -44,10 +43,10 @@ name_smiles("CC(=O)O") # 'acetic acid'

### Typed result with rules hit + OPSIN round-trip

For everything richer than the bare string, use `bluenamer.name`:
For everything richer than the bare string, use `openclatura.name`:

```python
from bluenamer import name
from openclatura import name

result = name("CC(=O)Nc1ccccc1", include_trace=True, verify_opsin=True)

Expand All @@ -64,7 +63,7 @@ Errors do not raise — they are captured on `result.error`, which makes
the batch API safe to point at noisy datasets:

```python
from bluenamer import name_many
from openclatura import name_many

results = name_many(
["CCO", "c1ccccc1", "definitely-not-a-smiles"],
Expand All @@ -78,7 +77,7 @@ For the full decision trace (one `TraceStep` per phase: parse, perception,
parent selection, numbering, assembly, …):

```python
from bluenamer import analyze_smiles
from openclatura import analyze_smiles

analysis = analyze_smiles("CC(=O)Nc1ccccc1")
for step in analysis.decisions:
Expand All @@ -87,25 +86,25 @@ for step in analysis.decisions:
### CLI

```bash
bluenamer name "CC(=O)Nc1ccccc1" # → N-phenylacetamide
bluenamer name "CC(=O)Nc1ccccc1" --json # JSON with trace + rules
bluenamer batch smiles.txt --output names.jsonl --processes auto
openclatura name "CC(=O)Nc1ccccc1" # → N-phenylacetamide
openclatura name "CC(=O)Nc1ccccc1" --json # JSON with trace + rules
openclatura batch smiles.txt --output names.jsonl --processes auto
```
The CLI tool has OPSIN verification turned on by default. It can be turned off with

```bash
bluenamer name "CN1C=NC2=C1C(=O)N(C(=O)N2C)C" --no-verify
openclatura name "CN1C=NC2=C1C(=O)N(C(=O)N2C)C" --no-verify
```

### Natural-language description (`describe`)

`bluenamer.describe(smiles)` walks the same trace and renders a
`openclatura.describe(smiles)` walks the same trace and renders a
deterministic, multi-paragraph explanation of how the name is built.
Useful for explainability views and for generating (SMILES, name,
description) training tuples:

```python
from bluenamer import describe
from openclatura import describe

d = describe("CC(=O)Nc1ccccc1")
print(d) # multi-paragraph prose
Expand All @@ -117,10 +116,10 @@ Same input → same output. No LLM in the loop.

## Human-like description

Bluenamer can generate uncanny human-like descriptions of molecules.
Openclatura can generate uncanny human-like descriptions of molecules.
```python

from bluenamer import describe_human
from openclatura import describe_human

d = describe_human("CN1C=NC2=C1C(=O)N(C(=O)N2C)C")
print(d.text)
Expand Down Expand Up @@ -155,8 +154,8 @@ pytest -m "not slow and not dataset and not golden"
pytest -m golden

# lint and format
ruff check --fix src/bluenamer
ruff format src/bluenamer
ruff check --fix src/openclatura
ruff format src/openclatura
```

Java is required for the OPSIN-based round-trip checks (see `py2opsin`).
Expand All @@ -169,8 +168,8 @@ so `verify_opsin=True` works out of the box.

```bash
# build + run
docker build -t bluenamer:local .
docker run --rm -p 8000:8000 bluenamer:local
docker build -t openclatura:local .
docker run --rm -p 8000:8000 openclatura:local

# or via compose
docker compose -f docker/compose.yaml up --build
Expand Down
4 changes: 2 additions & 2 deletions docker/compose.yaml
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
services:
bluenamer:
openclatura:
build:
context: ..
dockerfile: Dockerfile
image: bluenamer:local
image: openclatura:local
ports:
- "8000:8000"
restart: unless-stopped
Expand Down
2 changes: 1 addition & 1 deletion examples/eval_via_opsin.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from rdkit.Chem import CanonSmiles
from tqdm import tqdm

from bluenamer.namer import name_smiles
from openclatura.namer import name_smiles


def canon(smi):
Expand Down
6 changes: 3 additions & 3 deletions examples/find_small_failures.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@
from datasets import load_dataset
from tqdm import tqdm

from bluenamer.namer import name_smiles
from bluenamer.resonance_compare import equivalent_smiles
from bluenamer.utils import standardize_mol
from openclatura.namer import name_smiles
from openclatura.resonance_compare import equivalent_smiles
from openclatura.utils import standardize_mol


def canon(smi):
Expand Down
4 changes: 2 additions & 2 deletions examples/opsin_eval_ZINC22.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@
from huggingface_hub import HfApi, hf_hub_url
from tqdm import tqdm

from bluenamer.namer import name_smiles
from bluenamer.utils import standardize_mol
from openclatura.namer import name_smiles
from openclatura.utils import standardize_mol

# --- Configuration ---
ZINC22_REPO_ID = "chandar-lab/ZINC_22"
Expand Down
4 changes: 2 additions & 2 deletions examples/opsin_eval_pubchem.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
from datasets import load_dataset
from tqdm import tqdm

from bluenamer.namer import name_smiles
from bluenamer.utils import standardize_mol
from openclatura.namer import name_smiles
from openclatura.utils import standardize_mol

# --- Configuration ---
N_PER_SEED = 100_000
Expand Down
2 changes: 1 addition & 1 deletion examples/qm9_iupac_collect_token_confidence.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
from datasets import load_dataset
from tqdm import tqdm

from bluenamer import DEFAULT_NAMING_ENGINE, NamingRequest
from openclatura import DEFAULT_NAMING_ENGINE, NamingRequest

# --- Configuration ---
N_TEST = 5_000
Expand Down
2 changes: 1 addition & 1 deletion examples/random_sanity.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from bluenamer.namer import name_smiles
from openclatura.namer import name_smiles

TESTS = [
{
Expand Down
2 changes: 1 addition & 1 deletion examples/sanity_examples.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from bluenamer.namer import name_smiles
from openclatura.namer import name_smiles

tests = {
"1. The 'Everything' Acyclic Chain": {
Expand Down
4 changes: 2 additions & 2 deletions examples/test_opsin_mac.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
from datasets import load_dataset
from tqdm import tqdm

from bluenamer.namer import name_smiles
from bluenamer.utils import standardize_mol
from openclatura.namer import name_smiles
from openclatura.utils import standardize_mol

# --- Configuration ---
N_TEST = 1000_000
Expand Down
2 changes: 1 addition & 1 deletion examples/test_opsin_mac_ZINC.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
from tqdm import tqdm
from utils import standardize_mol

from bluenamer.namer import name_smiles
from openclatura.namer import name_smiles

# --- Configuration ---
N_TEST = 100_000
Expand Down
4 changes: 2 additions & 2 deletions examples/test_opsin_mac_ZINC22.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@
from datasets import load_dataset
from tqdm import tqdm

from bluenamer.namer import name_smiles
from bluenamer.utils import standardize_mol
from openclatura.namer import name_smiles
from openclatura.utils import standardize_mol

# --- Configuration ---
N_TEST = 100_000
Expand Down
4 changes: 2 additions & 2 deletions examples/test_opsin_mac_ZINC22_light.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@
from huggingface_hub import list_repo_files
from tqdm import tqdm

from bluenamer.namer import name_smiles
from bluenamer.utils import standardize_mol
from openclatura.namer import name_smiles
from openclatura.utils import standardize_mol

# --- Configuration ---
N_TEST = 100_000
Expand Down
2 changes: 1 addition & 1 deletion examples/test_qm9_opsin_batch.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
from rdkit.Chem.MolStandardize import rdMolStandardize
from tqdm import tqdm

from bluenamer.namer import name_smiles
from openclatura.namer import name_smiles

normalizer = rdMolStandardize.Normalizer()
reionizer = rdMolStandardize.Reionizer()
Expand Down
12 changes: 6 additions & 6 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ requires = ["hatchling"]
build-backend = "hatchling.build"

[project]
name = "bluenamer"
name = "openclatura"
version = "0.1.0"
description = "Deterministic SMILES-to-IUPAC name generator based on the IUPAC Blue Book"
readme = "README.md"
Expand Down Expand Up @@ -64,10 +64,10 @@ Repository = "https://github.com/lamalab-org/iupac-name-generator"
Issues = "https://github.com/lamalab-org/iupac-name-generator/issues"

[project.scripts]
bluenamer = "bluenamer.cli:main"
openclatura = "openclatura.cli:main"

[tool.hatch.build.targets.wheel]
packages = ["src/bluenamer"]
packages = ["src/openclatura"]


[tool.ruff]
Expand Down Expand Up @@ -99,8 +99,8 @@ line-ending = "auto"
minversion = "7.0"
testpaths = [
"tests",
"src/bluenamer/tests",
"src/bluenamer/tests_roundtrip",
"src/openclatura/tests",
"src/openclatura/tests_roundtrip",
]
addopts = [
"-ra",
Expand All @@ -118,7 +118,7 @@ filterwarnings = [
]

[tool.coverage.run]
source = ["bluenamer"]
source = ["openclatura"]
omit = ["*/tests/*", "*/tests_roundtrip/*"]

[tool.coverage.report]
Expand Down
2 changes: 1 addition & 1 deletion scripts/regenerate_goldens.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@

import rdkit

from bluenamer import name_smiles
from openclatura import name_smiles

ROOT = Path(__file__).resolve().parent.parent
CORPUS = ROOT / "tests/fixtures/diverse_corpus.csv"
Expand Down
2 changes: 1 addition & 1 deletion src/bluenamer/__init__.py → src/openclatura/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
"""bluenamer — deterministic SMILES → IUPAC name generator."""
"""openclatura — deterministic SMILES → IUPAC name generator."""

from collections.abc import Iterable

Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# bluenamer/assembler.py
# openclatura/assembler.py

import re

Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Loading
Loading