qdk_chemistry.remote.backends.base module

Remote execution of QDK/Chemistry. Base classes for remote backends.

This module provides the abstract base class for remote execution backends. Backends transfer serialized inputs, submit the worker process, poll its status, and retrieve serialized outputs.

class qdk_chemistry.remote.backends.base.JobState(*values)[source]

Bases: CaseInsensitiveStrEnum

Canonical states in the remote job lifecycle.

SUBMITTED = 'submitted'
RUNNING = 'running'
SUCCEEDED = 'succeeded'
FAILED = 'failed'
CANCELED = 'canceled'
CANCELLED = 'cancelled'
RETRIEVED = 'retrieved'
class qdk_chemistry.remote.backends.base.JobStatus(job_id, status, logs='', error=None, elapsed_seconds=None, metadata=<factory>)[source]

Bases: object

Status of a remote job.

Returned by RemoteBackend.check() and related helpers.

Parameters:
TERMINAL_STATUSES: ClassVar[frozenset[str]] = frozenset({JobState.CANCELED, JobState.CANCELLED, JobState.FAILED, JobState.RETRIEVED, JobState.SUCCEEDED})
logs: str = ''
error: str | None = None
elapsed_seconds: float | None = None
static normalize_status(status)[source]

Return the canonical form of a status string.

Parameters:

status (str | None) – Status string to normalize. If None, it is treated as an empty string.

Return type:

str

Returns:

The case-folded status string.

classmethod is_terminal_status(status)[source]

Return whether a status string represents a terminal state.

Parameters:

status (str | None) – Status string to check.

Return type:

bool

Returns:

True if the status represents a terminal state; otherwise, False.

classmethod is_successful_status(status)[source]

Return whether a status string represents successful execution.

Parameters:

status (str | None) – Status string to check.

Return type:

bool

Returns:

True if the status represents successful execution; otherwise, False.

property is_terminal: bool[source]

Whether the job has reached a terminal state.

Returns:

True if the job has reached a terminal state; otherwise, False.

property is_successful: bool[source]

Whether the job completed successfully.

Returns:

True if the job completed successfully; otherwise, False.

__init__(job_id, status, logs='', error=None, elapsed_seconds=None, metadata=<factory>)
Parameters:
Return type:

None

class qdk_chemistry.remote.backends.base.RemoteBackend(**backend_args)[source]

Bases: ABC

Abstract base class for remote execution backends.

Backends must implement these core operations:

  • connect / disconnect: lifecycle management

  • upload / download: default file transfer to/from the remote system

  • _submit: launch a job asynchronously (returns job_id + state)

  • check: poll job status

  • fetch: download and deserialize results

Backends may optionally implement:

  • cancel: cancel a running or queued job

  • cleanup_job: remove artifacts for a terminal job

upload() and download() are the normal transport for serialized job files. A backend with access to a cache shared by the client and compute node may use it instead for cache-backed artifacts, avoiding redundant file transfers. Files unavailable through that cache still use the default transport.

The remote node executes python -m qdk_chemistry.remote.worker which handles input deserialization, algorithm execution, caching, and output serialization.

Backend artifacts are retained after a job reaches a terminal state. Callers are responsible for removing them with cleanup() or fetch() with cleanup=True. Backend cleanup does not remove caller-owned local job records or result directories.

To create a custom backend:

  1. Subclass RemoteBackend

  2. Implement the methods above

  3. Register it from a QdkChemistryPlugin

  4. Optionally declare mcp_safe_config_options for constructor options that MCP clients may control. The default is deny-all.

Example

>>> from qdk_chemistry.plugins import PluginRegistrar, QdkChemistryPlugin
>>> class SlurmBackend(RemoteBackend):
...     name = "slurm"
...     mcp_safe_config_options = frozenset({"poll_interval", "timeout"})
...
...     def __init__(self, *, host, partition="default", poll_interval=5.0, timeout=3600.0):
...         super().__init__(
...             host=host,
...             partition=partition,
...             poll_interval=poll_interval,
...             timeout=timeout,
...         )
...         self.host = host
...         self.partition = partition
...
...     def connect(self):
...         self._client = SlurmClient(self.host)
...
...     def upload(self, local_path, remote_path):
...         self._client.sftp_put(local_path, remote_path)
...
...     def download(self, remote_path, local_path):
...         self._client.sftp_get(remote_path, local_path)
...
...     def disconnect(self):
...         self._client.close()
...
>>> class SlurmPlugin(QdkChemistryPlugin):
...     def register(self, registrar: PluginRegistrar):
...         registrar.register_remote_backend("slurm", SlurmBackend)
Parameters:

backend_args (Any)

mcp_safe_config_options: ClassVar[frozenset[str]] = frozenset({})

Constructor options that MCP clients may control.

Concrete backends must declare this attribute directly on the class to expose any options through MCP. The default is deny-all.

__init__(**backend_args)[source]

Store the arguments needed to recreate the concrete backend.

Parameters:

**backend_args (Any) – Constructor arguments supplied by the concrete backend. Persisted jobs normalize path-like values to strings and require every remaining value to be JSON-serializable.

Return type:

None

abstractmethod connect()[source]

Establish connection to the remote system.

This is called once before any upload/execute/download operations. Use this to set up network connections, authenticate with cloud services, etc.

Return type:

None

abstractmethod disconnect()[source]

Close the connection to the remote system.

Called after connection-scoped operations are complete. This must not cancel submitted jobs or remove artifacts referenced by persisted jobs.

Return type:

None

abstractmethod upload(local_path, remote_path)[source]

Upload a file from local system to remote system.

Backend implementations normally call this while staging the files produced by input serialization. Cache-backed files may be omitted when the compute node can read them from a shared cache.

Parameters:
  • local_path (str | Path) – Path to the local file.

  • remote_path (str) – Destination path on the remote system.

Return type:

None

abstractmethod download(remote_path, local_path)[source]

Download a file from remote system to local system.

Backend implementations normally call this from fetch() for serialized outputs that were not retrieved through a shared cache.

Parameters:
  • remote_path (str) – Path to the file on the remote system.

  • local_path (str | Path) – Destination path on the local system.

Return type:

None

submit(payload, *, job_dir=None)[source]

Submit a job and return immediately with a Job.

This method does not block. The returned Job is self-contained: it can be saved to disk, loaded in a different process, and used to Job.check(), Job.cancel(), or Job.fetch() results.

Subclasses must override _submit() to provide the backend-specific implementation.

Parameters:
  • payload (dict) – Execution request containing algorithm metadata and inputs.

  • job_dir (str | Path | None) – Optional directory where the job file is saved automatically (as <id>.job.json). If None the job is returned in-memory only.

Return type:

Job

Returns:

A Job that tracks this submission.

abstractmethod check(backend_state)[source]

Query the current status of a previously submitted job.

Parameters:

backend_state (dict) – The opaque state dict produced by _submit().

Return type:

JobStatus

Returns:

A JobStatus describing the job’s current state.

cancel(backend_state)[source]

Cancel a running or queued job.

This operation is optional. The default implementation raises NotImplementedError.

Parameters:

backend_state (dict) – The opaque state dict produced by _submit().

Return type:

None

abstractmethod fetch(backend_state, local_dir=None)[source]

Download and deserialize results for a completed job.

Parameters:
  • backend_state (dict) – The opaque state dict produced by _submit().

  • local_dir (str | Path | None) – Optional directory to download result files into. If None, a temporary directory is used and cleaned up after deserialization.

Return type:

Any

Returns:

The deserialized algorithm results (same format as the return value of the completed algorithm run).

cleanup_job(backend_state)[source]

Remove artifacts owned by a terminal job.

This operation is optional. Implementations must make repeated calls safe and must not remove shared backend work directories. The default implementation raises NotImplementedError.

Parameters:

backend_state (dict) – The opaque state dict produced by _submit().

Return type:

None

qdk_chemistry.remote.backends.base.available_backends()[source]

Return list of registered backend names.

Return type:

list[str]

qdk_chemistry.remote.backends.base.create_remote(name, **config)[source]

Create a configured remote backend instance.

Parameters:
  • name (str) – Backend name (e.g., “custom” or “local”)

  • **config – Backend-specific configuration options.

Return type:

RemoteBackend

Returns:

Configured RemoteBackend instance ready for use

Examples

>>> from qdk_chemistry.remote import create_remote
>>> from qdk_chemistry.algorithms import create
>>>
>>> remote = create_remote("local", timeout=7200, poll_interval=10.0)
>>> scf = create("scf_solver")
>>> energy, wfn = scf.run(structure, 0, 1, "cc-pvdz",
...                       cache="./cache", remote=remote)
qdk_chemistry.remote.backends.base.get_backend(name, **config)[source]

Create a backend instance by name.

Parameters:
  • name (str) – Backend name (e.g., “custom” or “local”)

  • **config – Backend-specific configuration.

Return type:

RemoteBackend

Returns:

Configured RemoteBackend instance

Raises:

ValueError – If no backend is registered with that name

qdk_chemistry.remote.backends.base.get_mcp_safe_config_options(name)[source]

Return MCP-safe constructor options for a registered backend.

Unknown backends and those without a direct declaration expose no configurable options to MCP clients.

Parameters:

name (str) – Registered backend name.

Return type:

frozenset[str]

Returns:

The explicitly declared MCP-safe constructor options. Returns an empty set when the backend is unknown or has no direct declaration.

qdk_chemistry.remote.backends.base.register_backend(name)[source]

Decorator to register a backend class with a name.

Example

>>> @register_backend("custom")
... class CustomBackend(RemoteBackend):
...     ...
Parameters:

name (str) – The backend name (e.g., “custom” or “local”).

Return type:

Callable[[Type[RemoteBackend]], Type[RemoteBackend]]

Returns:

Decorator function that registers the backend class.

Raises:

DuplicateRegistrationError – If the remote backend name or class is already registered.