Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/pull_request_template.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@

<!-- Delete this section when the wire contract is unchanged. -->

- [ ] The canonical proto and API documentation are synchronized
- [ ] The canonical schema and API documentation are synchronized
- [ ] Presence, defaults, and invalid states are defined
- [ ] Acceptance, streaming, terminal, and error behavior are defined
- [ ] Discovery and capability impacts are documented
Expand Down
11 changes: 6 additions & 5 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,12 @@ limitations under the License.
Thank you for your interest in OpenEngine. We welcome bug reports, feedback on
the API design, and pull requests.

OpenEngine is a pre-adoption API draft. `proto/openengine/v1/openengine.proto` is
the canonical wire contract and the single source of truth. Until an external
consumer adopts it, the schema may remove or renumber fields to stay minimal.
After external adoption, changes within `openengine.v1` will be additive. Please
open an issue to discuss protocol changes before sending a PR.
OpenEngine is a pre-adoption API draft. The files under
`proto/openengine/v1/` are the canonical wire contract and the single source of
truth. Until an external consumer adopts it, the schema may remove or renumber
fields to stay minimal. After external adoption, changes within `openengine.v1`
will be additive. Please open an issue to discuss protocol changes before
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).
Expand Down
17 changes: 9 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,15 @@ SPDX-License-Identifier: Apache-2.0
<a href="https://github.com/ai-dynamo/openengine/actions/workflows/buf.yml"><img alt="Buf CI" src="https://github.com/ai-dynamo/openengine/actions/workflows/buf.yml/badge.svg?branch=main"></a>
<a href="LICENSE"><img alt="Apache 2.0 License" src="https://img.shields.io/github/license/ai-dynamo/openengine?color=blue"></a>
<a href="#project-status"><img alt="Status: Experimental" src="https://img.shields.io/badge/status-experimental-f59e0b"></a>
<a href="proto/openengine/v1/openengine.proto"><img alt="API: openengine.v1" src="https://img.shields.io/badge/API-openengine.v1-6f42c1"></a>
<a href="proto/openengine/v1/"><img alt="API: openengine.v1" src="https://img.shields.io/badge/API-openengine.v1-6f42c1"></a>
<a href="https://grpc.io/"><img alt="Transport: gRPC" src="https://img.shields.io/badge/transport-gRPC-244c5a"></a>
<a href="https://protobuf.dev/"><img alt="Schema: Protocol Buffers" src="https://img.shields.io/badge/schema-Protobuf-4285F4"></a>
</p>

<p align="center">
<a href="docs/motivation.md">Why OpenEngine?</a>
· <a href="docs/api.md">API reference</a>
· <a href="proto/openengine/v1/openengine.proto">Canonical proto</a>
· <a href="proto/openengine/v1/">Canonical schema</a>
· <a href="CONTRIBUTING.md">Contributing</a>
</p>

Expand Down Expand Up @@ -98,8 +98,9 @@ exist alongside OpenEngine.

## Capabilities

The complete service is defined in
[`proto/openengine/v1/openengine.proto`](proto/openengine/v1/openengine.proto).
The canonical schema is organized by domain under
[`proto/openengine/v1/`](proto/openengine/v1/), with the service definition in
[`openengine.proto`](proto/openengine/v1/openengine.proto).

| Area | What the contract provides |
| --------------------- | ----------------------------------------------------------------------------------------------------------------- |
Expand Down Expand Up @@ -153,11 +154,11 @@ python -m grpc_tools.protoc \
-I "$PROTO_INCLUDE" \
--python_out="$OUT_DIR" \
--grpc_python_out="$OUT_DIR" \
proto/openengine/v1/openengine.proto
proto/openengine/v1/*.proto
```

Other protobuf-supported languages can generate clients and servers from the
same canonical file.
same canonical package.

## Project status

Expand Down Expand Up @@ -187,8 +188,8 @@ git commit --signoff -m "docs: describe the change"
```

Please validate protobuf changes with Buf and keep
[`proto/openengine/v1/openengine.proto`](proto/openengine/v1/openengine.proto) and
[`docs/api.md`](docs/api.md) synchronized.
[`proto/openengine/v1/`](proto/openengine/v1/) and [`docs/api.md`](docs/api.md)
synchronized.

## Security

Expand Down
6 changes: 3 additions & 3 deletions docs/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ SPDX-License-Identifier: Apache-2.0

# OpenEngine API v1

This is the human-readable reference for
[`openengine.v1`](../proto/openengine/v1/openengine.proto).
The proto is the source of truth.
This is the human-readable reference for the
[`openengine.v1` schema](../proto/openengine/v1/). The proto package is the
source of truth.

---

Expand Down
2 changes: 1 addition & 1 deletion docs/motivation.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ request and control data needed across the process boundary.
| KV routing | Event streams and native event-source discovery |

The [API reference](api.md) defines the fields. The
[proto](../proto/openengine/v1/openengine.proto) is the source of truth.
[`openengine.v1` schema](../proto/openengine/v1/) is the source of truth.

## What stays engine-specific

Expand Down
25 changes: 25 additions & 0 deletions proto/openengine/v1/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<!--
SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
SPDX-License-Identifier: Apache-2.0
-->

# OpenEngine v1 schema

This directory is the canonical `openengine.v1` wire contract. All proto files
share the same package and together define the API.

| File | Area |
| --- | --- |
| [`openengine.proto`](openengine.proto) | `OpenEngine` service and RPC declarations |
| [`engine.proto`](engine.proto) | Engine identity, roles, and parallelism |
| [`model.proto`](model.proto) | Model metadata and generation capabilities |
| [`generation.proto`](generation.proto) | Generation requests, streamed events, and usage |
| [`generation_params.proto`](generation_params.proto) | Sampling, stopping, response, KV, and guided-decoding parameters |
| [`lora.proto`](lora.proto) | LoRA adapter lifecycle |
| [`kv.proto`](kv.proto) | KV sessions, connector discovery, and cache events |
| [`lifecycle.proto`](lifecycle.proto) | Health, abort, and drain operations |
| [`observability.proto`](observability.proto) | Load snapshots and runtime events |
| [`error.proto`](error.proto) | Terminal errors for accepted streaming requests |

Generate bindings from every `.proto` file in this directory. Compiling only
`openengine.proto` does not generate bindings for its imported message files.
40 changes: 40 additions & 0 deletions proto/openengine/v1/engine.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

// Engine identity, role, and parallelism metadata.

syntax = "proto3";

package openengine.v1;

import "openengine/v1/kv.proto";

enum EngineRole {
ENGINE_ROLE_UNSPECIFIED = 0;
ENGINE_ROLE_AGGREGATED = 1;
ENGINE_ROLE_PREFILL = 2;
ENGINE_ROLE_DECODE = 3;
}

message GetEngineInfoRequest {}

message EngineInfo {
string engine_name = 1; // sglang, vllm, tensorrt_llm, etc.
string engine_version = 2;
EngineRole role = 3;
string instance_id = 4;
repeated string supported_models = 5;
ParallelismInfo parallelism = 6;
KvConnectorInfo kv_connector = 7;
uint32 schema_revision = 8; // Monotonic wire contract revision; zero is invalid.
uint32 minimum_client_revision = 9; // Oldest compatible client revision.
string schema_release = 10; // Immutable release or source tag for this schema.
}

message ParallelismInfo {
optional uint32 tensor_parallel_size = 1;
optional uint32 pipeline_parallel_size = 2;
optional uint32 data_parallel_size = 3;
optional uint32 data_parallel_rank = 4;
optional uint32 data_parallel_start_rank = 5;
}
36 changes: 36 additions & 0 deletions proto/openengine/v1/error.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

// Terminal errors for accepted streaming requests.

syntax = "proto3";

package openengine.v1;

import "google/protobuf/struct.proto";

// Accepted failures emit one terminal EngineError and close with OK.
// Validation and transport failures use non-OK gRPC status instead.
message EngineError {
ErrorCode code = 1;
string message = 2;
bool retryable = 3; // Retry may succeed without changing the request.
optional uint64 retry_after_ms = 4; // Zero permits immediate retry.
google.protobuf.Struct details = 5; // Machine-readable context.
}

enum ErrorCode {
ERROR_CODE_UNSPECIFIED = 0;
ERROR_CODE_INVALID_ARGUMENT = 1;
ERROR_CODE_UNSUPPORTED_FEATURE = 2;
ERROR_CODE_ROLE_MISMATCH = 3;
ERROR_CODE_MODEL_NOT_FOUND = 4;
ERROR_CODE_OVERLOADED = 5;
ERROR_CODE_REQUEST_NOT_FOUND = 6;
ERROR_CODE_DUPLICATE_REQUEST = 7;
ERROR_CODE_KV_SESSION_NOT_FOUND = 8;
ERROR_CODE_KV_TRANSFER_FAILED = 9;
ERROR_CODE_CANCELLED = 10;
ERROR_CODE_DRAINING = 11;
ERROR_CODE_INTERNAL = 12;
}
145 changes: 145 additions & 0 deletions proto/openengine/v1/generation.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

// Generation requests, streamed events, and usage.

syntax = "proto3";

package openengine.v1;

import "openengine/v1/error.proto";
import "openengine/v1/generation_params.proto";
import "openengine/v1/kv.proto";

message GenerateRequest {
string request_id = 1;
string model = 2;

oneof input {
string prompt = 3;
TokenIds token_ids = 4;
}

SamplingParams sampling = 5;
StoppingOptions stopping = 6;
ResponseOptions response = 7;
KvOptions kv = 8;
GuidedDecoding guided = 9;

// 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.
repeated MediaItem media = 10;

// Loaded LoRA adapter name to apply to this request. Empty = base model.
// ModelInfo.supports_lora advertises whether lifecycle and selection are
// available through OpenEngine.
string lora_name = 11;

// Higher values receive higher scheduling priority.
optional int32 priority = 12;

// Optional request metadata for tracing/admission/routing.
map<string, string> metadata = 13;
}

// Multimodal modality discriminator. 0 is treated as image for forward
// compatibility with senders that omit the field.
enum Modality {
MODALITY_UNSPECIFIED = 0;
MODALITY_IMAGE = 1;
MODALITY_VIDEO = 2;
MODALITY_AUDIO = 3;
}

// 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.
message MediaItem {
Modality modality = 1;
oneof source {
string url = 2; // http(s):// -- engine fetches
string data_uri = 3; // data:<mime>;base64,<...> -- engine decodes
bytes raw_bytes = 4; // pre-fetched bytes -- engine still preprocesses
}
string mime_type = 5; // optional, hints raw_bytes decode
string uuid = 6; // optional caller id / mm_hash
}

message GenerateResponse {
string request_id = 1;

// PromptOutput, PrefillReady, and EngineError are request-scoped.
// TokenOutput and GenerationFinished are output-scoped.
oneof event {
PromptOutput prompt = 2;
TokenOutput token = 3;
PrefillReady prefill_ready = 4;
GenerationFinished finished = 5;
EngineError error = 6;
}

Usage usage = 10; // Cumulative request usage; only set on the final response.
}

// Request-scoped prompt token information, emitted at most once.
message PromptOutput {
repeated TokenInfo tokens = 1;
}

// An incremental output delta. Tokens and text are never cumulative.
message TokenOutput {
optional uint32 output_index = 1; // Required, including for output zero.
repeated TokenInfo tokens = 2;
string text = 3;
}

message TokenInfo {
uint32 token_id = 1;
string token = 2;
optional double logprob = 3;
optional uint32 rank = 4;
repeated LogProb candidates = 5;
}

message LogProb {
uint32 token_id = 1;
double logprob = 2;
string token = 3;
optional uint32 rank = 4;
}

message PrefillReady {
KvSessionRef kv_session = 1;
}

message GenerationFinished {
optional uint32 output_index = 1; // Required, including for output zero.
FinishReason reason = 2;
string message = 3;
StopMatch stop_match = 4;
}

message StopMatch {
oneof match {
uint32 stop_token_id = 1;
string stop_text = 2;
uint32 eos_token_id = 3;
}
}

enum FinishReason {
FINISH_REASON_UNSPECIFIED = 0;
FINISH_REASON_STOP = 1;
FINISH_REASON_LENGTH = 2;
FINISH_REASON_CANCELLED = 3;
}

message Usage {
uint32 prompt_tokens = 1;
uint32 completion_tokens = 2;
uint32 total_tokens = 3;
optional uint32 cached_prompt_tokens = 4;
optional uint32 reasoning_tokens = 5;
}
Loading
Loading