Skip to content

feat: add support for (multi) reaction naming#1

Open
MrtinoRG wants to merge 1 commit into
mainfrom
add_reaction_naming
Open

feat: add support for (multi) reaction naming#1
MrtinoRG wants to merge 1 commit into
mainfrom
add_reaction_naming

Conversation

@MrtinoRG

@MrtinoRG MrtinoRG commented Jul 23, 2026

Copy link
Copy Markdown
Collaborator

Summary by Sourcery

Introduce template-grounded reaction-name classification and template provenance support across the library.

New Features:

  • Add ReactionClassifier for classifying reaction SMILES into all matching reaction labels with evidence and configurable matching policies.
  • Expose structured classification result types, including candidates, template matches, diagnostics, and outcome-preserving execution types.
  • Provide a reaction SMILES parser that supports reactants>>products and reactants>agents>products forms.

Enhancements:

  • Extend ReactionResult with template_id provenance and ensure enumerator attaches originating template IDs, including in parallel execution.
  • Refactor execution into an outcome-preserving engine shared by classification, with detailed diagnostics for reaction execution failures.
  • Add explicit, fragment-aware product-set matching policies, including connectivity, parent_fragment, tautomer, and main_product, with deterministic strength ordering.

Documentation:

  • Document reaction-name classification usage, semantics, and matching policies in the README.
  • Add an Unreleased changelog section describing ReactionClassifier, new matching policies, provenance, and behavioral changes around matching and incompleteness.

Tests:

  • Add comprehensive tests for classifier behavior, matching policies, outcome-preserving engine, reaction SMILES parsing, and template provenance on enumeration results.
  • Extend test fixtures to include additional molecules, a Suzuki reaction example, and a reusable ReactionClassifier fixture.

Summary by CodeRabbit

  • New Features
    • Added reaction-name classification with labels, candidate matches, evidence, diagnostics, and status reporting.
    • Added reaction SMILES parsing, including support for agents and multi-component reactions.
    • Added configurable product-matching policies, including fragment-aware and main-product matching.
    • Added template provenance through template_id and reactant index evidence.
  • Documentation
    • Added usage guidance and examples for reaction classification.
    • Added an Unreleased changelog section.

@sourcery-ai

sourcery-ai Bot commented Jul 23, 2026

Copy link
Copy Markdown

Reviewer's Guide

Introduce a new template-grounded reaction-name classification subsystem (ReactionClassifier) with explicit match policies and provenance, add reaction SMILES parsing utilities, and extend enumeration outputs and public types to carry template IDs and richer classification metadata, all backed by comprehensive tests and documentation updates.

Sequence diagram for ReactionClassifier.classify workflow

sequenceDiagram
    participant RC as ReactionClassifier
    participant PR as parse_reaction_smiles
    participant KG as KeyGenerator
    participant AT as apply_templates_to_pair
    participant PM as ProductMatcher
    participant CR as ClassificationResult

    RC->>PR: parse_reaction_smiles(reaction_smiles)
    PR-->>RC: ParsedReaction or ReactionParseError
    alt invalid_reaction_smiles
        RC-->>RC: create ClassificationResult(status=invalid_input)
    else valid_parsed
        RC->>RC: build candidate_smiles (reactants [+ agents])
        RC->>RC: Chem.MolFromSmiles for components
        RC->>KG: classify_many(unique_components)
        KG-->>RC: KeysResult list
        RC->>RC: extract_key_strings for each component
        RC->>PM: ProductMatcher(match_policies) [select]
        RC->>PM: observed_by_policy(parsed.products)
        PM-->>RC: observed_by_policy map
        loop for each reactant pair
            RC->>AT: apply_templates_to_pair(templates, s1, s2, m1, m2, keys1, keys2, idx1, idx2)
            AT-->>RC: TemplateApplication list, diagnostics
            RC->>PM: strongest_match(outcome.products, observed_by_policy)
            PM-->>RC: MatchType or None
            RC-->>RC: build TemplateMatch list
        end
        RC-->>RC: _dedupe_matches(matches)
        RC-->>RC: _aggregate_candidates(matches)
        RC-->>RC: _dedupe_diagnostics(diagnostics)
        RC-->>RC: _status(candidates, diagnostics)
        RC-->>CR: ClassificationResult(status, candidates, diagnostics)
    end
Loading

File-Level Changes

Change Details Files
Extend core reaction/result types and public exports to support provenance and classification metadata.
  • Add template_id to ReactionResult with default 0 for backward compatibility and document it in the docstring.
  • Introduce new dataclasses for internal execution (ReactionOutcome, TemplateApplication) and public classification domain (TemplateMatch, ReactionCandidate, ClassificationDiagnostic, ClassificationResult) including helper constants and literals for status and match types.
  • Implement readable str/repr for ClassificationResult and expose all new types and constants through types.all and the smartreact package init.
src/smartreact/types.py
src/smartreact/__init__.py
Wire template provenance through the existing enumeration engine and add tests to guarantee IDs are present in results.
  • Update worker and key-based enumeration paths to populate ReactionResult.template_id from the originating ReactionTemplate.
  • Add tests that verify default template_id behavior, that Suzuki enumerations carry valid template IDs in both sequential and parallel modes, and that parallel results always have positive template IDs.
src/smartreact/enumerator.py
tests/test_enumerator.py
Add a ReactionClassifier entry point that classifies reaction SMILES into reaction labels using template-grounded evidence and configurable matching policies.
  • Implement ReactionClassifier with resource management, template loading, key-generation reuse, and a classify() method that parses reaction SMILES, validates RDKit molecules, enumerates positional reactant pairs, applies templates via the shared engine, and aggregates evidence into ClassificationResult.
  • Define blocking vs non-blocking diagnostic codes and a status resolution policy that distinguishes matched, no_match, and incomplete outcomes based on candidates and diagnostics.
  • Implement evidence de-duplication and candidate aggregation helpers that preserve strongest match types, reactant indices, and deterministic ordering, and expose ReactionClassifier from the package root.
src/smartreact/classifier.py
src/smartreact/__init__.py
tests/test_classifier.py
Introduce an outcome-preserving reaction execution engine shared by the classifier, with structured diagnostics.
  • Add engine.apply_templates_to_pair to run compatible templates against one reactant pair, respecting reactant categories and orientation, and returning TemplateApplication instances plus diagnostics.
  • Implement RDKit outcome execution and sanitization helpers that de-map atoms, canonicalize product SMILES, preserve each RDKit outcome as a separate ReactionOutcome, and emit diagnostics for template build, RunReactants, sanitize, and unsupported arity failures.
  • Add unit tests that exercise normal execution, orientation/indices handling, template arity guards, and diagnostic paths for RunReactants and product sanitization failures.
src/smartreact/engine.py
tests/test_engine.py
Define explicit, fragment-aware product-set matching policies with a ranked strength model and a main_product fallback.
  • Implement ProductMatcher with policy configuration/validation, normalization of product sets (atom-map stripping, fragment splitting, parent-fragment reduction, tautomer canonicalization, and main-product selection), and strongest-match selection logic.
  • Ensure policies are evaluated strongest-first using MATCH_TYPE_RANK, with DEFAULT_POLICIES set to exact-only, and that observed product normalization can fail cleanly to signal inconclusive cases.
  • Add tests covering policy configuration errors, exact vs connectivity vs parent_fragment vs tautomer behavior, fragment-aware equality (disconnected vs dot-split products), main_product semantics for byproducts, and robustness to unnormalizable products.
src/smartreact/matching.py
tests/test_matching.py
Add a lightweight structural reaction SMILES parser and expose it through the public API.
  • Implement ReactionParseError, ParsedReaction, and parse_reaction_smiles to split reaction SMILES into reactants/agents/products on '>' and '.', enforce non-empty reactant/product sections and components, and remain RDKit-independent.
  • Expose ParsedReaction, ReactionParseError, and parse_reaction_smiles from the smartreact package for external consumers.
  • Add tests covering valid two- and three-section forms, whitespace handling, preservation of duplicate components, and various structural error cases (empty input, wrong separator count, empty sections/components).
src/smartreact/reaction_smiles.py
src/smartreact/__init__.py
tests/test_reaction_smiles.py
Update high-level documentation and changelog to describe the new classification capabilities and behaviors.
  • Extend README with a new Reaction-name classification section that documents ReactionClassifier usage, multi-label semantics, status codes, multi-component handling, agent candidate options, and the hierarchy and behavior of match policies including main_product.
  • Add an Unreleased section to CHANGELOG summarizing the addition of ReactionClassifier, reaction SMILES parsing, new public types, template-ID provenance, fragment-aware matching, main_product policy, reactant_indices provenance, and the new incomplete status for certain no_match cases.
README.md
CHANGELOG.md
Adjust tests and fixtures to integrate the classifier and support new examples.
  • Extend test fixtures to provide a shared ReactionClassifier instance and additional SMILES constants (ETHYL_ACETATE, SUZUKI_RXN).
  • Add tests that verify context-manager behavior and idempotent close() on ReactionClassifier.
tests/conftest.py
tests/test_classifier.py

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@coderabbitai

coderabbitai Bot commented Jul 23, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Changes

Reaction classification

Layer / File(s) Summary
Classification contracts and reaction parsing
src/smartreact/types.py, src/smartreact/reaction_smiles.py, src/smartreact/enumerator.py, tests/test_reaction_smiles.py, tests/test_enumerator.py
Adds structured classification types, template provenance, and validation for reaction SMILES parsing and enumeration results.
Product matching and template execution
src/smartreact/matching.py, src/smartreact/engine.py, tests/test_matching.py, tests/test_engine.py
Adds normalized strongest-first product policies and preserves template outcomes, orientations, indices, and execution diagnostics.
Classification orchestration and result aggregation
src/smartreact/classifier.py, tests/test_classifier.py, tests/conftest.py
Adds lifecycle-managed classification across reactant pairs, policy matching, deterministic evidence aggregation, status derivation, and diagnostics.
Public API surface and documentation
src/smartreact/__init__.py, README.md, CHANGELOG.md
Re-exports the classification API and documents statuses, evidence, agent handling, and matching behavior.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Caller
  participant ReactionClassifier
  participant ProductMatcher
  participant TemplateEngine
  Caller->>ReactionClassifier: classify reaction SMILES
  ReactionClassifier->>ProductMatcher: normalize observed products
  ReactionClassifier->>TemplateEngine: apply compatible templates
  TemplateEngine-->>ReactionClassifier: outcomes and diagnostics
  ReactionClassifier->>ProductMatcher: select strongest policy match
  ReactionClassifier-->>Caller: ClassificationResult
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 16.07% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title matches the main change: adding reaction-name classification support, including multi-label reaction naming.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch add_reaction_naming

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've left some high level feedback:

  • The global RDLogger.DisableLog("rdApp.*") in classifier.py silences all RDKit logging for the entire process; consider scoping this (or making it caller‑configurable) so library users can still opt into RDKit diagnostics when needed.
  • In ProductMatcher.normalize_set each call reparses and normalizes all product SMILES; if classification is run at scale, consider caching per-(smiles, policy) normalization results to avoid repeated RDKit work and improve performance.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The global `RDLogger.DisableLog("rdApp.*")` in `classifier.py` silences all RDKit logging for the entire process; consider scoping this (or making it caller‑configurable) so library users can still opt into RDKit diagnostics when needed.
- In `ProductMatcher.normalize_set` each call reparses and normalizes all product SMILES; if classification is run at scale, consider caching per-(smiles, policy) normalization results to avoid repeated RDKit work and improve performance.

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@CHANGELOG.md`:
- Around line 15-17: Update the changelog entry’s classification type
description to call ReactionOutcome and TemplateApplication public types,
matching their re-exported status in smartreact.__init__ and __all__; remove the
misleading “internal” wording while preserving the rest of the list.

In `@src/smartreact/matching.py`:
- Line 187: Reorder the entries in the module-level __all__ declaration
alphabetically to satisfy Ruff RUF022, preserving the same exported symbols:
DEFAULT_POLICIES, POLICY_ORDER, and ProductMatcher.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: b296ed8f-31d3-4a25-bfde-00959831883c

📥 Commits

Reviewing files that changed from the base of the PR and between 4bd594f and 9f8e529.

📒 Files selected for processing (15)
  • CHANGELOG.md
  • README.md
  • src/smartreact/__init__.py
  • src/smartreact/classifier.py
  • src/smartreact/engine.py
  • src/smartreact/enumerator.py
  • src/smartreact/matching.py
  • src/smartreact/reaction_smiles.py
  • src/smartreact/types.py
  • tests/conftest.py
  • tests/test_classifier.py
  • tests/test_engine.py
  • tests/test_enumerator.py
  • tests/test_matching.py
  • tests/test_reaction_smiles.py

Comment thread CHANGELOG.md
Comment on lines +15 to +17
- Public classification types: `ClassificationResult`, `ReactionCandidate`,
`TemplateMatch`, `ClassificationDiagnostic`, plus outcome-preserving internal
types `ReactionOutcome` and `TemplateApplication`.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Use “public” rather than “internal” for re-exported types.

Lines [15]-[17] describe ReactionOutcome and TemplateApplication as internal, but src/smartreact/__init__.py imports both and lists them in __all__. Update the changelog wording so users are not misled about their supported import surface.

Proposed wording
- plus outcome-preserving internal
+ plus outcome-preserving public
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- Public classification types: `ClassificationResult`, `ReactionCandidate`,
`TemplateMatch`, `ClassificationDiagnostic`, plus outcome-preserving internal
types `ReactionOutcome` and `TemplateApplication`.
- Public classification types: `ClassificationResult`, `ReactionCandidate`,
`TemplateMatch`, `ClassificationDiagnostic`, plus outcome-preserving public
types `ReactionOutcome` and `TemplateApplication`.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@CHANGELOG.md` around lines 15 - 17, Update the changelog entry’s
classification type description to call ReactionOutcome and TemplateApplication
public types, matching their re-exported status in smartreact.__init__ and
__all__; remove the misleading “internal” wording while preserving the rest of
the list.

return None


__all__ = ["ProductMatcher", "POLICY_ORDER", "DEFAULT_POLICIES"]

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

__all__ is not sorted (RUF022). Ruff flags this and it is autofixable; reorder to avoid a lint failure.

🧹 Proposed fix
-__all__ = ["ProductMatcher", "POLICY_ORDER", "DEFAULT_POLICIES"]
+__all__ = ["DEFAULT_POLICIES", "POLICY_ORDER", "ProductMatcher"]
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
__all__ = ["ProductMatcher", "POLICY_ORDER", "DEFAULT_POLICIES"]
__all__ = ["DEFAULT_POLICIES", "POLICY_ORDER", "ProductMatcher"]
🧰 Tools
🪛 Ruff (0.15.21)

[warning] 187-187: __all__ is not sorted

Apply an isort-style sorting to __all__

(RUF022)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/smartreact/matching.py` at line 187, Reorder the entries in the
module-level __all__ declaration alphabetically to satisfy Ruff RUF022,
preserving the same exported symbols: DEFAULT_POLICIES, POLICY_ORDER, and
ProductMatcher.

Source: Linters/SAST tools

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant