From b3fa13d9d4c35ebe640d7d4f45e8af8558e81628 Mon Sep 17 00:00:00 2001 From: njzjz-bot Date: Mon, 24 Aug 2026 04:03:21 +0800 Subject: [PATCH 1/2] fix: honor dflow local debug mode Recognize DFLOW_MODE=debug before configuring Bohrium and document credential-free local execution. Coding-Agent: Codex Codex-Version: codex-cli 0.149.1 Model: gpt-5.6-sol Reasoning-Effort: xhigh --- docs/quickcli.md | 11 +++++++ dpgen2/entrypoint/args.py | 5 +++- dpgen2/entrypoint/common.py | 4 ++- tests/entrypoint/test_local_mode.py | 46 +++++++++++++++++++++++++++++ 4 files changed, 64 insertions(+), 2 deletions(-) create mode 100644 tests/entrypoint/test_local_mode.py diff --git a/docs/quickcli.md b/docs/quickcli.md index e9003715..b1f07baa 100644 --- a/docs/quickcli.md +++ b/docs/quickcli.md @@ -10,6 +10,17 @@ dpgen2 submit input.json where `input.json` is the input script. A guide of writing the script is found [here](inputscript). When a workflow is submitted, a ID (WFID) of the workflow will be printed for later reference. +### Run locally without Bohrium or Kubernetes + +dflow debug mode executes workflow steps on the local machine. Enable it before submitting: + +```bash +export DFLOW_MODE=debug +dpgen2 submit input.json +``` + +In this mode, omit `bohrium_config` or set it to `null`; no username, password, or project ID is required. `dflow_config` and `dflow_s3_config` may also be omitted unless a local customization needs them. Commands referenced by the step configurations must be installed and runnable on the local machine. The legacy `DFLOW_DEBUG=1` environment variable remains supported. + ## Check the convergence of a workflow The convergence of stages of the workflow can be checked by the `status` command. It prints the indexes of the finished stages, iterations, and the accurate, candidate and failed ratio of explored configurations of each iteration. ```bash diff --git a/dpgen2/entrypoint/args.py b/dpgen2/entrypoint/args.py index df11ff7f..7cf0545f 100644 --- a/dpgen2/entrypoint/args.py +++ b/dpgen2/entrypoint/args.py @@ -808,7 +808,10 @@ def dpgen_step_config_args(default_config): def submit_args(default_step_config=normalize_step_dict({})): - doc_bohrium_config = "Configurations for the Bohrium platform." + doc_bohrium_config = ( + "Configurations for the Bohrium platform. Omit this section or set it " + "to null when running locally with dflow debug mode." + ) doc_step_configs = "Configurations for executing dflow steps" doc_upload_python_packages = "Upload python package, for debug purpose" doc_inputs = "The input parameter and artifacts for dpgen2" diff --git a/dpgen2/entrypoint/common.py b/dpgen2/entrypoint/common.py index 0d0af9e8..0bd8a698 100644 --- a/dpgen2/entrypoint/common.py +++ b/dpgen2/entrypoint/common.py @@ -29,7 +29,9 @@ def global_config_workflow( # dflow_config, dflow_s3_config workflow_config_from_dict(wf_config) - if os.getenv("DFLOW_DEBUG"): + # dflow documents DFLOW_MODE=debug, while older DPGEN2 examples use + # DFLOW_DEBUG. Accept both before touching any remote-platform credentials. + if os.getenv("DFLOW_DEBUG") or os.getenv("DFLOW_MODE", "").lower() == "debug": dflow.config["mode"] = "debug" return None diff --git a/tests/entrypoint/test_local_mode.py b/tests/entrypoint/test_local_mode.py new file mode 100644 index 00000000..83575544 --- /dev/null +++ b/tests/entrypoint/test_local_mode.py @@ -0,0 +1,46 @@ +import os +import unittest +from unittest import ( + mock, +) + +import dflow + +# isort: off +from .context import ( + dpgen2, +) +from dpgen2.entrypoint.common import ( + global_config_workflow, +) + +# isort: on + + +class TestLocalMode(unittest.TestCase): + @mock.patch("dpgen2.entrypoint.common.bohrium_config_from_dict") + @mock.patch.dict(os.environ, {"DFLOW_MODE": "debug"}, clear=True) + def test_dflow_mode_debug_skips_bohrium_configuration(self, mocked_bohrium): + previous_mode = dflow.config.get("mode") + self.addCleanup(dflow.config.__setitem__, "mode", previous_mode) + + global_config_workflow( + { + # Invalid on purpose: debug mode must return before reading it. + "bohrium_config": {"username": "not-used"}, + } + ) + + self.assertEqual(dflow.config["mode"], "debug") + mocked_bohrium.assert_not_called() + + @mock.patch("dpgen2.entrypoint.common.bohrium_config_from_dict") + @mock.patch.dict(os.environ, {"DFLOW_DEBUG": "1"}, clear=True) + def test_legacy_dflow_debug_skips_bohrium_configuration(self, mocked_bohrium): + previous_mode = dflow.config.get("mode") + self.addCleanup(dflow.config.__setitem__, "mode", previous_mode) + + global_config_workflow({"bohrium_config": {"username": "not-used"}}) + + self.assertEqual(dflow.config["mode"], "debug") + mocked_bohrium.assert_not_called() From 063dc0504615e20a46a2caf22bfd79e636527672 Mon Sep 17 00:00:00 2001 From: "A bot of @njzjz" Date: Sat, 29 Aug 2026 23:07:03 +0800 Subject: [PATCH 2/2] fix: use CALYPSO-compatible config default Avoid injecting LAMMPS-only defaults into an omitted CALYPSO exploration config and pin strict schema compatibility. Coding-Agent: Codex Codex-Version: codex-cli 0.151.0 Model: gpt-5.6-sol Reasoning-Effort: xhigh --- dpgen2/entrypoint/args.py | 2 +- tests/entrypoint/test_submit_args.py | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/dpgen2/entrypoint/args.py b/dpgen2/entrypoint/args.py index 7cf0545f..9c7b916e 100644 --- a/dpgen2/entrypoint/args.py +++ b/dpgen2/entrypoint/args.py @@ -312,7 +312,7 @@ def caly_args(): dict, run_expl_caly_conf_args(), optional=True, - default=RunLmp.normalize_config({}), + default={}, doc=doc_config, ), Argument( diff --git a/tests/entrypoint/test_submit_args.py b/tests/entrypoint/test_submit_args.py index ec7e4582..95ac41ec 100644 --- a/tests/entrypoint/test_submit_args.py +++ b/tests/entrypoint/test_submit_args.py @@ -11,12 +11,16 @@ import dpdata import numpy as np +from dargs import ( + Argument, +) # isort: off from .context import ( dpgen2, ) from dpgen2.entrypoint.args import ( + caly_args, normalize, ) from dpgen2.op import ( @@ -155,6 +159,16 @@ def test_bohrium(self): }, ) + def test_calypso_default_config_matches_schema(self): + """Keep an omitted CALYPSO config free of LAMMPS-only defaults.""" + config_arg = next(arg for arg in caly_args() if arg.name == "config") + schema = Argument("calypso", dict, [config_arg]) + + normalized = schema.normalize_value({}) + + schema.check_value(normalized, strict=True) + self.assertEqual(normalized["config"], {}) + old_str = textwrap.dedent( """