LLM4ChemE migration¶
LLM4ChemE (package llm4cheme) is owned by the same group as core. Its owner has cleared its
code and skills for migration into core under core's AGPL-3.0-or-later license. Third-party
data keeps its own license, and its LICENSE and SOURCE.md files move with it.
The goal is that core becomes the home of LLM4ChemE's reusable, LLM-independent engineering
tools and their skills. The agent runtime, the LLM wrappers, the workflows and the evaluation
code stay in LLM4ChemE, which can depend on cheme-as-code for the migrated tools (see
"Research and paper projects" in contributing.md).
The migration runs in two sessions. M1 moves the process side (units, phase calculations, thermodynamic model selection, pinch analysis, separation method selection). M2 moves the chemistry side (property retrieval, databases, kinetics, prediction-service clients) and the skill knowledge that needs it. Both sessions are done; the tables below give the final status of every component.
Rules applied to every migrated tool¶
- Typed inputs and structured results (dataclasses or pydantic models), explicit units in the field names or documented per field, no current-directory state, no printing, no file writes.
- No LLM, agent or LangChain import. The
llm_toolswrappers are not migrated; what they did deterministically (input models, unit conversion, the result model) is part of the domain function now. - Optional libraries are imported inside the function that needs them and belong to an extra
(
pyproject.toml).import chemeascodeand every domain package stay free of them (tests/test_boundaries.py). - Package data is read through
importlib.resources, never through a path relative to the working directory (this replaces LLM4ChemE'ssrc/paths.py). - New process modules live in sub-packages of
domains/process/(thermodynamics/,heat_integration/,separations/).generator_fingerprint()hashes only the*.pyfiles directly indomains/process/, the code that generates BFD/PFD sheets, so these analysis tools do not make existing generated sheets outdated. - Each tool's tests are ported to
tests/. A test that needs an extra usespytest.importorskip; a test that would need the network is mocked.
Component table¶
Status: M1 (moved in session M1), M2 (moved in session M2), no (not migrated, with
the reason). Deps lists what the component needs in core; "base" means the base dependencies.
Process side¶
| LLM4ChemE component | Destination in core | Status | Deps |
|---|---|---|---|
src/tools/unit_conversion.py (convert_units, parse_unit), src/schemas/quantity.py (ureg) |
foundation/units.py: convert_units, parse_unit, registry, plus core's pressure convention ((a)/(g) units) and to_kpa_abs/to_kelvin, which domains/process re-exports |
M1 | base for K, °C and core's pressure units; extra units (Pint) for every other unit |
src/schemas/quantity.py QuantityModel, src/tools/heat_integration/unit_conversion.py |
none: foundation.units.convert_units covers the conversion; core already has its Quantity (domains/process/schema.py) |
no (duplicate) | |
src/tools/phase_calculations/phase_tools.py, src/llm_tools/wrapped_phase_tool.py (phase_determination, EOSModel, ActivityCoefficientModel, FlashCalculationResult) |
domains/process/thermodynamics/phase.py: phase_determination(...) -> FlashCalculationResult (same signature, plus temperature_K/pressure_Pa in the result), with EOSModel and ActivityCoefficientModel |
M1 | extra props (thermo, chemicals, numpy) |
src/data/srk.json, srk.ipd, convert_srk_to_json.py (ChemSep SRK kij) |
not shipped any more: cheme data fetch chemsep-srk installs it (thermodynamics/chemsep_srk.py, the converter ported); data/chemsep/SOURCE.md records origin and licence status |
M1 | data |
src/tools/thermo_model_selection/ (kb_schema.py, rule_inference.py, thermo_model_selection_rules.yml), src/llm_tools/wrapped_thermo_tools.py |
domains/process/thermodynamics/rules.py (knowledge-base engine), model_selection.py (select_thermo_model), model_selection_rules.yml as package data |
M1 | base |
src/tools/heat_integration/pypinch.py (PyPinch, GPL-3.0), tools.py, src/schemas/heat_integration.py |
domains/process/heat_integration/pinch.py: pinch_analysis(streams, dt_min) -> PinchResult, the problem-table algorithm written anew without pandas, matplotlib, printing or CSV files in the working directory; streams may be piecewise (sensible and isothermal latent segments, exact in the cascade); the result is labelled as energy targets (basis: targets), not a synthesized or rated exchanger network; minimum_approach checks one exchanger's composite curves |
M1 | base |
src/tools/separation_selection/tools_separation_methods.py: matrices, pre-questions, COP, DSE, PI-matrix scoring, select_separation_methods_core, select_binary_separation_methods, select_multicomponent_separation_methods |
domains/process/separations/selection.py |
M1 | base |
| same file: HANNA activity coefficients, derived relative volatility | domains/chemistry/properties.py (PropertyRetriever.mixture, relative_volatility) on top of integrations/prediction_services.py |
M2 | base; HANNA endpoint as an argument |
same file: get_property_for_separation_methods, Component |
none (see the chemistry table: superseded by get_many + mixture) |
no (superseded) | |
same file: LangChain StructuredTool helpers |
none | no (LLM-coupled) | |
src/data/binary_separation_methods_dataset.json, Ternary_separation_methods_dataset.json |
none | no: prompt/answer benchmarks of the multi-agent system (evaluation material, see below) | |
skills/phase-calculation, skills/thermodynamic-model-selection, skills/separation-method-selection |
domains/process/skills/<name>/SKILL.md, in core's seven-section structure, calling the Python functions above; installed by cheme init |
M1 |
Chemistry and safety side¶
| LLM4ChemE component | Destination in core | Status | Deps |
|---|---|---|---|
src/tools/chemical_property/unify_chemical_property_retrieval.py, src/schemas/chemical_properties.py |
domains/chemistry/properties.py: PropertyRetriever (get, get_many, mixture), retrieve_properties, SOURCE_ORDER, relative_volatility, grappa_antoine_to_canonical, select_antoine_set; results are PropertyResult (value, units, source, error, metadata) |
M2 | base; each source as below |
same: async manager, global singleton, negative-cache TTL, PropertyJSONEncoder, the Cpm/Cpl/Cpg aliases and the single-point heat-capacity tables |
none: the retriever is synchronous, caches per instance, and gives cp_ideal_gas/cp_liquid/cp_solid in J/mol/K |
no (simplified) | |
src/databases/online_databases/pubchem.py |
integrations/pubchem.py (typed PubChemProperties, no JSON strings) |
M2 | base |
src/databases/online_databases/nist_webbook.py (scrapy spider) |
integrations/nist_webbook.py, the same XPaths on lxml over HTTP; no scrapy, no multiprocessing |
M2 | base |
src/databases/cactus.py |
integrations/cactus.py |
M2 | base |
src/databases/online_databases/thermo.py (LangChain tools) |
integrations/thermo.py: chemical_properties() |
M2 | extra props |
src/databases/online_databases/unifac_model_retrieval.py, src/schemas/unifac_models.py |
integrations/unifac.py (models and parser together; lxml instead of BeautifulSoup) |
M2 | base |
src/databases/rdkit_tools.py |
integrations/rdkit.py |
M2 | extra chemistry (rdkit) |
src/databases/online_databases/osha_safety_data.py (Selenium) |
adapter integrations/osha.py (HTTP + lxml, same table positions); owner domains/safety/hazard_data.py (flash point in K, explosive limits in vol-%) |
M2 | base; the live site was not reachable from the session, see the module docstring |
src/tools/property_prediction/tools_grappa.py, tools_hanna.py |
integrations/prediction_services.py: grappa_vapor_pressure, grappa_antoine_parameters, grappa_boiling_point, hanna_activity_coefficients, each with a required endpoint |
M2 | base |
services/grappa_service, services/hanna_service, services/rmg_service |
none: run them from an LLM4ChemE checkout (docker compose up -d grappa-api hanna-api rmg-api; ports 8080, 8000, 8001) and pass their base URL |
no (services and ML runtimes) | |
src/tools/reaction_kinetics/ (identifier, filter, retriever), src/databases/nist_kinetics.py, rmg_kinetics.py, src/schemas/reaction_kinetics.py |
domains/chemistry/kinetics/ (records.py, identification.py, filter.py in plain Python instead of pandas, retrieval.py), adapters integrations/nist_kinetics.py, integrations/rmg.py (RMG endpoint required) |
M2 | extra chemistry for SMILES checks |
src/databases/open_reaction_db.py |
none | no (a stub raising NotImplementedError) |
|
src/schemas/kinetics_output_schema.py, src/schemas/response_formats.py |
none | no (LLM response shapes) | |
src/databases/offline_databases/ (solvents/, polymers/), src/tools/offline_databases/ |
none | no: both datasets' LICENSE files are placeholders ("License: TODO — confirm and record the upstream license") and their SOURCE.md files leave the upstream source open, so redistribution in an AGPL package is not established. The SQL ingestion code stays with them: without a dataset it would have nothing to serve. Move both together once the owner records the upstream terms. |
|
src/tools/separation_selection/tools_separation_methods.py: get_property_for_separation_methods, Component |
none: it formatted a property snapshot for the agent; PropertyRetriever.get_many and mixture (relative volatility, boiling-point difference, HANNA γ) give the same values with sources |
no (superseded) | |
skills/property-retrieval |
domains/chemistry/skills/property-retrieval/ |
M2 | |
skills/materials |
domains/chemistry/skills/materials/, rewritten for process/materials.yaml and stream compositions in process/flowsheet.yaml (YAML edits, no editor commands) |
M2 |
Not migrated¶
| LLM4ChemE component | Reason |
|---|---|
src/domain/, src/application/, src/infrastructure/ (agents, routers, LLM factories, YAML graph factory, skill loader, system-message builders), src/cli.py, main.py, configs/ |
the agent framework; deterministic tools in core must run without an agent runtime |
src/llm_tools/ |
LLM tool wrappers; their deterministic parts moved with the tools (see above) |
src/workflows/ (dexpi_process, separation_method, reaction_kinetics, state_access.py) |
LangGraph state and handlers |
src/evaluation/, src/data/*_dataset.json, examples/, system messages |
evaluation and paper material; stays with the research repository |
src/tools/dexpi_process/, src/llm_tools/dexpi_process/, src/workflows/dexpi_process/ |
built on pyDEXPI's editor commands (process.*), which core does not use: core edits DEXPI files directly (dexpi-xml, pid-edit) and generates BFD/PFD sheets from YAML (cheme process build) |
skills/general-dexpi-operations, the DEXPI parts of skills/process-steps-and-streams and skills/materials |
written for those editor tools; the engineering knowledge is recorded below for folding into core's skills |
scripts/generate_pydexpi_reference.py, scripts/example_dexpi_bfd.py, scripts/blank_agent_example.py |
reference generation for the editor skills, examples of the agent runtime; core has scripts/build_snippets.py |
src/paths.py |
replaced by importlib.resources |
Engineering knowledge from the DEXPI skills¶
The editor-based skills hold rules that are independent of how a file is changed. Rules that are
clearly correct and fit core's YAML-first, file-editing workflow are folded into
process-design (and dexpi-xml where they concern the XML). M2 was to fold them, together with
the materials skill. Candidates:
- Materials: a material template lists the components; a material state refers to a template and gives a composition whose fractions sum to one on a stated basis (mole or mass).
- Ports and streams: every stream connects one outlet port to one inlet port; a port carries one stream; the direction of a port follows the flow.
- Steps: a process step is decomposed into sub-steps only where the decomposition carries information; the hierarchy level (process, unit operation, ...) follows the decomposition.
M2 folded the materials rules into the materials skill (template order defines the order of
every composition vector; fractions of one composition sum to one on the stated basis). The
port and step rules were not added to process-design: they still need checking against the
rules cheme validate applies to process/*.yaml, so that the skill does not restate or
contradict a check. This is open.
Tests¶
| LLM4ChemE test | Ported to | Status |
|---|---|---|
tests/tools/test_unit_conversion.py |
tests/test_units.py |
M1 |
tests/tools/test_phase_tools.py |
tests/test_thermo_phase.py |
M1 |
tests/tools/test_ruledb_inference_engine.py, test_thermo_model_selection_tool.py (+ the knowledge-base fixtures of tests/tools/conftest.py) |
tests/test_thermo_model_selection.py |
M1 |
tests/tools/test_separation_method.py |
tests/test_separation_selection.py |
M1 |
| (no tests in LLM4ChemE for pinch analysis) | tests/test_pinch.py, new |
M1 |
test_thermo_tool.py, test_pubchem.py, test_nist_webook_retrieval.py, test_cactus.py, test_rdkit_tools.py, test_unify_chemical_property_retrieval.py, test_unified_property_retrieval.py, test_unifaq_model_retrieval.py, test_osha_retrieval.py, test_scraping_extra.py, test_offline_databases*.py, tests/databases/, kinetics tests, test_tools_grappa.py, test_rmg_*.py |
tests/test_chemistry_properties.py, test_chemistry_cli.py, test_kinetics_*.py, test_nist_kinetics.py, test_rmg_client.py, test_chemistry_adapters_rdkit_cactus.py, every network access mocked |
M2 |
tests/application/, tests/domain/, tests/infrastructure/, tests/workflows/, tests/evaluation/, tests/services/, DEXPI tool tests, test_offline_databases*.py, tests/databases/, test_scraping_extra.py, test_rmg_service.py |
none | no |