diff --git a/.github/workflows/rust-release.yml b/.github/workflows/rust-release.yml
new file mode 100644
index 0000000..1b3083a
--- /dev/null
+++ b/.github/workflows/rust-release.yml
@@ -0,0 +1,103 @@
+name: Rust crate release
+
+on:
+ push:
+ tags:
+ - "openengine-v*"
+
+permissions:
+ contents: read
+
+env:
+ RUSTUP_TOOLCHAIN: "1.98.1"
+
+jobs:
+ validate:
+ name: Validate release
+ runs-on: ubuntu-latest
+ outputs:
+ published: ${{ steps.registry.outputs.published }}
+ steps:
+ - name: Check out repository
+ uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.0.2
+ with:
+ fetch-depth: 0
+
+ - name: Verify release tag points to main
+ shell: bash
+ run: |
+ git fetch --no-tags origin main
+ if ! git merge-base --is-ancestor HEAD origin/main; then
+ echo "::error::Release tags must point to commits on main"
+ exit 1
+ fi
+
+ - name: Set up Rust
+ run: rustup toolchain install "${RUSTUP_TOOLCHAIN}" --profile minimal --no-self-update
+
+ - name: Check release version
+ run: ./scripts/check-release-version.sh "${GITHUB_REF_NAME}"
+
+ - name: Check schema release metadata
+ run: ./scripts/check-schema-release.sh --verify-registry
+
+ - name: Check generated bindings
+ run: ./scripts/check-generated.sh
+
+ - name: Build publishable crate
+ run: cargo package --locked --package openengine
+
+ - name: Check for an existing publication
+ id: registry
+ env:
+ VERSION: ${{ github.ref_name }}
+ shell: bash
+ run: |
+ version="${VERSION#openengine-v}"
+ url="https://crates.io/api/v1/crates/openengine/${version}"
+ user_agent="openengine-release (+https://github.com/ai-dynamo/openengine)"
+ response="${RUNNER_TEMP}/openengine-${version}.json"
+ status="$(curl --silent --show-error --user-agent "${user_agent}" --output "${response}" --write-out '%{http_code}' "${url}")"
+ case "${status}" in
+ 200)
+ remote_checksum="$(jq --raw-output '.version.checksum' "${response}")"
+ local_checksum="$(sha256sum "target/package/openengine-${version}.crate" | cut --delimiter=' ' --fields=1)"
+ if [[ "${local_checksum}" != "${remote_checksum}" ]]; then
+ echo "::error::crates.io already has openengine ${version} with different contents"
+ exit 1
+ fi
+ echo "published=true" >> "${GITHUB_OUTPUT}"
+ ;;
+ 404)
+ echo "published=false" >> "${GITHUB_OUTPUT}"
+ ;;
+ *)
+ echo "::error::crates.io returned HTTP ${status}"
+ exit 1
+ ;;
+ esac
+
+ publish:
+ name: Publish crate
+ needs: validate
+ if: needs.validate.outputs.published != 'true'
+ runs-on: ubuntu-latest
+ environment: release
+ permissions:
+ contents: read
+ id-token: write
+ steps:
+ - name: Check out repository
+ uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.0.2
+
+ - name: Set up Rust
+ run: rustup toolchain install "${RUSTUP_TOOLCHAIN}" --profile minimal --no-self-update
+
+ - name: Authenticate with crates.io
+ id: auth
+ uses: rust-lang/crates-io-auth-action@c6f97d42243bad5fab37ca0427f495c86d5b1a18 # v1.0.5
+
+ - name: Publish to crates.io
+ env:
+ CARGO_REGISTRY_TOKEN: ${{ steps.auth.outputs.token }}
+ run: cargo publish --locked --package openengine
diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml
new file mode 100644
index 0000000..559c6e4
--- /dev/null
+++ b/.github/workflows/rust.yml
@@ -0,0 +1,104 @@
+name: Rust
+
+on:
+ push:
+ branches:
+ - main
+ paths:
+ - "proto/**"
+ - "packages/rust/**"
+ - "tools/rust-codegen/**"
+ - "scripts/check-generated.sh"
+ - "scripts/check-release-version.sh"
+ - "scripts/check-schema-release.sh"
+ - "scripts/generate-rust.sh"
+ - "Cargo.toml"
+ - "Cargo.lock"
+ - ".github/workflows/rust.yml"
+ - ".github/workflows/rust-release.yml"
+ pull_request:
+ paths:
+ - "proto/**"
+ - "packages/rust/**"
+ - "tools/rust-codegen/**"
+ - "scripts/check-generated.sh"
+ - "scripts/check-release-version.sh"
+ - "scripts/check-schema-release.sh"
+ - "scripts/generate-rust.sh"
+ - "Cargo.toml"
+ - "Cargo.lock"
+ - ".github/workflows/rust.yml"
+ - ".github/workflows/rust-release.yml"
+
+permissions:
+ contents: read
+
+jobs:
+ generated:
+ name: Generated bindings
+ runs-on: ubuntu-latest
+ steps:
+ - name: Check out repository
+ uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.0.2
+ with:
+ fetch-depth: 0
+
+ - name: Set up Rust
+ uses: dtolnay/rust-toolchain@master
+ with:
+ toolchain: stable
+
+ - name: Check schema release metadata
+ run: ./scripts/check-schema-release.sh
+
+ - name: Check generated bindings
+ run: ./scripts/check-generated.sh
+
+ check:
+ name: Rust ${{ matrix.toolchain }}
+ runs-on: ubuntu-latest
+ strategy:
+ fail-fast: false
+ matrix:
+ toolchain: ["1.88.0", "stable"]
+ steps:
+ - name: Check out repository
+ uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.0.2
+
+ - name: Set up Rust
+ uses: dtolnay/rust-toolchain@master
+ with:
+ toolchain: ${{ matrix.toolchain }}
+
+ - name: Check workspace
+ run: cargo check --locked --workspace --all-targets
+
+ quality:
+ name: Rust quality
+ runs-on: ubuntu-latest
+ steps:
+ - name: Check out repository
+ uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.0.2
+
+ - name: Set up Rust
+ uses: dtolnay/rust-toolchain@master
+ with:
+ toolchain: stable
+ components: clippy,rustfmt
+
+ - name: Check formatting
+ run: cargo fmt --all --check
+
+ - name: Run Clippy
+ run: cargo clippy --locked --workspace --all-targets -- -D warnings
+
+ - name: Build documentation
+ env:
+ RUSTDOCFLAGS: "-D warnings"
+ run: cargo doc --locked --no-deps --package openengine
+
+ - name: Build publishable crate
+ run: cargo package --locked --package openengine
+
+ - name: List packaged files
+ run: cargo package --locked --package openengine --list
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..a88093b
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,17 @@
+
+
+# Changelog
+
+All notable changes to the generated OpenEngine Rust crate are documented here.
+
+## [Unreleased]
+
+### Added
+
+- Generated `openengine` Prost messages and Tonic client/server bindings.
+- Reproducible Rust code generation, package CI, and tag-driven crates.io releases.
+
+[Unreleased]: https://github.com/ai-dynamo/openengine/commits/main
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 194c851..9773008 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -30,7 +30,27 @@ sending a PR.
- **Bugs / feedback / design questions**: open a [GitHub issue](https://github.com/ai-dynamo/openengine/issues).
- **Pull requests**: open against `main`. Keep changes focused (one logical change per PR).
-## Signing Your Work
+## Development checks
+
+The schema under `proto/openengine/v1/` is the source of truth. Generated Rust bindings are checked in for crate consumers and must be updated in the same pull request as a schema change.
+
+```bash
+buf build
+buf lint
+
+./scripts/generate-rust.sh
+./scripts/check-generated.sh
+./scripts/check-schema-release.sh
+
+cargo check --locked --workspace --all-targets
+cargo clippy --locked --workspace --all-targets -- -D warnings
+cargo doc --locked --no-deps --package openengine
+cargo package --locked --package openengine
+```
+
+The Rust code-generation toolchain is pinned. Do not edit generated files by hand; update the schema or generator and regenerate them.
+
+## Signing your work
We require that all contributors "sign off" on their commits. This certifies that
you wrote the contribution, or otherwise have the right to submit it under the
diff --git a/Cargo.lock b/Cargo.lock
new file mode 100644
index 0000000..f7ea283
--- /dev/null
+++ b/Cargo.lock
@@ -0,0 +1,1039 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 4
+
+[[package]]
+name = "aho-corasick"
+version = "1.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c982642fa9e8606056828ee9a8505737230110bb1099153c79efe865c59d12ba"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "anyhow"
+version = "1.0.104"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "330a5ed07fa54e4702c9d6c4174f74427fc0ef6e214bbd677ae50a5099946470"
+
+[[package]]
+name = "async-trait"
+version = "0.1.92"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "82f6aeea286b8eb4dd3431a1be1b59d290ace00f5bfd8e2a159bc2a05e2c1667"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 3.0.4",
+]
+
+[[package]]
+name = "atomic-waker"
+version = "1.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
+
+[[package]]
+name = "axum"
+version = "0.8.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "31b698c5f9a010f6573133b09e0de5408834d0c82f8d7475a89fc1867a71cd90"
+dependencies = [
+ "axum-core",
+ "bytes",
+ "futures-util",
+ "http",
+ "http-body",
+ "http-body-util",
+ "itoa",
+ "matchit",
+ "memchr",
+ "mime",
+ "percent-encoding",
+ "pin-project-lite",
+ "serde_core",
+ "sync_wrapper",
+ "tower",
+ "tower-layer",
+ "tower-service",
+]
+
+[[package]]
+name = "axum-core"
+version = "0.5.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "08c78f31d7b1291f7ee735c1c6780ccde7785daae9a9206026862dab7d8792d1"
+dependencies = [
+ "bytes",
+ "futures-core",
+ "http",
+ "http-body",
+ "http-body-util",
+ "mime",
+ "pin-project-lite",
+ "sync_wrapper",
+ "tower-layer",
+ "tower-service",
+]
+
+[[package]]
+name = "base64"
+version = "0.22.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
+
+[[package]]
+name = "bitflags"
+version = "2.13.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b588b76d00fde79687d7646a9b5bdf3cc0f655e0bbd080335a95d7e96f3587da"
+
+[[package]]
+name = "bytes"
+version = "1.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fc652a48c352aef3ea3aed32080501cf3ef6ed5da78602a020c991775b0aff04"
+
+[[package]]
+name = "cfg-if"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
+
+[[package]]
+name = "either"
+version = "1.18.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "252afb9ae5eaa683babdc6a068b3f5726eb19e05070c731f9b2a23a7c3e8ed34"
+
+[[package]]
+name = "equivalent"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
+
+[[package]]
+name = "errno"
+version = "0.3.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
+dependencies = [
+ "libc",
+ "windows-sys",
+]
+
+[[package]]
+name = "fastrand"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "da7c62ceae207dd37ea5b845da6a0696c799f85e97da1ab5b7910be3c1c80223"
+
+[[package]]
+name = "fixedbitset"
+version = "0.5.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99"
+
+[[package]]
+name = "fnv"
+version = "1.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
+
+[[package]]
+name = "foldhash"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
+
+[[package]]
+name = "futures-channel"
+version = "0.3.34"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b1f9e3d69d39e4862ffed03ed071a76f9a13ba1d9109d355b0f0aa6b15e393c4"
+dependencies = [
+ "futures-core",
+]
+
+[[package]]
+name = "futures-core"
+version = "0.3.34"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "92d699e522242e69e3003b94ecc1f960f3a5e015aa7c5d7486e65ad01dd94f5e"
+
+[[package]]
+name = "futures-sink"
+version = "0.3.34"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1944426bf7d03f1d14f708785e4b33efd750b36d48a157b836b3efc15ede8e1d"
+
+[[package]]
+name = "futures-task"
+version = "0.3.34"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cd417de3d1d015fc3bfd2b1ea46dfc7bab72ef86f1cc7cc9c78e728b34a6d1fd"
+
+[[package]]
+name = "futures-util"
+version = "0.3.34"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0d50a92467f8ba5dd6e3ee5d4bd04d73ab2e4e1c44474a0674821dfce14b79bc"
+dependencies = [
+ "futures-core",
+ "futures-task",
+ "pin-project-lite",
+ "slab",
+]
+
+[[package]]
+name = "getrandom"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "r-efi",
+]
+
+[[package]]
+name = "h2"
+version = "0.4.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ef8e5e5a340588f4452631496976cf8636d4a7ecf600239fdc27615d2530bc16"
+dependencies = [
+ "atomic-waker",
+ "bytes",
+ "fnv",
+ "futures-core",
+ "futures-sink",
+ "http",
+ "indexmap",
+ "slab",
+ "tokio",
+ "tokio-util",
+ "tracing",
+]
+
+[[package]]
+name = "hashbrown"
+version = "0.15.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
+dependencies = [
+ "foldhash",
+]
+
+[[package]]
+name = "hashbrown"
+version = "0.17.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a"
+
+[[package]]
+name = "heck"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
+
+[[package]]
+name = "http"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "918d3568bebf352712bc2ef3d46a8bcf1a75b373be6539de198e9105cbbf9ce0"
+dependencies = [
+ "bytes",
+ "itoa",
+]
+
+[[package]]
+name = "http-body"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ca2a8f2913ee65f60facd6a5905613afaa448497a0230cc41ce022d93290bc2c"
+dependencies = [
+ "bytes",
+ "http",
+]
+
+[[package]]
+name = "http-body-util"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "23169fe34a5fbcdd3f3862e78fb9b6fccd5f02a6dc6f732547005d45631ce71c"
+dependencies = [
+ "bytes",
+ "futures-core",
+ "http",
+ "http-body",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "httparse"
+version = "1.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87"
+
+[[package]]
+name = "httpdate"
+version = "1.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
+
+[[package]]
+name = "hyper"
+version = "1.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "27b501faa50e7a26c3d3560ca625132f4078a17771f4810baf70475ae48cbe43"
+dependencies = [
+ "atomic-waker",
+ "bytes",
+ "futures-channel",
+ "futures-core",
+ "h2",
+ "http",
+ "http-body",
+ "httparse",
+ "httpdate",
+ "itoa",
+ "pin-project-lite",
+ "smallvec",
+ "tokio",
+ "want",
+]
+
+[[package]]
+name = "hyper-timeout"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2b90d566bffbce6a75bd8b09a05aa8c2cb1fabb6cb348f8840c9e4c90a0d83b0"
+dependencies = [
+ "hyper",
+ "hyper-util",
+ "pin-project-lite",
+ "tokio",
+ "tower-service",
+]
+
+[[package]]
+name = "hyper-util"
+version = "0.1.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0"
+dependencies = [
+ "bytes",
+ "futures-channel",
+ "futures-util",
+ "http",
+ "http-body",
+ "hyper",
+ "libc",
+ "pin-project-lite",
+ "socket2",
+ "tokio",
+ "tower-service",
+ "tracing",
+]
+
+[[package]]
+name = "indexmap"
+version = "2.14.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "07aa2048142242915a31d35844fb311e0e53fcca590c3a0a40dcf1b841fa09eb"
+dependencies = [
+ "equivalent",
+ "hashbrown 0.17.1",
+]
+
+[[package]]
+name = "itertools"
+version = "0.14.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285"
+dependencies = [
+ "either",
+]
+
+[[package]]
+name = "itoa"
+version = "1.0.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
+
+[[package]]
+name = "libc"
+version = "0.2.189"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2"
+
+[[package]]
+name = "linux-raw-sys"
+version = "0.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53"
+
+[[package]]
+name = "log"
+version = "0.4.34"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f9f8bd3e56ce4dfc153cf470fffbfa98c7620958b312ca5c3a4b8d5181fd13c6"
+
+[[package]]
+name = "matchit"
+version = "0.8.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3"
+
+[[package]]
+name = "memchr"
+version = "2.8.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98"
+
+[[package]]
+name = "mime"
+version = "0.3.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
+
+[[package]]
+name = "mio"
+version = "1.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4b18443e9c262bfe8fa82f51666e2642c53393f7e5c27b3e1aeab922cff5b9d8"
+dependencies = [
+ "libc",
+ "wasi",
+ "windows-sys",
+]
+
+[[package]]
+name = "multimap"
+version = "0.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1d87ecb2933e8aeadb3e3a02b828fed80a7528047e68b4f424523a0981a3a084"
+
+[[package]]
+name = "once_cell"
+version = "1.21.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
+
+[[package]]
+name = "openengine"
+version = "0.1.0"
+dependencies = [
+ "prost",
+ "prost-types",
+ "tonic",
+ "tonic-prost",
+]
+
+[[package]]
+name = "openengine-rust-codegen"
+version = "0.0.0"
+dependencies = [
+ "protoc-bin-vendored",
+ "serde",
+ "serde_json",
+ "tonic-prost-build",
+]
+
+[[package]]
+name = "percent-encoding"
+version = "2.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220"
+
+[[package]]
+name = "petgraph"
+version = "0.8.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8701b58ea97060d5e5b155d383a69952a60943f0e6dfe30b04c287beb0b27455"
+dependencies = [
+ "fixedbitset",
+ "hashbrown 0.15.5",
+ "indexmap",
+]
+
+[[package]]
+name = "pin-project"
+version = "1.1.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2466b2336ed02bcdca6b294417127b90ec92038d1d5c4fbeac971a922e0e0924"
+dependencies = [
+ "pin-project-internal",
+]
+
+[[package]]
+name = "pin-project-internal"
+version = "1.1.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c96395f0a926bc13b1c17622aaddda1ecb55d49c8f1bf9777e4d877800a43f8b"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.119",
+]
+
+[[package]]
+name = "pin-project-lite"
+version = "0.2.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd"
+
+[[package]]
+name = "prettyplease"
+version = "0.2.37"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b"
+dependencies = [
+ "proc-macro2",
+ "syn 2.0.119",
+]
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.107"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "prost"
+version = "0.14.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "528ac67416ff8646872a3c02cad9cc4ee5dc9f9540c9b10771855c95cb2e5ae1"
+dependencies = [
+ "bytes",
+ "prost-derive",
+]
+
+[[package]]
+name = "prost-build"
+version = "0.14.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "03da047801ff44bb6a4d407d4860c05fd70bb81714e6b2f3812603d5b145b042"
+dependencies = [
+ "heck",
+ "itertools",
+ "log",
+ "multimap",
+ "petgraph",
+ "prettyplease",
+ "prost",
+ "prost-types",
+ "pulldown-cmark",
+ "pulldown-cmark-to-cmark",
+ "regex",
+ "syn 2.0.119",
+ "tempfile",
+]
+
+[[package]]
+name = "prost-derive"
+version = "0.14.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b570b25f7617e43d59005d0990ccb79e950a423952cea19671b7a876da390adf"
+dependencies = [
+ "anyhow",
+ "itertools",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.119",
+]
+
+[[package]]
+name = "prost-types"
+version = "0.14.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f94967dc7688f3054c7fac87473ffae4cc4c3904800e2d9f5b857246d8963b0a"
+dependencies = [
+ "prost",
+]
+
+[[package]]
+name = "protoc-bin-vendored"
+version = "3.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d1c381df33c98266b5f08186583660090a4ffa0889e76c7e9a5e175f645a67fa"
+dependencies = [
+ "protoc-bin-vendored-linux-aarch_64",
+ "protoc-bin-vendored-linux-ppcle_64",
+ "protoc-bin-vendored-linux-s390_64",
+ "protoc-bin-vendored-linux-x86_32",
+ "protoc-bin-vendored-linux-x86_64",
+ "protoc-bin-vendored-macos-aarch_64",
+ "protoc-bin-vendored-macos-x86_64",
+ "protoc-bin-vendored-win32",
+]
+
+[[package]]
+name = "protoc-bin-vendored-linux-aarch_64"
+version = "3.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c350df4d49b5b9e3ca79f7e646fde2377b199e13cfa87320308397e1f37e1a4c"
+
+[[package]]
+name = "protoc-bin-vendored-linux-ppcle_64"
+version = "3.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a55a63e6c7244f19b5c6393f025017eb5d793fd5467823a099740a7a4222440c"
+
+[[package]]
+name = "protoc-bin-vendored-linux-s390_64"
+version = "3.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1dba5565db4288e935d5330a07c264a4ee8e4a5b4a4e6f4e83fad824cc32f3b0"
+
+[[package]]
+name = "protoc-bin-vendored-linux-x86_32"
+version = "3.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8854774b24ee28b7868cd71dccaae8e02a2365e67a4a87a6cd11ee6cdbdf9cf5"
+
+[[package]]
+name = "protoc-bin-vendored-linux-x86_64"
+version = "3.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b38b07546580df720fa464ce124c4b03630a6fb83e05c336fea2a241df7e5d78"
+
+[[package]]
+name = "protoc-bin-vendored-macos-aarch_64"
+version = "3.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "89278a9926ce312e51f1d999fee8825d324d603213344a9a706daa009f1d8092"
+
+[[package]]
+name = "protoc-bin-vendored-macos-x86_64"
+version = "3.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "81745feda7ccfb9471d7a4de888f0652e806d5795b61480605d4943176299756"
+
+[[package]]
+name = "protoc-bin-vendored-win32"
+version = "3.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "95067976aca6421a523e491fce939a3e65249bac4b977adee0ee9771568e8aa3"
+
+[[package]]
+name = "pulldown-cmark"
+version = "0.13.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e9f068eba8e7071c5f9511831b44f32c740d5adf574e990f946ddb53db2f314e"
+dependencies = [
+ "bitflags",
+ "memchr",
+ "unicase",
+]
+
+[[package]]
+name = "pulldown-cmark-to-cmark"
+version = "22.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ab1ad36992cead65f02aa399a373a42730922f1525d988172634fdefdecb8a60"
+dependencies = [
+ "pulldown-cmark",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.47"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "r-efi"
+version = "6.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf"
+
+[[package]]
+name = "regex"
+version = "1.13.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f020237b6c8eed93db2e2cb53c00c60a8e1bc73da7d073199a1180401450218d"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-automata",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-automata"
+version = "0.4.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ad8553b9b26413251cbf30e620595c7a41b3887f03da04579c0e6b0d6a06b4b2"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-syntax"
+version = "0.8.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4"
+
+[[package]]
+name = "rustix"
+version = "1.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190"
+dependencies = [
+ "bitflags",
+ "errno",
+ "libc",
+ "linux-raw-sys",
+ "windows-sys",
+]
+
+[[package]]
+name = "serde"
+version = "1.0.229"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba"
+dependencies = [
+ "serde_core",
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_core"
+version = "1.0.229"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.229"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 3.0.4",
+]
+
+[[package]]
+name = "serde_json"
+version = "1.0.151"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c841b55ecdae098c80dcae9cf767f6f8a0c2cdb3416bbef72181df4d0fe73f14"
+dependencies = [
+ "itoa",
+ "memchr",
+ "serde",
+ "serde_core",
+ "zmij",
+]
+
+[[package]]
+name = "slab"
+version = "0.4.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5"
+
+[[package]]
+name = "smallvec"
+version = "1.16.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b9be42f50aa861c555654aa3a37f52f4b1074bacf4e48fe0ef7fa584e80f1f0f"
+
+[[package]]
+name = "socket2"
+version = "0.6.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c3d1e2c7f27f8d4cb10542a02c49005dbd6e93095799d6f3be745fae9f8fedd4"
+dependencies = [
+ "libc",
+ "windows-sys",
+]
+
+[[package]]
+name = "syn"
+version = "2.0.119"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "syn"
+version = "3.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e6275cddf4610d1775e6d1fe9469b2e77d0f39fd98fb7450901b821e0c53649f"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "sync_wrapper"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263"
+
+[[package]]
+name = "tempfile"
+version = "3.27.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd"
+dependencies = [
+ "fastrand",
+ "getrandom",
+ "once_cell",
+ "rustix",
+ "windows-sys",
+]
+
+[[package]]
+name = "tokio"
+version = "1.53.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "202caea871b69668250d242070849eb495be178ed697a3e98aebce5bc81a0bed"
+dependencies = [
+ "bytes",
+ "libc",
+ "mio",
+ "pin-project-lite",
+ "socket2",
+ "tokio-macros",
+ "windows-sys",
+]
+
+[[package]]
+name = "tokio-macros"
+version = "2.7.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "78773a2a397f451582ce068015985c33193cf6dea8b74d2a639fe457b2f07b0e"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 3.0.4",
+]
+
+[[package]]
+name = "tokio-stream"
+version = "0.1.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a3d06f0b082ba57c26b79407372e57cf2a1e28124f78e9479fe80322cf53420b"
+dependencies = [
+ "futures-core",
+ "pin-project-lite",
+ "tokio",
+]
+
+[[package]]
+name = "tokio-util"
+version = "0.7.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "494815d09bf52b5548659851081238f0ca39ff638363907596da739561c62c52"
+dependencies = [
+ "bytes",
+ "futures-core",
+ "futures-sink",
+ "libc",
+ "pin-project-lite",
+ "tokio",
+]
+
+[[package]]
+name = "tonic"
+version = "0.14.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac2a5518c70fa84342385732db33fb3f44bc4cc748936eb5833d2df34d6445ef"
+dependencies = [
+ "async-trait",
+ "axum",
+ "base64",
+ "bytes",
+ "h2",
+ "http",
+ "http-body",
+ "http-body-util",
+ "hyper",
+ "hyper-timeout",
+ "hyper-util",
+ "percent-encoding",
+ "pin-project",
+ "socket2",
+ "sync_wrapper",
+ "tokio",
+ "tokio-stream",
+ "tower",
+ "tower-layer",
+ "tower-service",
+ "tracing",
+]
+
+[[package]]
+name = "tonic-build"
+version = "0.14.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c68f61875ac5293cf72e6c8cf0158086428c82c37229e98c840878f1706b0322"
+dependencies = [
+ "prettyplease",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.119",
+]
+
+[[package]]
+name = "tonic-prost"
+version = "0.14.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "50849f68853be452acf590cde0b146665b8d507b3b8af17261df47e02c209ea0"
+dependencies = [
+ "bytes",
+ "prost",
+ "tonic",
+]
+
+[[package]]
+name = "tonic-prost-build"
+version = "0.14.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "654e5643eff75d7f8c99197ce1440ed19a3474eada74c12bbac488b2cafdae27"
+dependencies = [
+ "prettyplease",
+ "proc-macro2",
+ "prost-build",
+ "prost-types",
+ "quote",
+ "syn 2.0.119",
+ "tempfile",
+ "tonic-build",
+]
+
+[[package]]
+name = "tower"
+version = "0.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4"
+dependencies = [
+ "futures-core",
+ "futures-util",
+ "indexmap",
+ "pin-project-lite",
+ "slab",
+ "sync_wrapper",
+ "tokio",
+ "tokio-util",
+ "tower-layer",
+ "tower-service",
+ "tracing",
+]
+
+[[package]]
+name = "tower-layer"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e"
+
+[[package]]
+name = "tower-service"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3"
+
+[[package]]
+name = "tracing"
+version = "0.1.44"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100"
+dependencies = [
+ "pin-project-lite",
+ "tracing-attributes",
+ "tracing-core",
+]
+
+[[package]]
+name = "tracing-attributes"
+version = "0.1.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.119",
+]
+
+[[package]]
+name = "tracing-core"
+version = "0.1.36"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a"
+dependencies = [
+ "once_cell",
+]
+
+[[package]]
+name = "try-lock"
+version = "0.2.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
+
+[[package]]
+name = "unicase"
+version = "2.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dbc4bc3a9f746d862c45cb89d705aa10f187bb96c76001afab07a0d35ce60142"
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.24"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
+
+[[package]]
+name = "want"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e"
+dependencies = [
+ "try-lock",
+]
+
+[[package]]
+name = "wasi"
+version = "0.11.1+wasi-snapshot-preview1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
+
+[[package]]
+name = "windows-link"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
+
+[[package]]
+name = "windows-sys"
+version = "0.61.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
+dependencies = [
+ "windows-link",
+]
+
+[[package]]
+name = "zmij"
+version = "1.0.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b"
diff --git a/Cargo.toml b/Cargo.toml
new file mode 100644
index 0000000..e2d59f4
--- /dev/null
+++ b/Cargo.toml
@@ -0,0 +1,13 @@
+[workspace]
+members = [
+ "packages/rust/openengine",
+ "tools/rust-codegen",
+]
+resolver = "2"
+
+[workspace.package]
+version = "0.1.0"
+edition = "2021"
+rust-version = "1.88"
+license = "Apache-2.0"
+repository = "https://github.com/ai-dynamo/openengine"
diff --git a/README.md b/README.md
index 6b501b4..7de2739 100644
--- a/README.md
+++ b/README.md
@@ -25,6 +25,7 @@ SPDX-License-Identifier: Apache-2.0
· Canonical schema
· Release v0.1.0
· Consume from Buf
+ · Rust crate
· Contributing
@@ -40,6 +41,7 @@ SPDX-License-Identifier: Apache-2.0
- [Getting started](#getting-started)
- [Release v0.1.0](#release-v010)
- [Consume from Buf](#consume-from-buf)
+- [Rust crate](#rust-crate)
- [Project status](#project-status)
- [Contributing](#contributing)
- [Security](#security)
@@ -150,7 +152,7 @@ Use the immutable BSR commit as the dependency identifier. The `v0.1.0` and `mai
## Consume from Buf
-OpenEngine is distributed as the [`buf.build/openengine/openengine`](https://buf.build/openengine/openengine) module. Consumers can use [BSR-generated SDKs](https://buf.build/docs/bsr/generated-sdks/) or generate bindings with their own version-pinned plugins. OpenEngine does not currently maintain first-party language packages.
+OpenEngine is distributed as the [`buf.build/openengine/openengine`](https://buf.build/openengine/openengine) module. Consumers can use [BSR-generated SDKs](https://buf.build/docs/bsr/generated-sdks/) or generate bindings with their own version-pinned plugins. Rust users can instead install the first-party crate described below.
Generate bindings for v0.1.0 from the immutable module input with the consumer's language-specific `buf.gen.yaml`:
@@ -167,6 +169,20 @@ OpenEngine source commit instead.
See [`RELEASING.md`](RELEASING.md) for BSR publication.
+## Rust crate
+
+The `openengine` crate provides generated Prost messages and Tonic client/server bindings for `openengine.v1`:
+
+```bash
+cargo add openengine
+```
+
+```rust
+use openengine::v1::{control_client::ControlClient, inference_client::InferenceClient};
+```
+
+The crate contains checked-in generated source and a complete protobuf descriptor set. Consumer builds do not run Buf or `protoc`. The crate also exposes the schema revision and immutable BSR commit corresponding to its bindings.
+
## Project status
OpenEngine v0.1.0 is an experimental, pre-adoption release. The current focus is making the contract coherent across inference engines before implementations depend on it. Expect direct schema refinement during this phase.
@@ -194,7 +210,7 @@ git commit --signoff -m "docs: describe the change"
Please validate protobuf changes with Buf and keep
[`proto/openengine/v1/`](proto/openengine/v1/) and [`docs/api.md`](docs/api.md)
-synchronized.
+synchronized. Schema changes must also regenerate and commit the Rust bindings.
## Security
diff --git a/RELEASING.md b/RELEASING.md
index 586c331..a50cb46 100644
--- a/RELEASING.md
+++ b/RELEASING.md
@@ -5,7 +5,7 @@ SPDX-License-Identifier: Apache-2.0
# Releasing OpenEngine
-OpenEngine publishes its canonical Protobuf schema as `buf.build/openengine/openengine`. The project does not currently maintain first-party language packages. Consumers may use BSR-generated SDKs or generate bindings with their own version-pinned plugins from an immutable BSR module commit.
+OpenEngine publishes its canonical Protobuf schema as `buf.build/openengine/openengine` and generated Rust bindings as the `openengine` crate on crates.io. Schema releases use `vMAJOR.MINOR.PATCH` tags; crate releases use separate `openengine-vMAJOR.MINOR.PATCH` tags so each crate can identify an already-published immutable BSR commit.
## Prepare a release
@@ -59,3 +59,50 @@ Maintainers may also run the workflow manually from `main`. A manual publication
8. Update `README.md` and `proto/openengine/v1/README.md` on `main` with the immutable BSR commit assigned during publication.
Consumers may use release labels for discovery, but must not use a moving label as their production dependency.
+
+## Release the Rust crate
+
+### One-time registry configuration
+
+The crate must exist before crates.io allows a Trusted Publisher to be configured. Bootstrap the first release as follows:
+
+1. Create the `release` GitHub environment and protect it with the desired approval policy.
+2. Prepare and merge the crate release commit using the process below.
+3. Create the signed `openengine-vMAJOR.MINOR.PATCH` tag locally, but do not push it yet.
+4. Create a short-lived crates.io token authorized to publish a new crate, install the release workflow's pinned Rust toolchain with `rustup toolchain install 1.98.1 --profile minimal`, publish from the tagged commit with `CARGO_REGISTRY_TOKEN=... cargo +1.98.1 publish --locked --package openengine`, and immediately revoke the token.
+5. Add the project maintainers or an `ai-dynamo` GitHub team as crate owners.
+6. Configure a crates.io Trusted Publisher for GitHub owner `ai-dynamo`, repository `openengine`, workflow `rust-release.yml`, and environment `release`.
+7. Push the signed tag. The workflow verifies that the existing crates.io archive matches the tagged source and skips a duplicate publication.
+
+Subsequent releases use crates.io Trusted Publishing and do not require a stored registry token.
+
+### Prepare a crate release
+
+1. Identify the published schema release for the bindings. If the schema changed, publish and verify it through the BSR process above first.
+2. Update the workspace package version in `Cargo.toml` and the crate version, schema revision, immutable BSR module commit, Git tag, and Git commit in `packages/rust/openengine/schema-release.json`. Update the pinned schema link in `packages/rust/openengine/README.md`.
+3. Regenerate and validate the package:
+
+ ```bash
+ ./scripts/generate-rust.sh
+ ./scripts/check-generated.sh
+ ./scripts/check-schema-release.sh --verify-registry
+ cargo check --locked --workspace --all-targets
+ cargo clippy --locked --workspace --all-targets -- -D warnings
+ cargo doc --locked --no-deps --package openengine
+ cargo package --locked --package openengine
+ ```
+
+4. Update `CHANGELOG.md`, then open and merge the release-preparation pull request.
+
+### Publish a crate release
+
+Create and push a signed tag from the merged release commit:
+
+```bash
+VERSION=0.2.0
+./scripts/check-release-version.sh "openengine-v${VERSION}"
+git tag --sign "openengine-v${VERSION}" -m "OpenEngine Rust crate ${VERSION}"
+git push origin "openengine-v${VERSION}"
+```
+
+The `Rust crate release` workflow verifies the tag, generated bindings, and packaged archive before obtaining a short-lived crates.io token through OpenID Connect and publishing the crate. Published crate versions cannot be replaced; yank a broken release and prepare a new patch version.
diff --git a/packages/rust/openengine/Cargo.toml b/packages/rust/openengine/Cargo.toml
new file mode 100644
index 0000000..6a22582
--- /dev/null
+++ b/packages/rust/openengine/Cargo.toml
@@ -0,0 +1,20 @@
+[package]
+name = "openengine"
+version.workspace = true
+edition.workspace = true
+rust-version.workspace = true
+license.workspace = true
+repository.workspace = true
+description = "Generated Rust bindings for the OpenEngine gRPC protocol"
+readme = "README.md"
+homepage = "https://github.com/ai-dynamo/openengine"
+documentation = "https://docs.rs/openengine"
+keywords = ["grpc", "inference", "protobuf"]
+categories = ["api-bindings", "network-programming"]
+publish = ["crates-io"]
+
+[dependencies]
+prost = "0.14.4"
+prost-types = "0.14.4"
+tonic = "0.14.6"
+tonic-prost = "0.14.6"
diff --git a/packages/rust/openengine/LICENSE b/packages/rust/openengine/LICENSE
new file mode 100644
index 0000000..4c9ad98
--- /dev/null
+++ b/packages/rust/openengine/LICENSE
@@ -0,0 +1,201 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/packages/rust/openengine/README.md b/packages/rust/openengine/README.md
new file mode 100644
index 0000000..846cafc
--- /dev/null
+++ b/packages/rust/openengine/README.md
@@ -0,0 +1,21 @@
+
+
+# OpenEngine Rust bindings
+
+Generated Prost messages and Tonic client/server bindings for the
+[`openengine.v1`](https://github.com/ai-dynamo/openengine/tree/v0.1.0/proto/openengine/v1)
+protocol.
+
+```bash
+cargo add openengine
+```
+
+```rust
+use openengine::v1::{control_client::ControlClient, inference_client::InferenceClient};
+```
+
+The crate contains generated Rust source and a protobuf descriptor set.
+Consumer builds do not run `protoc`.
diff --git a/packages/rust/openengine/schema-release.json b/packages/rust/openengine/schema-release.json
new file mode 100644
index 0000000..759858e
--- /dev/null
+++ b/packages/rust/openengine/schema-release.json
@@ -0,0 +1,7 @@
+{
+ "crate_version": "0.1.0",
+ "schema_revision": 1,
+ "schema_release": "768a93c7b44e40f28c692ad0b471a8f2",
+ "schema_git_tag": "v0.1.0",
+ "schema_git_commit": "b5f2bd93721f7b888d3e2440679e0ae7012939d1"
+}
diff --git a/packages/rust/openengine/src/generated/openengine.v1.rs b/packages/rust/openengine/src/generated/openengine.v1.rs
new file mode 100644
index 0000000..d04b061
--- /dev/null
+++ b/packages/rust/openengine/src/generated/openengine.v1.rs
@@ -0,0 +1,2425 @@
+// This file is @generated by prost-build.
+/// Accepted failures emit one terminal EngineError and close with OK.
+/// Validation and transport failures use non-OK gRPC status instead.
+#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
+pub struct EngineError {
+ #[prost(enumeration = "ErrorCode", tag = "1")]
+ pub code: i32,
+ #[prost(string, tag = "2")]
+ pub message: ::prost::alloc::string::String,
+ /// Retry may succeed without changing the request.
+ #[prost(bool, tag = "3")]
+ pub retryable: bool,
+}
+#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)]
+#[repr(i32)]
+pub enum ErrorCode {
+ Unspecified = 0,
+ InvalidArgument = 1,
+ UnsupportedFeature = 2,
+ RoleMismatch = 3,
+ ModelNotFound = 4,
+ Overloaded = 5,
+ RequestNotFound = 6,
+ DuplicateRequest = 7,
+ KvSessionNotFound = 8,
+ KvTransferFailed = 9,
+ Cancelled = 10,
+ Internal = 11,
+}
+impl ErrorCode {
+ /// String value of the enum field names used in the ProtoBuf definition.
+ ///
+ /// The values are not transformed in any way and thus are considered stable
+ /// (if the ProtoBuf definition does not change) and safe for programmatic use.
+ pub fn as_str_name(&self) -> &'static str {
+ match self {
+ Self::Unspecified => "ERROR_CODE_UNSPECIFIED",
+ Self::InvalidArgument => "ERROR_CODE_INVALID_ARGUMENT",
+ Self::UnsupportedFeature => "ERROR_CODE_UNSUPPORTED_FEATURE",
+ Self::RoleMismatch => "ERROR_CODE_ROLE_MISMATCH",
+ Self::ModelNotFound => "ERROR_CODE_MODEL_NOT_FOUND",
+ Self::Overloaded => "ERROR_CODE_OVERLOADED",
+ Self::RequestNotFound => "ERROR_CODE_REQUEST_NOT_FOUND",
+ Self::DuplicateRequest => "ERROR_CODE_DUPLICATE_REQUEST",
+ Self::KvSessionNotFound => "ERROR_CODE_KV_SESSION_NOT_FOUND",
+ Self::KvTransferFailed => "ERROR_CODE_KV_TRANSFER_FAILED",
+ Self::Cancelled => "ERROR_CODE_CANCELLED",
+ Self::Internal => "ERROR_CODE_INTERNAL",
+ }
+ }
+ /// Creates an enum from field names used in the ProtoBuf definition.
+ pub fn from_str_name(value: &str) -> ::core::option::Option {
+ match value {
+ "ERROR_CODE_UNSPECIFIED" => Some(Self::Unspecified),
+ "ERROR_CODE_INVALID_ARGUMENT" => Some(Self::InvalidArgument),
+ "ERROR_CODE_UNSUPPORTED_FEATURE" => Some(Self::UnsupportedFeature),
+ "ERROR_CODE_ROLE_MISMATCH" => Some(Self::RoleMismatch),
+ "ERROR_CODE_MODEL_NOT_FOUND" => Some(Self::ModelNotFound),
+ "ERROR_CODE_OVERLOADED" => Some(Self::Overloaded),
+ "ERROR_CODE_REQUEST_NOT_FOUND" => Some(Self::RequestNotFound),
+ "ERROR_CODE_DUPLICATE_REQUEST" => Some(Self::DuplicateRequest),
+ "ERROR_CODE_KV_SESSION_NOT_FOUND" => Some(Self::KvSessionNotFound),
+ "ERROR_CODE_KV_TRANSFER_FAILED" => Some(Self::KvTransferFailed),
+ "ERROR_CODE_CANCELLED" => Some(Self::Cancelled),
+ "ERROR_CODE_INTERNAL" => Some(Self::Internal),
+ _ => None,
+ }
+ }
+}
+#[derive(Clone, PartialEq, ::prost::Message)]
+pub struct KvSessionRef {
+ #[prost(string, tag = "1")]
+ pub session_id: ::prost::alloc::string::String,
+ #[prost(string, tag = "2")]
+ pub transfer_backend: ::prost::alloc::string::String,
+ #[prost(message, repeated, tag = "3")]
+ pub endpoints: ::prost::alloc::vec::Vec,
+ #[prost(uint32, tag = "4")]
+ pub dp_rank: u32,
+ /// Engine-specific KV-transfer and rendezvous parameters, carried as a Struct
+ /// so numbers, booleans, and arrays survive the wire with their JSON type
+ /// intact. Struct numbers are IEEE-754 doubles (exact only to 2^53); carry
+ /// larger integer values as decimal strings.
+ #[prost(message, optional, tag = "5")]
+ pub attributes_struct: ::core::option::Option<::prost_types::Struct>,
+}
+#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
+pub struct KvEndpoint {
+ #[prost(string, tag = "1")]
+ pub host: ::prost::alloc::string::String,
+ #[prost(uint32, tag = "2")]
+ pub port: u32,
+ /// grpc, nixl, ucx, tcp, shm, etc.
+ #[prost(string, tag = "3")]
+ pub protocol: ::prost::alloc::string::String,
+}
+#[derive(Clone, PartialEq, ::prost::Message)]
+pub struct KvConnectorInfo {
+ #[prost(bool, optional, tag = "1")]
+ pub enabled: ::core::option::Option,
+ #[prost(string, tag = "2")]
+ pub transfer_backend: ::prost::alloc::string::String,
+ #[prost(message, repeated, tag = "3")]
+ pub local_endpoints: ::prost::alloc::vec::Vec,
+ #[prost(string, repeated, tag = "4")]
+ pub supported_protocols: ::prost::alloc::vec::Vec<::prost::alloc::string::String>,
+ #[prost(bool, optional, tag = "5")]
+ pub supports_remote_prefill: ::core::option::Option,
+ #[prost(bool, optional, tag = "6")]
+ pub supports_decode_pull: ::core::option::Option,
+ #[prost(bool, optional, tag = "7")]
+ pub supports_abort_cleanup: ::core::option::Option,
+ #[prost(uint32, optional, tag = "8")]
+ pub schema_version: ::core::option::Option,
+}
+#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
+pub struct GetKvEventSourcesRequest {
+ #[prost(uint32, repeated, tag = "1")]
+ pub data_parallel_ranks: ::prost::alloc::vec::Vec,
+}
+#[derive(Clone, PartialEq, ::prost::Message)]
+pub struct GetKvEventSourcesResponse {
+ #[prost(message, repeated, tag = "1")]
+ pub sources: ::prost::alloc::vec::Vec,
+}
+#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
+pub struct KvEventSource {
+ /// grpc, zmq
+ #[prost(string, tag = "1")]
+ pub transport: ::prost::alloc::string::String,
+ /// Connectable address. MUST use a routable host, never a bind wildcard.
+ #[prost(message, optional, tag = "2")]
+ pub endpoint_addr: ::core::option::Option,
+ #[prost(string, tag = "3")]
+ pub topic: ::prost::alloc::string::String,
+ /// optional, for ZMQ replay
+ #[prost(string, tag = "4")]
+ pub replay_endpoint: ::prost::alloc::string::String,
+ #[prost(uint32, optional, tag = "5")]
+ pub data_parallel_rank: ::core::option::Option,
+ /// protobuf, msgpack
+ #[prost(string, tag = "6")]
+ pub encoding: ::prost::alloc::string::String,
+ #[prost(uint32, optional, tag = "7")]
+ pub schema_version: ::core::option::Option,
+ #[prost(uint32, optional, tag = "8")]
+ pub buffer_steps: ::core::option::Option,
+ #[prost(uint32, optional, tag = "9")]
+ pub hwm: ::core::option::Option,
+ #[prost(uint32, optional, tag = "10")]
+ pub max_queue_size: ::core::option::Option,
+}
+#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
+pub struct SubscribeKvEventsRequest {
+ #[prost(uint32, repeated, tag = "1")]
+ pub data_parallel_ranks: ::prost::alloc::vec::Vec,
+ #[prost(bool, tag = "2")]
+ pub include_snapshot: bool,
+ #[prost(uint64, tag = "3")]
+ pub start_sequence_number: u64,
+}
+#[derive(Clone, PartialEq, ::prost::Message)]
+pub struct SubscribeKvEventsResponse {
+ #[prost(oneof = "subscribe_kv_events_response::Event", tags = "1, 2")]
+ pub event: ::core::option::Option,
+}
+/// Nested message and enum types in `SubscribeKvEventsResponse`.
+pub mod subscribe_kv_events_response {
+ #[derive(Clone, PartialEq, ::prost::Oneof)]
+ pub enum Event {
+ #[prost(message, tag = "1")]
+ Batch(super::KvEventBatch),
+ /// Terminal.
+ #[prost(message, tag = "2")]
+ Error(super::EngineError),
+ }
+}
+#[derive(Clone, PartialEq, ::prost::Message)]
+pub struct KvEventBatch {
+ #[prost(uint64, tag = "1")]
+ pub sequence_number: u64,
+ #[prost(uint64, tag = "2")]
+ pub timestamp_unix_nanos: u64,
+ #[prost(uint32, tag = "3")]
+ pub data_parallel_rank: u32,
+ #[prost(message, repeated, tag = "4")]
+ pub events: ::prost::alloc::vec::Vec,
+}
+#[derive(Clone, PartialEq, ::prost::Message)]
+pub struct KvEvent {
+ #[prost(string, tag = "1")]
+ pub request_id: ::prost::alloc::string::String,
+ #[prost(message, optional, tag = "2")]
+ pub kv_session: ::core::option::Option,
+ #[prost(oneof = "kv_event::Event", tags = "10, 11, 12")]
+ pub event: ::core::option::Option,
+}
+/// Nested message and enum types in `KvEvent`.
+pub mod kv_event {
+ #[derive(Clone, PartialEq, ::prost::Oneof)]
+ pub enum Event {
+ #[prost(message, tag = "10")]
+ BlockStored(super::BlockStored),
+ #[prost(message, tag = "11")]
+ BlockRemoved(super::BlockRemoved),
+ #[prost(message, tag = "12")]
+ AllBlocksCleared(super::AllBlocksCleared),
+ }
+}
+#[derive(Clone, PartialEq, ::prost::Message)]
+pub struct BlockStored {
+ #[prost(message, repeated, tag = "1")]
+ pub block_hashes: ::prost::alloc::vec::Vec,
+ #[prost(message, optional, tag = "2")]
+ pub parent_block_hash: ::core::option::Option,
+ #[prost(uint32, repeated, tag = "3")]
+ pub token_ids: ::prost::alloc::vec::Vec,
+ #[prost(uint32, tag = "4")]
+ pub block_size: u32,
+ #[prost(int64, tag = "5")]
+ pub lora_id: i64,
+ #[prost(string, tag = "6")]
+ pub lora_name: ::prost::alloc::string::String,
+ #[prost(enumeration = "StorageMedium", tag = "7")]
+ pub medium: i32,
+ /// vLLM-compatible optional metadata for reconstructing block keys.
+ #[prost(message, repeated, tag = "20")]
+ pub extra_keys: ::prost::alloc::vec::Vec,
+ #[prost(uint32, tag = "21")]
+ pub group_idx: u32,
+ #[prost(string, tag = "22")]
+ pub kv_cache_spec_kind: ::prost::alloc::string::String,
+ #[prost(uint32, tag = "23")]
+ pub kv_cache_spec_sliding_window: u32,
+}
+#[derive(Clone, PartialEq, ::prost::Message)]
+pub struct BlockRemoved {
+ #[prost(message, repeated, tag = "1")]
+ pub block_hashes: ::prost::alloc::vec::Vec,
+ #[prost(enumeration = "StorageMedium", tag = "2")]
+ pub medium: i32,
+ #[prost(uint32, tag = "3")]
+ pub group_idx: u32,
+}
+#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)]
+pub struct AllBlocksCleared {}
+#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
+pub struct KvBlockHash {
+ #[prost(bytes = "vec", tag = "1")]
+ pub value: ::prost::alloc::vec::Vec,
+ /// int64, string, bytes, engine_specific
+ #[prost(string, tag = "2")]
+ pub encoding: ::prost::alloc::string::String,
+}
+#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
+pub struct OpaqueKeyTuple {
+ #[prost(string, repeated, tag = "1")]
+ pub values: ::prost::alloc::vec::Vec<::prost::alloc::string::String>,
+}
+#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)]
+#[repr(i32)]
+pub enum StorageMedium {
+ Unspecified = 0,
+ Gpu = 1,
+ CpuPinned = 2,
+ Disk = 3,
+ External = 4,
+}
+impl StorageMedium {
+ /// String value of the enum field names used in the ProtoBuf definition.
+ ///
+ /// The values are not transformed in any way and thus are considered stable
+ /// (if the ProtoBuf definition does not change) and safe for programmatic use.
+ pub fn as_str_name(&self) -> &'static str {
+ match self {
+ Self::Unspecified => "STORAGE_MEDIUM_UNSPECIFIED",
+ Self::Gpu => "STORAGE_MEDIUM_GPU",
+ Self::CpuPinned => "STORAGE_MEDIUM_CPU_PINNED",
+ Self::Disk => "STORAGE_MEDIUM_DISK",
+ Self::External => "STORAGE_MEDIUM_EXTERNAL",
+ }
+ }
+ /// Creates an enum from field names used in the ProtoBuf definition.
+ pub fn from_str_name(value: &str) -> ::core::option::Option {
+ match value {
+ "STORAGE_MEDIUM_UNSPECIFIED" => Some(Self::Unspecified),
+ "STORAGE_MEDIUM_GPU" => Some(Self::Gpu),
+ "STORAGE_MEDIUM_CPU_PINNED" => Some(Self::CpuPinned),
+ "STORAGE_MEDIUM_DISK" => Some(Self::Disk),
+ "STORAGE_MEDIUM_EXTERNAL" => Some(Self::External),
+ _ => None,
+ }
+ }
+}
+#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
+pub struct TokenIds {
+ #[prost(uint32, repeated, tag = "1")]
+ pub ids: ::prost::alloc::vec::Vec,
+}
+/// A single multimodal input. Exactly one `source` should be set. The engine
+/// owns fetch, decode, and preprocessing, so pre-decoded or RDMA media
+/// descriptors are not represented here.
+#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
+pub struct MediaItem {
+ #[prost(enumeration = "Modality", tag = "1")]
+ pub modality: i32,
+ /// optional, hints raw_bytes decode
+ #[prost(string, tag = "5")]
+ pub mime_type: ::prost::alloc::string::String,
+ /// optional caller id / mm_hash
+ #[prost(string, tag = "6")]
+ pub uuid: ::prost::alloc::string::String,
+ #[prost(oneof = "media_item::Source", tags = "2, 3, 4")]
+ pub source: ::core::option::Option,
+}
+/// Nested message and enum types in `MediaItem`.
+pub mod media_item {
+ #[derive(Clone, PartialEq, Eq, Hash, ::prost::Oneof)]
+ pub enum Source {
+ /// http(s):// -- engine fetches
+ #[prost(string, tag = "2")]
+ Url(::prost::alloc::string::String),
+ /// data:;base64,\<...> -- engine decodes
+ #[prost(string, tag = "3")]
+ DataUri(::prost::alloc::string::String),
+ /// pre-fetched bytes -- engine still preprocesses
+ #[prost(bytes, tag = "4")]
+ RawBytes(::prost::alloc::vec::Vec),
+ }
+}
+#[derive(Clone, PartialEq, ::prost::Message)]
+pub struct GenerateRequest {
+ #[prost(string, tag = "1")]
+ pub request_id: ::prost::alloc::string::String,
+ #[prost(string, tag = "2")]
+ pub model: ::prost::alloc::string::String,
+ #[prost(message, optional, tag = "5")]
+ pub sampling: ::core::option::Option,
+ #[prost(message, optional, tag = "6")]
+ pub stopping: ::core::option::Option,
+ #[prost(message, optional, tag = "7")]
+ pub response: ::core::option::Option,
+ #[prost(message, optional, tag = "8")]
+ pub kv: ::core::option::Option,
+ #[prost(message, optional, tag = "9")]
+ pub guided: ::core::option::Option,
+ /// Multimodal inputs. Order is significant: the i-th item aligns with the
+ /// i-th (un-expanded) placeholder marker carried in the prompt/token_ids.
+ /// The engine fetches/decodes and preprocesses each item, then expands the
+ /// marker into the model's replacement run. Empty for text-only requests.
+ #[prost(message, repeated, tag = "10")]
+ pub media: ::prost::alloc::vec::Vec,
+ /// Loaded LoRA adapter name to apply to this request. Empty = base model.
+ /// ModelInfo.supports_lora advertises whether lifecycle and selection are
+ /// available through OpenEngine.
+ #[prost(string, tag = "11")]
+ pub lora_name: ::prost::alloc::string::String,
+ /// Engine-specific request parameters that have no portable field above (e.g.
+ /// an experimental sampler knob). NOT part of the portable contract: an engine
+ /// MAY ignore keys it does not recognize, and a request MUST remain valid with
+ /// this field empty. Clients treat it as best-effort and never depend on it
+ /// for correctness. Carried as a Struct so JSON types survive the wire.
+ #[prost(message, optional, tag = "12")]
+ pub extra: ::core::option::Option<::prost_types::Struct>,
+ #[prost(oneof = "generate_request::Input", tags = "3, 4")]
+ pub input: ::core::option::Option,
+}
+/// Nested message and enum types in `GenerateRequest`.
+pub mod generate_request {
+ #[derive(Clone, PartialEq, Eq, Hash, ::prost::Oneof)]
+ pub enum Input {
+ #[prost(string, tag = "3")]
+ Prompt(::prost::alloc::string::String),
+ #[prost(message, tag = "4")]
+ TokenIds(super::TokenIds),
+ }
+}
+#[derive(Clone, Copy, PartialEq, ::prost::Message)]
+pub struct SamplingParams {
+ #[prost(double, optional, tag = "1")]
+ pub temperature: ::core::option::Option,
+ #[prost(double, optional, tag = "2")]
+ pub top_p: ::core::option::Option,
+ #[prost(int32, optional, tag = "3")]
+ pub top_k: ::core::option::Option,
+ #[prost(double, optional, tag = "4")]
+ pub min_p: ::core::option::Option,
+ #[prost(double, optional, tag = "5")]
+ pub frequency_penalty: ::core::option::Option,
+ #[prost(double, optional, tag = "6")]
+ pub presence_penalty: ::core::option::Option,
+ #[prost(double, optional, tag = "7")]
+ pub repetition_penalty: ::core::option::Option,
+ #[prost(uint64, optional, tag = "8")]
+ pub seed: ::core::option::Option,
+ #[prost(uint32, optional, tag = "9")]
+ pub num_sequences: ::core::option::Option,
+}
+#[derive(Clone, PartialEq, ::prost::Message)]
+pub struct StoppingOptions {
+ #[prost(uint32, optional, tag = "1")]
+ pub max_tokens: ::core::option::Option,
+ #[prost(uint32, optional, tag = "2")]
+ pub min_tokens: ::core::option::Option,
+ #[prost(message, repeated, tag = "3")]
+ pub conditions: ::prost::alloc::vec::Vec,
+ #[prost(bool, optional, tag = "4")]
+ pub ignore_eos: ::core::option::Option,
+ #[prost(bool, optional, tag = "5")]
+ pub include_stop_in_output: ::core::option::Option,
+}
+#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
+pub struct ResponseOptions {
+ #[prost(bool, optional, tag = "1")]
+ pub return_prompt_logprobs: ::core::option::Option,
+ #[prost(message, optional, tag = "2")]
+ pub prompt_candidates: ::core::option::Option,
+ #[prost(bool, optional, tag = "3")]
+ pub return_output_logprobs: ::core::option::Option,
+ #[prost(message, optional, tag = "4")]
+ pub output_candidates: ::core::option::Option,
+ #[prost(uint32, optional, tag = "5")]
+ pub prompt_logprob_start: ::core::option::Option,
+}
+#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
+pub struct CandidateTokenSelection {
+ #[prost(oneof = "candidate_token_selection::Selection", tags = "1, 2, 3")]
+ pub selection: ::core::option::Option,
+}
+/// Nested message and enum types in `CandidateTokenSelection`.
+pub mod candidate_token_selection {
+ #[derive(Clone, PartialEq, Eq, Hash, ::prost::Oneof)]
+ pub enum Selection {
+ #[prost(uint32, tag = "1")]
+ TopN(u32),
+ #[prost(message, tag = "2")]
+ TokenIds(super::TokenIds),
+ #[prost(message, tag = "3")]
+ All(super::AllCandidates),
+ }
+}
+#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)]
+pub struct AllCandidates {}
+#[derive(Clone, PartialEq, ::prost::Message)]
+pub struct KvOptions {
+ #[prost(message, optional, tag = "1")]
+ pub session: ::core::option::Option,
+ #[prost(bool, optional, tag = "2")]
+ pub bypass_prefix_cache: ::core::option::Option,
+ #[prost(string, optional, tag = "3")]
+ pub cache_salt: ::core::option::Option<::prost::alloc::string::String>,
+}
+#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
+pub struct StopCondition {
+ #[prost(oneof = "stop_condition::Condition", tags = "1, 2")]
+ pub condition: ::core::option::Option,
+}
+/// Nested message and enum types in `StopCondition`.
+pub mod stop_condition {
+ #[derive(Clone, PartialEq, Eq, Hash, ::prost::Oneof)]
+ pub enum Condition {
+ #[prost(string, tag = "1")]
+ StopText(::prost::alloc::string::String),
+ #[prost(uint32, tag = "2")]
+ StopTokenId(u32),
+ }
+}
+/// Constrained / guided decoding spec. At most one of `guide` should be set.
+/// The engine enforces the constraint during sampling via its grammar backend
+/// (xgrammar / outlines / llguidance); clients cannot apply it post-hoc.
+#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
+pub struct GuidedDecoding {
+ /// Optional grammar backend override (e.g. "xgrammar", "outlines",
+ /// "llguidance"). Empty = engine default.
+ #[prost(string, tag = "7")]
+ pub backend: ::prost::alloc::string::String,
+ #[prost(oneof = "guided_decoding::Guide", tags = "1, 2, 3, 4, 5, 6")]
+ pub guide: ::core::option::Option,
+}
+/// Nested message and enum types in `GuidedDecoding`.
+pub mod guided_decoding {
+ #[derive(Clone, PartialEq, Eq, Hash, ::prost::Oneof)]
+ pub enum Guide {
+ /// output conforms to this JSON schema
+ #[prost(string, tag = "1")]
+ JsonSchema(::prost::alloc::string::String),
+ /// output matches this regex
+ #[prost(string, tag = "2")]
+ Regex(::prost::alloc::string::String),
+ /// output follows this EBNF / context-free grammar
+ #[prost(string, tag = "3")]
+ EbnfGrammar(::prost::alloc::string::String),
+ /// xgrammar structural-tag constraint (JSON string)
+ #[prost(string, tag = "4")]
+ StructuralTag(::prost::alloc::string::String),
+ #[prost(message, tag = "5")]
+ Choice(super::ChoiceConstraint),
+ #[prost(message, tag = "6")]
+ JsonObject(super::JsonObjectConstraint),
+ }
+}
+#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
+pub struct ChoiceConstraint {
+ #[prost(string, repeated, tag = "1")]
+ pub choices: ::prost::alloc::vec::Vec<::prost::alloc::string::String>,
+}
+#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)]
+pub struct JsonObjectConstraint {}
+#[derive(Clone, PartialEq, ::prost::Message)]
+pub struct GenerateResponse {
+ #[prost(string, tag = "1")]
+ pub request_id: ::prost::alloc::string::String,
+ /// Cumulative request usage; only set on the final response.
+ #[prost(message, optional, tag = "10")]
+ pub usage: ::core::option::Option,
+ /// PromptOutput, PrefillReady, and EngineError are request-scoped.
+ /// TokenOutput and GenerationFinished are output-scoped.
+ #[prost(oneof = "generate_response::Event", tags = "2, 3, 4, 5, 6")]
+ pub event: ::core::option::Option,
+}
+/// Nested message and enum types in `GenerateResponse`.
+pub mod generate_response {
+ /// PromptOutput, PrefillReady, and EngineError are request-scoped.
+ /// TokenOutput and GenerationFinished are output-scoped.
+ #[derive(Clone, PartialEq, ::prost::Oneof)]
+ pub enum Event {
+ #[prost(message, tag = "2")]
+ Prompt(super::PromptOutput),
+ #[prost(message, tag = "3")]
+ Token(super::TokenOutput),
+ #[prost(message, tag = "4")]
+ PrefillReady(super::PrefillReady),
+ #[prost(message, tag = "5")]
+ Finished(super::GenerationFinished),
+ #[prost(message, tag = "6")]
+ Error(super::EngineError),
+ }
+}
+/// Request-scoped prompt token information, emitted at most once.
+#[derive(Clone, PartialEq, ::prost::Message)]
+pub struct PromptOutput {
+ #[prost(message, repeated, tag = "1")]
+ pub tokens: ::prost::alloc::vec::Vec,
+}
+/// An incremental output delta. Tokens and text are never cumulative.
+#[derive(Clone, PartialEq, ::prost::Message)]
+pub struct TokenOutput {
+ /// Required, including for output zero.
+ #[prost(uint32, optional, tag = "1")]
+ pub output_index: ::core::option::Option,
+ #[prost(message, repeated, tag = "2")]
+ pub tokens: ::prost::alloc::vec::Vec,
+ #[prost(string, tag = "3")]
+ pub text: ::prost::alloc::string::String,
+}
+#[derive(Clone, PartialEq, ::prost::Message)]
+pub struct TokenInfo {
+ #[prost(uint32, tag = "1")]
+ pub token_id: u32,
+ #[prost(string, tag = "2")]
+ pub token: ::prost::alloc::string::String,
+ #[prost(double, optional, tag = "3")]
+ pub logprob: ::core::option::Option,
+ #[prost(uint32, optional, tag = "4")]
+ pub rank: ::core::option::Option,
+ #[prost(message, repeated, tag = "5")]
+ pub candidates: ::prost::alloc::vec::Vec,
+}
+#[derive(Clone, PartialEq, ::prost::Message)]
+pub struct LogProb {
+ #[prost(uint32, tag = "1")]
+ pub token_id: u32,
+ #[prost(double, tag = "2")]
+ pub logprob: f64,
+ #[prost(string, tag = "3")]
+ pub token: ::prost::alloc::string::String,
+ #[prost(uint32, optional, tag = "4")]
+ pub rank: ::core::option::Option,
+}
+#[derive(Clone, PartialEq, ::prost::Message)]
+pub struct PrefillReady {
+ #[prost(message, optional, tag = "1")]
+ pub kv_session: ::core::option::Option,
+}
+#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
+pub struct GenerationFinished {
+ /// Required, including for output zero.
+ #[prost(uint32, optional, tag = "1")]
+ pub output_index: ::core::option::Option,
+ #[prost(enumeration = "FinishReason", tag = "2")]
+ pub reason: i32,
+ #[prost(string, tag = "3")]
+ pub message: ::prost::alloc::string::String,
+ #[prost(message, optional, tag = "4")]
+ pub stop_match: ::core::option::Option,
+}
+#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
+pub struct StopMatch {
+ #[prost(oneof = "stop_match::Match", tags = "1, 2, 3")]
+ pub r#match: ::core::option::Option,
+}
+/// Nested message and enum types in `StopMatch`.
+pub mod stop_match {
+ #[derive(Clone, PartialEq, Eq, Hash, ::prost::Oneof)]
+ pub enum Match {
+ #[prost(uint32, tag = "1")]
+ StopTokenId(u32),
+ #[prost(string, tag = "2")]
+ StopText(::prost::alloc::string::String),
+ #[prost(uint32, tag = "3")]
+ EosTokenId(u32),
+ }
+}
+#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)]
+pub struct Usage {
+ #[prost(uint32, tag = "1")]
+ pub prompt_tokens: u32,
+ #[prost(uint32, tag = "2")]
+ pub completion_tokens: u32,
+ #[prost(uint32, tag = "3")]
+ pub total_tokens: u32,
+ #[prost(uint32, optional, tag = "4")]
+ pub cached_prompt_tokens: ::core::option::Option,
+ #[prost(uint32, optional, tag = "5")]
+ pub reasoning_tokens: ::core::option::Option,
+}
+/// Multimodal modality discriminator. UNSPECIFIED means the sender left it unset.
+#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)]
+#[repr(i32)]
+pub enum Modality {
+ Unspecified = 0,
+ Image = 1,
+ Video = 2,
+ Audio = 3,
+}
+impl Modality {
+ /// String value of the enum field names used in the ProtoBuf definition.
+ ///
+ /// The values are not transformed in any way and thus are considered stable
+ /// (if the ProtoBuf definition does not change) and safe for programmatic use.
+ pub fn as_str_name(&self) -> &'static str {
+ match self {
+ Self::Unspecified => "MODALITY_UNSPECIFIED",
+ Self::Image => "MODALITY_IMAGE",
+ Self::Video => "MODALITY_VIDEO",
+ Self::Audio => "MODALITY_AUDIO",
+ }
+ }
+ /// Creates an enum from field names used in the ProtoBuf definition.
+ pub fn from_str_name(value: &str) -> ::core::option::Option {
+ match value {
+ "MODALITY_UNSPECIFIED" => Some(Self::Unspecified),
+ "MODALITY_IMAGE" => Some(Self::Image),
+ "MODALITY_VIDEO" => Some(Self::Video),
+ "MODALITY_AUDIO" => Some(Self::Audio),
+ _ => None,
+ }
+ }
+}
+#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)]
+#[repr(i32)]
+pub enum FinishReason {
+ Unspecified = 0,
+ Stop = 1,
+ Length = 2,
+ Cancelled = 3,
+}
+impl FinishReason {
+ /// String value of the enum field names used in the ProtoBuf definition.
+ ///
+ /// The values are not transformed in any way and thus are considered stable
+ /// (if the ProtoBuf definition does not change) and safe for programmatic use.
+ pub fn as_str_name(&self) -> &'static str {
+ match self {
+ Self::Unspecified => "FINISH_REASON_UNSPECIFIED",
+ Self::Stop => "FINISH_REASON_STOP",
+ Self::Length => "FINISH_REASON_LENGTH",
+ Self::Cancelled => "FINISH_REASON_CANCELLED",
+ }
+ }
+ /// Creates an enum from field names used in the ProtoBuf definition.
+ pub fn from_str_name(value: &str) -> ::core::option::Option {
+ match value {
+ "FINISH_REASON_UNSPECIFIED" => Some(Self::Unspecified),
+ "FINISH_REASON_STOP" => Some(Self::Stop),
+ "FINISH_REASON_LENGTH" => Some(Self::Length),
+ "FINISH_REASON_CANCELLED" => Some(Self::Cancelled),
+ _ => None,
+ }
+ }
+}
+#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)]
+pub struct GetServerInfoRequest {}
+#[derive(Clone, PartialEq, ::prost::Message)]
+pub struct ServerInfo {
+ /// sglang, vllm, tensorrt_llm, etc.
+ #[prost(string, tag = "1")]
+ pub engine_name: ::prost::alloc::string::String,
+ #[prost(string, tag = "2")]
+ pub engine_version: ::prost::alloc::string::String,
+ #[prost(enumeration = "EngineRole", tag = "3")]
+ pub engine_role: i32,
+ #[prost(string, tag = "4")]
+ pub instance_id: ::prost::alloc::string::String,
+ #[prost(string, repeated, tag = "5")]
+ pub supported_models: ::prost::alloc::vec::Vec<::prost::alloc::string::String>,
+ #[prost(message, optional, tag = "6")]
+ pub parallelism: ::core::option::Option,
+ #[prost(message, optional, tag = "7")]
+ pub kv_connector: ::core::option::Option,
+ /// Contract revision implemented; zero is invalid.
+ #[prost(uint32, tag = "8")]
+ pub schema_revision: u32,
+ /// Oldest compatible contract revision.
+ #[prost(uint32, tag = "9")]
+ pub minimum_client_revision: u32,
+ /// Immutable BSR module commit; unpublished builds may use a source commit.
+ #[prost(string, tag = "10")]
+ pub schema_release: ::prost::alloc::string::String,
+ /// Configured capacity for this deployed server.
+ #[prost(message, optional, tag = "11")]
+ pub capacity: ::core::option::Option,
+ /// Engine-specific server metadata with no portable field (e.g. attention
+ /// backend, build flags, experimental capabilities). NOT part of the portable
+ /// contract: clients read it opportunistically and never depend on it for
+ /// correctness. Carried as a Struct so JSON types survive the wire.
+ #[prost(message, optional, tag = "12")]
+ pub extra: ::core::option::Option<::prost_types::Struct>,
+}
+#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)]
+pub struct DeploymentCapacity {
+ /// Tokens per deployed KV-cache block.
+ #[prost(uint32, optional, tag = "1")]
+ pub kv_block_size: ::core::option::Option,
+ /// Allocatable KV blocks in the reporting scope.
+ #[prost(uint64, optional, tag = "2")]
+ pub total_kv_blocks: ::core::option::Option,
+ /// Concurrent running-request ceiling.
+ #[prost(uint64, optional, tag = "3")]
+ pub max_running_requests: ::core::option::Option,
+ /// Scheduler token ceiling per batch.
+ #[prost(uint64, optional, tag = "4")]
+ pub max_batched_tokens: ::core::option::Option,
+ /// Maximum simultaneously resident LoRA adapters.
+ #[prost(uint32, optional, tag = "5")]
+ pub max_loras: ::core::option::Option,
+}
+#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)]
+pub struct ParallelismInfo {
+ #[prost(uint32, optional, tag = "1")]
+ pub tensor_parallel_size: ::core::option::Option,
+ #[prost(uint32, optional, tag = "2")]
+ pub pipeline_parallel_size: ::core::option::Option,
+ #[prost(uint32, optional, tag = "3")]
+ pub data_parallel_size: ::core::option::Option,
+ #[prost(uint32, optional, tag = "4")]
+ pub data_parallel_rank: ::core::option::Option,
+ #[prost(uint32, optional, tag = "5")]
+ pub data_parallel_start_rank: ::core::option::Option,
+ /// Ranks per decode-context group; at least 1.
+ #[prost(uint32, optional, tag = "6")]
+ pub decode_context_parallel_size: ::core::option::Option,
+}
+#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)]
+pub struct GetLoadRequest {
+ #[prost(bool, tag = "1")]
+ pub include_per_rank: bool,
+}
+#[derive(Clone, PartialEq, ::prost::Message)]
+pub struct LoadInfo {
+ #[prost(string, tag = "1")]
+ pub instance_id: ::prost::alloc::string::String,
+ #[prost(uint64, optional, tag = "2")]
+ pub timestamp_unix_nanos: ::core::option::Option,
+ #[prost(uint32, optional, tag = "3")]
+ pub running_requests: ::core::option::Option,
+ #[prost(uint32, optional, tag = "4")]
+ pub queued_requests: ::core::option::Option,
+ #[prost(uint32, optional, tag = "5")]
+ pub active_kv_sessions: ::core::option::Option,
+ #[prost(uint64, optional, tag = "6")]
+ pub used_kv_blocks: ::core::option::Option,
+ #[prost(uint64, optional, tag = "7")]
+ pub total_kv_blocks: ::core::option::Option,
+ #[prost(uint64, optional, tag = "8")]
+ pub running_tokens: ::core::option::Option,
+ #[prost(uint64, optional, tag = "9")]
+ pub waiting_tokens: ::core::option::Option,
+ #[prost(uint32, optional, tag = "10")]
+ pub prefill_batch_size: ::core::option::Option,
+ #[prost(uint32, optional, tag = "11")]
+ pub decode_batch_size: ::core::option::Option,
+ #[prost(message, repeated, tag = "20")]
+ pub ranks: ::prost::alloc::vec::Vec,
+ #[prost(message, optional, tag = "30")]
+ pub attributes: ::core::option::Option<::prost_types::Struct>,
+}
+#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)]
+pub struct RankLoadInfo {
+ #[prost(uint32, optional, tag = "1")]
+ pub data_parallel_rank: ::core::option::Option,
+ #[prost(uint32, optional, tag = "2")]
+ pub running_requests: ::core::option::Option,
+ #[prost(uint32, optional, tag = "3")]
+ pub queued_requests: ::core::option::Option,
+ #[prost(uint64, optional, tag = "4")]
+ pub used_kv_blocks: ::core::option::Option,
+ #[prost(uint64, optional, tag = "5")]
+ pub total_kv_blocks: ::core::option::Option,
+ #[prost(uint32, optional, tag = "6")]
+ pub prefill_batch_size: ::core::option::Option,
+ #[prost(uint32, optional, tag = "7")]
+ pub decode_batch_size: ::core::option::Option,
+}
+#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)]
+#[repr(i32)]
+pub enum EngineRole {
+ Unspecified = 0,
+ Aggregated = 1,
+ Prefill = 2,
+ Decode = 3,
+}
+impl EngineRole {
+ /// String value of the enum field names used in the ProtoBuf definition.
+ ///
+ /// The values are not transformed in any way and thus are considered stable
+ /// (if the ProtoBuf definition does not change) and safe for programmatic use.
+ pub fn as_str_name(&self) -> &'static str {
+ match self {
+ Self::Unspecified => "ENGINE_ROLE_UNSPECIFIED",
+ Self::Aggregated => "ENGINE_ROLE_AGGREGATED",
+ Self::Prefill => "ENGINE_ROLE_PREFILL",
+ Self::Decode => "ENGINE_ROLE_DECODE",
+ }
+ }
+ /// Creates an enum from field names used in the ProtoBuf definition.
+ pub fn from_str_name(value: &str) -> ::core::option::Option {
+ match value {
+ "ENGINE_ROLE_UNSPECIFIED" => Some(Self::Unspecified),
+ "ENGINE_ROLE_AGGREGATED" => Some(Self::Aggregated),
+ "ENGINE_ROLE_PREFILL" => Some(Self::Prefill),
+ "ENGINE_ROLE_DECODE" => Some(Self::Decode),
+ _ => None,
+ }
+ }
+}
+#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
+pub struct HealthRequest {
+ /// False means a lightweight readiness/liveness check. True asks the engine to
+ /// run a role-appropriate minimal inference probe and report it as a check.
+ #[prost(bool, tag = "1")]
+ pub include_inference_probe: bool,
+ /// Optional. Used when include_inference_probe is true. Empty means engine
+ /// default served model.
+ #[prost(string, tag = "2")]
+ pub model: ::prost::alloc::string::String,
+ /// Optional expected role for role-specific inference probes.
+ #[prost(enumeration = "EngineRole", tag = "3")]
+ pub role: i32,
+}
+#[derive(Clone, PartialEq, ::prost::Message)]
+pub struct HealthResponse {
+ #[prost(enumeration = "HealthState", tag = "1")]
+ pub state: i32,
+ #[prost(message, repeated, tag = "2")]
+ pub checks: ::prost::alloc::vec::Vec,
+}
+#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
+pub struct HealthCheck {
+ /// grpc, scheduler, model, kv_connector, role, inference_probe
+ #[prost(string, tag = "1")]
+ pub name: ::prost::alloc::string::String,
+ #[prost(enumeration = "HealthState", tag = "2")]
+ pub state: i32,
+ #[prost(string, tag = "3")]
+ pub message: ::prost::alloc::string::String,
+}
+#[derive(Clone, PartialEq, ::prost::Message)]
+pub struct AbortRequest {
+ #[prost(oneof = "abort_request::Target", tags = "1, 2, 3")]
+ pub target: ::core::option::Option,
+}
+/// Nested message and enum types in `AbortRequest`.
+pub mod abort_request {
+ #[derive(Clone, PartialEq, ::prost::Oneof)]
+ pub enum Target {
+ #[prost(string, tag = "1")]
+ RequestId(::prost::alloc::string::String),
+ #[prost(message, tag = "2")]
+ KvSession(super::KvSessionRef),
+ #[prost(message, tag = "3")]
+ AllRequests(super::AllRequests),
+ }
+}
+#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)]
+pub struct AllRequests {}
+#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
+pub struct AbortResponse {
+ #[prost(enumeration = "AbortStatus", tag = "1")]
+ pub status: i32,
+ #[prost(string, tag = "2")]
+ pub message: ::prost::alloc::string::String,
+}
+#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)]
+#[repr(i32)]
+pub enum HealthState {
+ Unspecified = 0,
+ Starting = 1,
+ Ready = 2,
+ Degraded = 3,
+ NotReady = 4,
+}
+impl HealthState {
+ /// String value of the enum field names used in the ProtoBuf definition.
+ ///
+ /// The values are not transformed in any way and thus are considered stable
+ /// (if the ProtoBuf definition does not change) and safe for programmatic use.
+ pub fn as_str_name(&self) -> &'static str {
+ match self {
+ Self::Unspecified => "HEALTH_STATE_UNSPECIFIED",
+ Self::Starting => "HEALTH_STATE_STARTING",
+ Self::Ready => "HEALTH_STATE_READY",
+ Self::Degraded => "HEALTH_STATE_DEGRADED",
+ Self::NotReady => "HEALTH_STATE_NOT_READY",
+ }
+ }
+ /// Creates an enum from field names used in the ProtoBuf definition.
+ pub fn from_str_name(value: &str) -> ::core::option::Option {
+ match value {
+ "HEALTH_STATE_UNSPECIFIED" => Some(Self::Unspecified),
+ "HEALTH_STATE_STARTING" => Some(Self::Starting),
+ "HEALTH_STATE_READY" => Some(Self::Ready),
+ "HEALTH_STATE_DEGRADED" => Some(Self::Degraded),
+ "HEALTH_STATE_NOT_READY" => Some(Self::NotReady),
+ _ => None,
+ }
+ }
+}
+#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)]
+#[repr(i32)]
+pub enum AbortStatus {
+ Unspecified = 0,
+ Aborted = 1,
+ AlreadyFinished = 2,
+}
+impl AbortStatus {
+ /// String value of the enum field names used in the ProtoBuf definition.
+ ///
+ /// The values are not transformed in any way and thus are considered stable
+ /// (if the ProtoBuf definition does not change) and safe for programmatic use.
+ pub fn as_str_name(&self) -> &'static str {
+ match self {
+ Self::Unspecified => "ABORT_STATUS_UNSPECIFIED",
+ Self::Aborted => "ABORT_STATUS_ABORTED",
+ Self::AlreadyFinished => "ABORT_STATUS_ALREADY_FINISHED",
+ }
+ }
+ /// Creates an enum from field names used in the ProtoBuf definition.
+ pub fn from_str_name(value: &str) -> ::core::option::Option {
+ match value {
+ "ABORT_STATUS_UNSPECIFIED" => Some(Self::Unspecified),
+ "ABORT_STATUS_ABORTED" => Some(Self::Aborted),
+ "ABORT_STATUS_ALREADY_FINISHED" => Some(Self::AlreadyFinished),
+ _ => None,
+ }
+ }
+}
+#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
+pub struct LoraAdapter {
+ #[prost(int64, tag = "1")]
+ pub lora_id: i64,
+ #[prost(string, tag = "2")]
+ pub lora_name: ::prost::alloc::string::String,
+ #[prost(string, tag = "3")]
+ pub source_path: ::prost::alloc::string::String,
+}
+#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
+pub struct LoadLoraRequest {
+ #[prost(message, optional, tag = "1")]
+ pub adapter: ::core::option::Option,
+}
+#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
+pub struct LoadLoraResponse {
+ #[prost(message, optional, tag = "1")]
+ pub adapter: ::core::option::Option,
+ #[prost(bool, tag = "2")]
+ pub already_loaded: bool,
+}
+#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
+pub struct UnloadLoraRequest {
+ #[prost(string, tag = "1")]
+ pub lora_name: ::prost::alloc::string::String,
+}
+#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
+pub struct UnloadLoraResponse {
+ #[prost(message, optional, tag = "1")]
+ pub adapter: ::core::option::Option,
+}
+#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)]
+pub struct ListLorasRequest {}
+#[derive(Clone, PartialEq, ::prost::Message)]
+pub struct ListLorasResponse {
+ #[prost(message, repeated, tag = "1")]
+ pub adapters: ::prost::alloc::vec::Vec,
+}
+#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
+pub struct GetModelInfoRequest {
+ #[prost(string, tag = "1")]
+ pub model: ::prost::alloc::string::String,
+}
+#[derive(Clone, PartialEq, ::prost::Message)]
+pub struct ModelInfo {
+ #[prost(string, tag = "1")]
+ pub model_id: ::prost::alloc::string::String,
+ #[prost(string, tag = "2")]
+ pub served_model_name: ::prost::alloc::string::String,
+ #[prost(string, repeated, tag = "3")]
+ pub served_model_aliases: ::prost::alloc::vec::Vec<::prost::alloc::string::String>,
+ /// Effective context-window limit in this deployment.
+ #[prost(uint32, optional, tag = "4")]
+ pub max_context_length: ::core::option::Option,
+ /// Effective generated-token limit in this deployment.
+ #[prost(uint32, optional, tag = "5")]
+ pub max_output_tokens: ::core::option::Option,
+ #[prost(string, repeated, tag = "10")]
+ pub tokenizer_modes: ::prost::alloc::vec::Vec<::prost::alloc::string::String>,
+ #[prost(bool, optional, tag = "20")]
+ pub supports_text_input: ::core::option::Option,
+ #[prost(bool, optional, tag = "21")]
+ pub supports_token_ids_input: ::core::option::Option,
+ #[prost(message, optional, tag = "22")]
+ pub generation: ::core::option::Option,
+ #[prost(bool, optional, tag = "23")]
+ pub supports_lora: ::core::option::Option,
+ #[prost(bool, optional, tag = "24")]
+ pub supports_multimodal: ::core::option::Option,
+ /// Engine-advertised response parser names. Clients can apply these to a
+ /// model's output stream for tool-call extraction or reasoning separation.
+ /// Empty means no parser is configured for this model.
+ #[prost(string, tag = "25")]
+ pub reasoning_parser: ::prost::alloc::string::String,
+ #[prost(string, tag = "26")]
+ pub tool_call_parser: ::prost::alloc::string::String,
+ /// Engine-specific model metadata with no portable field (e.g. kv-cache dtype,
+ /// quantization, capabilities not yet standardized). NOT part of the portable
+ /// contract: clients read it opportunistically and never depend on it for
+ /// correctness. Carried as a Struct so JSON types survive the wire.
+ #[prost(message, optional, tag = "28")]
+ pub extra: ::core::option::Option<::prost_types::Struct>,
+}
+#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
+pub struct GenerationCapabilities {
+ #[prost(message, optional, tag = "1")]
+ pub prompt_logprobs: ::core::option::Option,
+ #[prost(message, optional, tag = "2")]
+ pub output_logprobs: ::core::option::Option,
+ #[prost(message, optional, tag = "3")]
+ pub guided_decoding: ::core::option::Option,
+ #[prost(uint32, optional, tag = "4")]
+ pub max_num_sequences: ::core::option::Option,
+ #[prost(bool, optional, tag = "5")]
+ pub supports_priority: ::core::option::Option,
+ #[prost(bool, optional, tag = "6")]
+ pub supports_stop_in_output: ::core::option::Option,
+ #[prost(bool, optional, tag = "7")]
+ pub supports_cache_salt: ::core::option::Option,
+ #[prost(bool, optional, tag = "8")]
+ pub supports_prefix_cache_bypass: ::core::option::Option,
+}
+#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
+pub struct LogprobCapabilities {
+ #[prost(bool, optional, tag = "1")]
+ pub supported: ::core::option::Option,
+ #[prost(enumeration = "CandidateTokenSelectionMode", repeated, tag = "2")]
+ pub candidate_selection_modes: ::prost::alloc::vec::Vec,
+ #[prost(uint32, optional, tag = "3")]
+ pub max_top_n: ::core::option::Option,
+}
+#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
+pub struct GuidedDecodingCapabilities {
+ #[prost(bool, optional, tag = "1")]
+ pub supported: ::core::option::Option,
+ #[prost(enumeration = "GuidedDecodingMode", repeated, tag = "2")]
+ pub modes: ::prost::alloc::vec::Vec,
+}
+#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)]
+#[repr(i32)]
+pub enum CandidateTokenSelectionMode {
+ Unspecified = 0,
+ TopN = 1,
+ TokenIds = 2,
+ All = 3,
+}
+impl CandidateTokenSelectionMode {
+ /// String value of the enum field names used in the ProtoBuf definition.
+ ///
+ /// The values are not transformed in any way and thus are considered stable
+ /// (if the ProtoBuf definition does not change) and safe for programmatic use.
+ pub fn as_str_name(&self) -> &'static str {
+ match self {
+ Self::Unspecified => "CANDIDATE_TOKEN_SELECTION_MODE_UNSPECIFIED",
+ Self::TopN => "CANDIDATE_TOKEN_SELECTION_MODE_TOP_N",
+ Self::TokenIds => "CANDIDATE_TOKEN_SELECTION_MODE_TOKEN_IDS",
+ Self::All => "CANDIDATE_TOKEN_SELECTION_MODE_ALL",
+ }
+ }
+ /// Creates an enum from field names used in the ProtoBuf definition.
+ pub fn from_str_name(value: &str) -> ::core::option::Option {
+ match value {
+ "CANDIDATE_TOKEN_SELECTION_MODE_UNSPECIFIED" => Some(Self::Unspecified),
+ "CANDIDATE_TOKEN_SELECTION_MODE_TOP_N" => Some(Self::TopN),
+ "CANDIDATE_TOKEN_SELECTION_MODE_TOKEN_IDS" => Some(Self::TokenIds),
+ "CANDIDATE_TOKEN_SELECTION_MODE_ALL" => Some(Self::All),
+ _ => None,
+ }
+ }
+}
+#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)]
+#[repr(i32)]
+pub enum GuidedDecodingMode {
+ Unspecified = 0,
+ JsonSchema = 1,
+ Regex = 2,
+ EbnfGrammar = 3,
+ StructuralTag = 4,
+ Choice = 5,
+ JsonObject = 6,
+}
+impl GuidedDecodingMode {
+ /// String value of the enum field names used in the ProtoBuf definition.
+ ///
+ /// The values are not transformed in any way and thus are considered stable
+ /// (if the ProtoBuf definition does not change) and safe for programmatic use.
+ pub fn as_str_name(&self) -> &'static str {
+ match self {
+ Self::Unspecified => "GUIDED_DECODING_MODE_UNSPECIFIED",
+ Self::JsonSchema => "GUIDED_DECODING_MODE_JSON_SCHEMA",
+ Self::Regex => "GUIDED_DECODING_MODE_REGEX",
+ Self::EbnfGrammar => "GUIDED_DECODING_MODE_EBNF_GRAMMAR",
+ Self::StructuralTag => "GUIDED_DECODING_MODE_STRUCTURAL_TAG",
+ Self::Choice => "GUIDED_DECODING_MODE_CHOICE",
+ Self::JsonObject => "GUIDED_DECODING_MODE_JSON_OBJECT",
+ }
+ }
+ /// Creates an enum from field names used in the ProtoBuf definition.
+ pub fn from_str_name(value: &str) -> ::core::option::Option {
+ match value {
+ "GUIDED_DECODING_MODE_UNSPECIFIED" => Some(Self::Unspecified),
+ "GUIDED_DECODING_MODE_JSON_SCHEMA" => Some(Self::JsonSchema),
+ "GUIDED_DECODING_MODE_REGEX" => Some(Self::Regex),
+ "GUIDED_DECODING_MODE_EBNF_GRAMMAR" => Some(Self::EbnfGrammar),
+ "GUIDED_DECODING_MODE_STRUCTURAL_TAG" => Some(Self::StructuralTag),
+ "GUIDED_DECODING_MODE_CHOICE" => Some(Self::Choice),
+ "GUIDED_DECODING_MODE_JSON_OBJECT" => Some(Self::JsonObject),
+ _ => None,
+ }
+ }
+}
+/// Generated client implementations.
+pub mod inference_client {
+ #![allow(
+ unused_variables,
+ dead_code,
+ missing_docs,
+ clippy::wildcard_imports,
+ clippy::let_unit_value,
+ )]
+ use tonic::codegen::*;
+ use tonic::codegen::http::Uri;
+ #[derive(Debug, Clone)]
+ pub struct InferenceClient {
+ inner: tonic::client::Grpc,
+ }
+ impl InferenceClient {
+ /// Attempt to create a new client by connecting to a given endpoint.
+ pub async fn connect(dst: D) -> Result
+ where
+ D: TryInto,
+ D::Error: Into,
+ {
+ let conn = tonic::transport::Endpoint::new(dst)?.connect().await?;
+ Ok(Self::new(conn))
+ }
+ }
+ impl InferenceClient
+ where
+ T: tonic::client::GrpcService,
+ T::Error: Into,
+ T::ResponseBody: Body + std::marker::Send + 'static,
+ ::Error: Into + std::marker::Send,
+ {
+ pub fn new(inner: T) -> Self {
+ let inner = tonic::client::Grpc::new(inner);
+ Self { inner }
+ }
+ pub fn with_origin(inner: T, origin: Uri) -> Self {
+ let inner = tonic::client::Grpc::with_origin(inner, origin);
+ Self { inner }
+ }
+ pub fn with_interceptor(
+ inner: T,
+ interceptor: F,
+ ) -> InferenceClient>
+ where
+ F: tonic::service::Interceptor,
+ T::ResponseBody: Default,
+ T: tonic::codegen::Service<
+ http::Request,
+ Response = http::Response<
+ >::ResponseBody,
+ >,
+ >,
+ ,
+ >>::Error: Into + std::marker::Send + std::marker::Sync,
+ {
+ InferenceClient::new(InterceptedService::new(inner, interceptor))
+ }
+ /// Compress requests with the given encoding.
+ ///
+ /// This requires the server to support it otherwise it might respond with an
+ /// error.
+ #[must_use]
+ pub fn send_compressed(mut self, encoding: CompressionEncoding) -> Self {
+ self.inner = self.inner.send_compressed(encoding);
+ self
+ }
+ /// Enable decompressing responses.
+ #[must_use]
+ pub fn accept_compressed(mut self, encoding: CompressionEncoding) -> Self {
+ self.inner = self.inner.accept_compressed(encoding);
+ self
+ }
+ /// Limits the maximum size of a decoded message.
+ ///
+ /// Default: `4MB`
+ #[must_use]
+ pub fn max_decoding_message_size(mut self, limit: usize) -> Self {
+ self.inner = self.inner.max_decoding_message_size(limit);
+ self
+ }
+ /// Limits the maximum size of an encoded message.
+ ///
+ /// Default: `usize::MAX`
+ #[must_use]
+ pub fn max_encoding_message_size(mut self, limit: usize) -> Self {
+ self.inner = self.inner.max_encoding_message_size(limit);
+ self
+ }
+ /// Core inference path.
+ pub async fn generate(
+ &mut self,
+ request: impl tonic::IntoRequest,
+ ) -> std::result::Result<
+ tonic::Response>,
+ tonic::Status,
+ > {
+ self.inner
+ .ready()
+ .await
+ .map_err(|e| {
+ tonic::Status::unknown(
+ format!("Service was not ready: {}", e.into()),
+ )
+ })?;
+ let codec = tonic_prost::ProstCodec::default();
+ let path = http::uri::PathAndQuery::from_static(
+ "/openengine.v1.Inference/Generate",
+ );
+ let mut req = request.into_request();
+ req.extensions_mut()
+ .insert(GrpcMethod::new("openengine.v1.Inference", "Generate"));
+ self.inner.server_streaming(req, path, codec).await
+ }
+ }
+}
+/// Generated server implementations.
+pub mod inference_server {
+ #![allow(
+ unused_variables,
+ dead_code,
+ missing_docs,
+ clippy::wildcard_imports,
+ clippy::let_unit_value,
+ )]
+ use tonic::codegen::*;
+ /// Generated trait containing gRPC methods that should be implemented for use with InferenceServer.
+ #[async_trait]
+ pub trait Inference: std::marker::Send + std::marker::Sync + 'static {
+ /// Server streaming response type for the Generate method.
+ type GenerateStream: tonic::codegen::tokio_stream::Stream<
+ Item = std::result::Result,
+ >
+ + std::marker::Send
+ + 'static;
+ /// Core inference path.
+ async fn generate(
+ &self,
+ request: tonic::Request,
+ ) -> std::result::Result, tonic::Status>;
+ }
+ #[derive(Debug)]
+ pub struct InferenceServer {
+ inner: Arc,
+ accept_compression_encodings: EnabledCompressionEncodings,
+ send_compression_encodings: EnabledCompressionEncodings,
+ max_decoding_message_size: Option,
+ max_encoding_message_size: Option,
+ }
+ impl InferenceServer {
+ pub fn new(inner: T) -> Self {
+ Self::from_arc(Arc::new(inner))
+ }
+ pub fn from_arc(inner: Arc) -> Self {
+ Self {
+ inner,
+ accept_compression_encodings: Default::default(),
+ send_compression_encodings: Default::default(),
+ max_decoding_message_size: None,
+ max_encoding_message_size: None,
+ }
+ }
+ pub fn with_interceptor(
+ inner: T,
+ interceptor: F,
+ ) -> InterceptedService
+ where
+ F: tonic::service::Interceptor,
+ {
+ InterceptedService::new(Self::new(inner), interceptor)
+ }
+ /// Enable decompressing requests with the given encoding.
+ #[must_use]
+ pub fn accept_compressed(mut self, encoding: CompressionEncoding) -> Self {
+ self.accept_compression_encodings.enable(encoding);
+ self
+ }
+ /// Compress responses with the given encoding, if the client supports it.
+ #[must_use]
+ pub fn send_compressed(mut self, encoding: CompressionEncoding) -> Self {
+ self.send_compression_encodings.enable(encoding);
+ self
+ }
+ /// Limits the maximum size of a decoded message.
+ ///
+ /// Default: `4MB`
+ #[must_use]
+ pub fn max_decoding_message_size(mut self, limit: usize) -> Self {
+ self.max_decoding_message_size = Some(limit);
+ self
+ }
+ /// Limits the maximum size of an encoded message.
+ ///
+ /// Default: `usize::MAX`
+ #[must_use]
+ pub fn max_encoding_message_size(mut self, limit: usize) -> Self {
+ self.max_encoding_message_size = Some(limit);
+ self
+ }
+ }
+ impl tonic::codegen::Service> for InferenceServer
+ where
+ T: Inference,
+ B: Body + std::marker::Send + 'static,
+ B::Error: Into + std::marker::Send + 'static,
+ {
+ type Response = http::Response;
+ type Error = std::convert::Infallible;
+ type Future = BoxFuture;
+ fn poll_ready(
+ &mut self,
+ _cx: &mut Context<'_>,
+ ) -> Poll> {
+ Poll::Ready(Ok(()))
+ }
+ fn call(&mut self, req: http::Request) -> Self::Future {
+ match req.uri().path() {
+ "/openengine.v1.Inference/Generate" => {
+ #[allow(non_camel_case_types)]
+ struct GenerateSvc(pub Arc);
+ impl<
+ T: Inference,
+ > tonic::server::ServerStreamingService
+ for GenerateSvc {
+ type Response = super::GenerateResponse;
+ type ResponseStream = T::GenerateStream;
+ type Future = BoxFuture<
+ tonic::Response,
+ tonic::Status,
+ >;
+ fn call(
+ &mut self,
+ request: tonic::Request,
+ ) -> Self::Future {
+ let inner = Arc::clone(&self.0);
+ let fut = async move {
+ ::generate(&inner, request).await
+ };
+ Box::pin(fut)
+ }
+ }
+ let accept_compression_encodings = self.accept_compression_encodings;
+ let send_compression_encodings = self.send_compression_encodings;
+ let max_decoding_message_size = self.max_decoding_message_size;
+ let max_encoding_message_size = self.max_encoding_message_size;
+ let inner = self.inner.clone();
+ let fut = async move {
+ let method = GenerateSvc(inner);
+ let codec = tonic_prost::ProstCodec::default();
+ let mut grpc = tonic::server::Grpc::new(codec)
+ .apply_compression_config(
+ accept_compression_encodings,
+ send_compression_encodings,
+ )
+ .apply_max_message_size_config(
+ max_decoding_message_size,
+ max_encoding_message_size,
+ );
+ let res = grpc.server_streaming(method, req).await;
+ Ok(res)
+ };
+ Box::pin(fut)
+ }
+ _ => {
+ Box::pin(async move {
+ let mut response = http::Response::new(
+ tonic::body::Body::default(),
+ );
+ let headers = response.headers_mut();
+ headers
+ .insert(
+ tonic::Status::GRPC_STATUS,
+ (tonic::Code::Unimplemented as i32).into(),
+ );
+ headers
+ .insert(
+ http::header::CONTENT_TYPE,
+ tonic::metadata::GRPC_CONTENT_TYPE,
+ );
+ Ok(response)
+ })
+ }
+ }
+ }
+ }
+ impl Clone for InferenceServer {
+ fn clone(&self) -> Self {
+ let inner = self.inner.clone();
+ Self {
+ inner,
+ accept_compression_encodings: self.accept_compression_encodings,
+ send_compression_encodings: self.send_compression_encodings,
+ max_decoding_message_size: self.max_decoding_message_size,
+ max_encoding_message_size: self.max_encoding_message_size,
+ }
+ }
+ }
+ /// Generated gRPC service name
+ pub const SERVICE_NAME: &str = "openengine.v1.Inference";
+ impl tonic::server::NamedService for InferenceServer {
+ const NAME: &'static str = SERVICE_NAME;
+ }
+}
+/// Generated client implementations.
+pub mod control_client {
+ #![allow(
+ unused_variables,
+ dead_code,
+ missing_docs,
+ clippy::wildcard_imports,
+ clippy::let_unit_value,
+ )]
+ use tonic::codegen::*;
+ use tonic::codegen::http::Uri;
+ #[derive(Debug, Clone)]
+ pub struct ControlClient {
+ inner: tonic::client::Grpc,
+ }
+ impl ControlClient {
+ /// Attempt to create a new client by connecting to a given endpoint.
+ pub async fn connect(dst: D) -> Result
+ where
+ D: TryInto,
+ D::Error: Into,
+ {
+ let conn = tonic::transport::Endpoint::new(dst)?.connect().await?;
+ Ok(Self::new(conn))
+ }
+ }
+ impl ControlClient
+ where
+ T: tonic::client::GrpcService,
+ T::Error: Into,
+ T::ResponseBody: Body + std::marker::Send + 'static,
+ ::Error: Into + std::marker::Send,
+ {
+ pub fn new(inner: T) -> Self {
+ let inner = tonic::client::Grpc::new(inner);
+ Self { inner }
+ }
+ pub fn with_origin(inner: T, origin: Uri) -> Self {
+ let inner = tonic::client::Grpc::with_origin(inner, origin);
+ Self { inner }
+ }
+ pub fn with_interceptor(
+ inner: T,
+ interceptor: F,
+ ) -> ControlClient>
+ where
+ F: tonic::service::Interceptor,
+ T::ResponseBody: Default,
+ T: tonic::codegen::Service<
+ http::Request,
+ Response = http::Response<
+ >::ResponseBody,
+ >,
+ >,
+ ,
+ >>::Error: Into + std::marker::Send + std::marker::Sync,
+ {
+ ControlClient::new(InterceptedService::new(inner, interceptor))
+ }
+ /// Compress requests with the given encoding.
+ ///
+ /// This requires the server to support it otherwise it might respond with an
+ /// error.
+ #[must_use]
+ pub fn send_compressed(mut self, encoding: CompressionEncoding) -> Self {
+ self.inner = self.inner.send_compressed(encoding);
+ self
+ }
+ /// Enable decompressing responses.
+ #[must_use]
+ pub fn accept_compressed(mut self, encoding: CompressionEncoding) -> Self {
+ self.inner = self.inner.accept_compressed(encoding);
+ self
+ }
+ /// Limits the maximum size of a decoded message.
+ ///
+ /// Default: `4MB`
+ #[must_use]
+ pub fn max_decoding_message_size(mut self, limit: usize) -> Self {
+ self.inner = self.inner.max_decoding_message_size(limit);
+ self
+ }
+ /// Limits the maximum size of an encoded message.
+ ///
+ /// Default: `usize::MAX`
+ #[must_use]
+ pub fn max_encoding_message_size(mut self, limit: usize) -> Self {
+ self.inner = self.inner.max_encoding_message_size(limit);
+ self
+ }
+ /// Runtime metadata and scheduling state.
+ pub async fn get_server_info(
+ &mut self,
+ request: impl tonic::IntoRequest,
+ ) -> std::result::Result, tonic::Status> {
+ self.inner
+ .ready()
+ .await
+ .map_err(|e| {
+ tonic::Status::unknown(
+ format!("Service was not ready: {}", e.into()),
+ )
+ })?;
+ let codec = tonic_prost::ProstCodec::default();
+ let path = http::uri::PathAndQuery::from_static(
+ "/openengine.v1.Control/GetServerInfo",
+ );
+ let mut req = request.into_request();
+ req.extensions_mut()
+ .insert(GrpcMethod::new("openengine.v1.Control", "GetServerInfo"));
+ self.inner.unary(req, path, codec).await
+ }
+ pub async fn get_model_info(
+ &mut self,
+ request: impl tonic::IntoRequest,
+ ) -> std::result::Result, tonic::Status> {
+ self.inner
+ .ready()
+ .await
+ .map_err(|e| {
+ tonic::Status::unknown(
+ format!("Service was not ready: {}", e.into()),
+ )
+ })?;
+ let codec = tonic_prost::ProstCodec::default();
+ let path = http::uri::PathAndQuery::from_static(
+ "/openengine.v1.Control/GetModelInfo",
+ );
+ let mut req = request.into_request();
+ req.extensions_mut()
+ .insert(GrpcMethod::new("openengine.v1.Control", "GetModelInfo"));
+ self.inner.unary(req, path, codec).await
+ }
+ pub async fn get_load(
+ &mut self,
+ request: impl tonic::IntoRequest,
+ ) -> std::result::Result, tonic::Status> {
+ self.inner
+ .ready()
+ .await
+ .map_err(|e| {
+ tonic::Status::unknown(
+ format!("Service was not ready: {}", e.into()),
+ )
+ })?;
+ let codec = tonic_prost::ProstCodec::default();
+ let path = http::uri::PathAndQuery::from_static(
+ "/openengine.v1.Control/GetLoad",
+ );
+ let mut req = request.into_request();
+ req.extensions_mut()
+ .insert(GrpcMethod::new("openengine.v1.Control", "GetLoad"));
+ self.inner.unary(req, path, codec).await
+ }
+ /// Health and lifecycle.
+ pub async fn health(
+ &mut self,
+ request: impl tonic::IntoRequest,
+ ) -> std::result::Result, tonic::Status> {
+ self.inner
+ .ready()
+ .await
+ .map_err(|e| {
+ tonic::Status::unknown(
+ format!("Service was not ready: {}", e.into()),
+ )
+ })?;
+ let codec = tonic_prost::ProstCodec::default();
+ let path = http::uri::PathAndQuery::from_static(
+ "/openengine.v1.Control/Health",
+ );
+ let mut req = request.into_request();
+ req.extensions_mut()
+ .insert(GrpcMethod::new("openengine.v1.Control", "Health"));
+ self.inner.unary(req, path, codec).await
+ }
+ pub async fn abort(
+ &mut self,
+ request: impl tonic::IntoRequest,
+ ) -> std::result::Result, tonic::Status> {
+ self.inner
+ .ready()
+ .await
+ .map_err(|e| {
+ tonic::Status::unknown(
+ format!("Service was not ready: {}", e.into()),
+ )
+ })?;
+ let codec = tonic_prost::ProstCodec::default();
+ let path = http::uri::PathAndQuery::from_static(
+ "/openengine.v1.Control/Abort",
+ );
+ let mut req = request.into_request();
+ req.extensions_mut()
+ .insert(GrpcMethod::new("openengine.v1.Control", "Abort"));
+ self.inner.unary(req, path, codec).await
+ }
+ /// LoRA lifecycle.
+ pub async fn load_lora(
+ &mut self,
+ request: impl tonic::IntoRequest,
+ ) -> std::result::Result<
+ tonic::Response,
+ tonic::Status,
+ > {
+ self.inner
+ .ready()
+ .await
+ .map_err(|e| {
+ tonic::Status::unknown(
+ format!("Service was not ready: {}", e.into()),
+ )
+ })?;
+ let codec = tonic_prost::ProstCodec::default();
+ let path = http::uri::PathAndQuery::from_static(
+ "/openengine.v1.Control/LoadLora",
+ );
+ let mut req = request.into_request();
+ req.extensions_mut()
+ .insert(GrpcMethod::new("openengine.v1.Control", "LoadLora"));
+ self.inner.unary(req, path, codec).await
+ }
+ pub async fn unload_lora(
+ &mut self,
+ request: impl tonic::IntoRequest,
+ ) -> std::result::Result<
+ tonic::Response,
+ tonic::Status,
+ > {
+ self.inner
+ .ready()
+ .await
+ .map_err(|e| {
+ tonic::Status::unknown(
+ format!("Service was not ready: {}", e.into()),
+ )
+ })?;
+ let codec = tonic_prost::ProstCodec::default();
+ let path = http::uri::PathAndQuery::from_static(
+ "/openengine.v1.Control/UnloadLora",
+ );
+ let mut req = request.into_request();
+ req.extensions_mut()
+ .insert(GrpcMethod::new("openengine.v1.Control", "UnloadLora"));
+ self.inner.unary(req, path, codec).await
+ }
+ pub async fn list_loras(
+ &mut self,
+ request: impl tonic::IntoRequest,
+ ) -> std::result::Result<
+ tonic::Response,
+ tonic::Status,
+ > {
+ self.inner
+ .ready()
+ .await
+ .map_err(|e| {
+ tonic::Status::unknown(
+ format!("Service was not ready: {}", e.into()),
+ )
+ })?;
+ let codec = tonic_prost::ProstCodec::default();
+ let path = http::uri::PathAndQuery::from_static(
+ "/openengine.v1.Control/ListLoras",
+ );
+ let mut req = request.into_request();
+ req.extensions_mut()
+ .insert(GrpcMethod::new("openengine.v1.Control", "ListLoras"));
+ self.inner.unary(req, path, codec).await
+ }
+ /// Disaggregated serving / KV transfer. Connector info: ServerInfo.kv_connector.
+ pub async fn get_kv_event_sources(
+ &mut self,
+ request: impl tonic::IntoRequest,
+ ) -> std::result::Result<
+ tonic::Response,
+ tonic::Status,
+ > {
+ self.inner
+ .ready()
+ .await
+ .map_err(|e| {
+ tonic::Status::unknown(
+ format!("Service was not ready: {}", e.into()),
+ )
+ })?;
+ let codec = tonic_prost::ProstCodec::default();
+ let path = http::uri::PathAndQuery::from_static(
+ "/openengine.v1.Control/GetKvEventSources",
+ );
+ let mut req = request.into_request();
+ req.extensions_mut()
+ .insert(GrpcMethod::new("openengine.v1.Control", "GetKvEventSources"));
+ self.inner.unary(req, path, codec).await
+ }
+ pub async fn subscribe_kv_events(
+ &mut self,
+ request: impl tonic::IntoRequest,
+ ) -> std::result::Result<
+ tonic::Response>,
+ tonic::Status,
+ > {
+ self.inner
+ .ready()
+ .await
+ .map_err(|e| {
+ tonic::Status::unknown(
+ format!("Service was not ready: {}", e.into()),
+ )
+ })?;
+ let codec = tonic_prost::ProstCodec::default();
+ let path = http::uri::PathAndQuery::from_static(
+ "/openengine.v1.Control/SubscribeKvEvents",
+ );
+ let mut req = request.into_request();
+ req.extensions_mut()
+ .insert(GrpcMethod::new("openengine.v1.Control", "SubscribeKvEvents"));
+ self.inner.server_streaming(req, path, codec).await
+ }
+ }
+}
+/// Generated server implementations.
+pub mod control_server {
+ #![allow(
+ unused_variables,
+ dead_code,
+ missing_docs,
+ clippy::wildcard_imports,
+ clippy::let_unit_value,
+ )]
+ use tonic::codegen::*;
+ /// Generated trait containing gRPC methods that should be implemented for use with ControlServer.
+ #[async_trait]
+ pub trait Control: std::marker::Send + std::marker::Sync + 'static {
+ /// Runtime metadata and scheduling state.
+ async fn get_server_info(
+ &self,
+ request: tonic::Request,
+ ) -> std::result::Result, tonic::Status>;
+ async fn get_model_info(
+ &self,
+ request: tonic::Request,
+ ) -> std::result::Result, tonic::Status>;
+ async fn get_load(
+ &self,
+ request: tonic::Request,
+ ) -> std::result::Result, tonic::Status>;
+ /// Health and lifecycle.
+ async fn health(
+ &self,
+ request: tonic::Request,
+ ) -> std::result::Result, tonic::Status>;
+ async fn abort(
+ &self,
+ request: tonic::Request,
+ ) -> std::result::Result, tonic::Status>;
+ /// LoRA lifecycle.
+ async fn load_lora(
+ &self,
+ request: tonic::Request,
+ ) -> std::result::Result<
+ tonic::Response,
+ tonic::Status,
+ >;
+ async fn unload_lora(
+ &self,
+ request: tonic::Request,
+ ) -> std::result::Result<
+ tonic::Response,
+ tonic::Status,
+ >;
+ async fn list_loras(
+ &self,
+ request: tonic::Request,
+ ) -> std::result::Result<
+ tonic::Response,
+ tonic::Status,
+ >;
+ /// Disaggregated serving / KV transfer. Connector info: ServerInfo.kv_connector.
+ async fn get_kv_event_sources(
+ &self,
+ request: tonic::Request,
+ ) -> std::result::Result<
+ tonic::Response