Skip to content

feat(operators): add script operation support for DiscoverySpace#956

Open
michael-johnston wants to merge 2 commits into
mainfrom
maj_script_operators
Open

feat(operators): add script operation support for DiscoverySpace#956
michael-johnston wants to merge 2 commits into
mainfrom
maj_script_operators

Conversation

@michael-johnston
Copy link
Copy Markdown
Member

This PR adds first-class support for inline script operations on DiscoverySpace instances, so custom sampling code can store measurements under a proper OperationResource without registering an operator plugin or going through orchestrate().

  • Introduces ScriptOperatorConf - specifies a "dynamic" operator that doesn't require the operator registry
  • Adds DiscoverySpace.operation_context() to create and lifecycle-manage a script operation (CREATED → STARTED → FINISHED/SUCCESS or FAIL), yielding an operation_id for use with Actuators
  • Ensures script-run measurements appear in sampledEntities(), space summaries, and ado show operation commands
  • Can be used to record any type of operation

Use

Combined with #169 you can now do the following to iterate over a space, make measurements synchronously, store them without ray objects.

  space = DiscoverySpace.from_stored_configuration(
                      project_context=ctx,
                      space_identifier=space_id,
                      )

  actuators = {}
  for experiment in space.measurementSpace.experiments:
      aid = experiment.actuatorIdentifier
      if aid in actuators:
          continue
      actuator_cls = registry.actuatorForIdentifier(aid)
      actuators[aid] = actuator_cls()

  with space.operation_context(
      name="direct-standard-actuator-grid",
      description="Example of iterating over a discrete space",
  ) as operation_id:
      sampler = ExplicitEntitySpaceGridSampleGenerator(mode=WalkModeEnum.SEQUENTIAL)
      request_index = 0
      for batch in sampler.entityIterator(space, batchsize=1):
          for experiment in space.measurementSpace.experiments:
              ref = experiment.reference
              actuator = actuators[ref.actuatorIdentifier]
              completed = actuator.execute(
                  entities=batch,
                  experimentReference=ref,
                  requesterid=operation_id,
                  requestIndex=request_index,
                  use_ray=False,
              )
              space.addMeasurement(completed)
              request_index += 1

Motivation

Developers want to use DiscoverySpace for sampling and result storage from custom scripts or lightweight inline operator logic, perhaps inside an existing operator. Previously this required a registered explore operator in order to create the required OperationResource to associate measurements to (measurements stored without a linked OperationResource were invisible to most ADO query paths.)

This allows dynamic operations to be recorded on discoveryspaces i.e. without having to install a separate operator plugin
Comment on lines +909 to +911
name: Human-readable script name stored in the operation configuration.
description: Optional description for the operation metadata.
metadata: Optional extra metadata fields merged into ConfigurationMetadata.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Wouldn't it be clearer to have a ConfigurationMetadata parameter instead of these three? When I read name I assumed it would've been basically the operation identifier, which felt weird to me but that's what I assumed

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

🤷 no real opinion :-)

Comment thread tests/core/test_space.py
Comment on lines +341 to +350
mock_store = MagicMock()
pfas_space._metadataStore = mock_store

with pfas_space.operation_context(
name="test-script",
description="Script operation for testing",
metadata={"labels": {"source": "test"}},
) as operation_id:
assert operation_id.startswith("operation-script-test-script-")
assert operation_id in pfas_space._verified_operation_ids
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Why are these things mocked?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Agents

Comment thread tests/core/test_space.py
Comment on lines +382 to +393
mock_store = MagicMock()
pfas_space._metadataStore = mock_store

with (
pytest.raises(RuntimeError, match="boom"),
pfas_space.operation_context(name="failing-script"),
):
raise RuntimeError("boom")

finished_operation = mock_store.updateResource.call_args_list[-1].args[0]
assert finished_operation.status[-1].event == OperationResourceEventEnum.FINISHED
assert finished_operation.status[-1].exit_state == OperationExitStateEnum.FAIL
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I assume this serves to test a case where there's an exception after the operation id is returned. First of all, it looks a bit weird with the "boom", so I would maybe change that to be something like "exception during execution"
We also don't need to mock the store, right?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

sure

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.

2 participants