Skip to content

feat: add a two-stage inference runner - #241

Open
codename1995 wants to merge 1 commit into
openvinotoolkit:mainfrom
codename1995:feat/xr1-two-stage-runner
Open

feat: add a two-stage inference runner#241
codename1995 wants to merge 1 commit into
openvinotoolkit:mainfrom
codename1995:feat/xr1-two-stage-runner

Conversation

@codename1995

Copy link
Copy Markdown

Summary

  • Adds physicalai.inference.runners.TwoStage, a runner that executes two artifacts in sequence: the first stage's output feeds the second's input, and each stage can sit on a different backend.
  • Registers it in the runner factory, so a manifest can name it through class_path like any other runner.
  • 217 lines of tests in tests/unit/inference/test_runners.py.

Why

A vision-language-action policy is not always exportable as one graph. Concretely, a Qwen3-VL backbone under transformers 5.5.4 is capturable by neither torch.export nor torch.jit.trace, while the action expert that runs several times per chunk exports cleanly and benefits most from an optimized backend.

Splitting execution recovers the value: the backbone runs on the Torch adapter, the action expert on the OpenVINO one, and InferenceModel loads the directory unchanged. Measured end to end on such an export, the hybrid path matches the pure-Torch path to 7.9e-08 absolute, with the action expert 2.9x faster on CPU (2.6 ms vs 7.6 ms, AMD Threadripper 3960X).

The runner is deliberately generic. It takes any two adapters, has no policy name in its logic, and knows nothing about the model that motivated it. An earlier draft added a bind_export_dir hook to InferenceRunner; it was dropped once it became clear the existing resolve_artifact already solved the problem. One new concept, in the place runners already live.

Consumer side: open-edge-platform/physical-ai-studio PR adding the XR-1 policy, whose hybrid export writes a manifest naming this runner.

Validation

  • pytest tests/unit/inference/test_runners.py — new coverage for two-stage execution, including artifact resolution and stage wiring.
  • pytest tests/unit — full unit suite passes.
  • prek run --all-files and pyrefly check clean.
  • Exercised end to end from the Studio side: InferenceModel(...) loading a two-artifact export, backbone on Torch and action expert on OpenVINO, 7.9e-08 against the single-backend export.

Breaking changes

  • None. New runner plus its registration; existing runners and manifests are untouched.

Related issues

  • None.

Some policies cannot be exported as one graph. A vision-language-action model
may have a backbone no exporter can capture and an action head that exports
cleanly, and it is the head that is evaluated repeatedly per action chunk, so
running it on an accelerated backend is where the time goes.

TwoStage runs the primary adapter, matches its output names against the second
model's declared input names and feeds them across, reporting a mismatch up
front rather than leaving it to surface as a shape error inside the backend.
The second model is loaded on first use, so building the runner while the
manifest is read never compiles a graph.

The runner needs its own artifact, which a manifest names relative to the
export directory, so get_runner() takes an optional export_dir and applies
resolve_artifact() to the spec - the same path preprocessor artifacts already
take. Instantiation and the runner type check moved into one helper shared by
both branches.

Note that a runner spec carrying manifest metadata must use class_path mode:
in type mode every extra field is forwarded to the constructor, so a chunk_size
annotation reaches the adapter as a backend option.

Signed-off-by: Weijie Wei <codename1995@gmail.com>
@codename1995
codename1995 marked this pull request as ready for review August 18, 2026 21:33
@codename1995
codename1995 requested a review from a team as a code owner August 18, 2026 21:33
Copilot AI lite review requested due to automatic review settings August 18, 2026 21:33

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This pull request introduces a new InferenceRunner implementation, physicalai.inference.runners.TwoStage, to support hybrid exports where a policy must be executed as two separate artifacts (potentially on different backends) while keeping the export directory layout and InferenceModel loading flow unchanged.

Changes:

  • Added TwoStage runner that runs a primary adapter and feeds selected intermediate outputs into a lazily-loaded second-stage adapter.
  • Updated runner factory + InferenceModel integration so runner artifact paths can be resolved relative to the export directory (with traversal protection via resolve_artifact).
  • Added a dedicated unit test suite for runner behavior, factory selection, lazy loading, and artifact resolution.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated no comments.

Show a summary per file
File Description
tests/unit/inference/test_runners.py Adds unit tests for SinglePass, TwoStage, and get_runner (including lazy-load and artifact resolution behavior).
src/physicalai/inference/runners/two_stage.py Implements the new TwoStage runner with lazy second-stage loading and input-name-based wiring/validation.
src/physicalai/inference/runners/factory.py Extends get_runner to accept export_dir and resolves runner artifacts via resolve_artifact before instantiation.
src/physicalai/inference/runners/init.py Exports TwoStage from the runners package.
src/physicalai/inference/model.py Passes self.export_dir into get_runner so runner artifacts resolve like other manifest components.
src/physicalai/inference/component_factory.py Registers the "two_stage" runner type in the component registry.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

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