feat: implement DGR-006 tensor bundle boundary
This commit is contained in:
@@ -32,6 +32,15 @@ Both `--check` modes run in CI via `tests/test_native_shard_protocol.py`, so a
|
||||
schema edit that is not accompanied by regenerated output fails the suite rather
|
||||
than shipping stubs that disagree with the schema they claim to implement.
|
||||
|
||||
## DGR-006 decode and tail compatibility
|
||||
|
||||
`DecodeStep.bundle` is the versioned `TensorBundle` fast-path boundary. It is
|
||||
authoritative whenever present and supports architecture sidebands. The original
|
||||
`DecodeStep.tensor` remains readable as the compact one-tensor encoding for
|
||||
certified boundaries that need only one tensor; new readers wrap it into a
|
||||
one-member bundle. Tail completions use `TailResult`, which binds logits or a
|
||||
sampled token to request/recipe identity and sampling/template/reasoning inputs.
|
||||
|
||||
## Building and running the C++ conformance test
|
||||
|
||||
If the machine has no protobuf C++ toolchain:
|
||||
|
||||
@@ -33,6 +33,16 @@ enum SchemaVersion {
|
||||
SCHEMA_VERSION_1 = 1;
|
||||
}
|
||||
|
||||
// Certified transformer boundary family. Names alone are not an adapter: a
|
||||
// Node must select an explicit architecture contract before interpreting a
|
||||
// TensorBundle.
|
||||
enum ArchitectureType {
|
||||
ARCHITECTURE_TYPE_UNSPECIFIED = 0;
|
||||
ARCHITECTURE_TYPE_DENSE = 1;
|
||||
ARCHITECTURE_TYPE_MOE = 2;
|
||||
ARCHITECTURE_TYPE_MLA = 3;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Tensor bundle — the public activation boundary
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -142,6 +152,11 @@ message TensorBundle {
|
||||
// boundary payload can evolve without a whole-protocol version bump.
|
||||
uint32 bundle_version = 1;
|
||||
repeated NamedTensor tensors = 2;
|
||||
// Explicit adapter selection; names are never interpreted through unchecked
|
||||
// substitutions. UNSPECIFIED is accepted only for legacy DGR-002 bundles.
|
||||
ArchitectureType architecture = 3;
|
||||
// Adapter-owned semantic boundary, such as "pre_tail_residual".
|
||||
string boundary_point = 4;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -393,8 +408,9 @@ message ActivationChunk {
|
||||
// A decode step is one token: the envelope's identity fields are already fixed
|
||||
// for the life of the stream, so repeating them per token is pure overhead on
|
||||
// the hottest path. A DecodeStep carries only what changes — the step, the
|
||||
// position, and one tensor — and inherits the rest from the SessionOpen
|
||||
// handshake. A peer may always fall back to ActivationChunk with PHASE_DECODE.
|
||||
// position, and one compact boundary encoding — and inherits the rest from the
|
||||
// SessionOpen handshake. A Node may always fall back to ActivationChunk with
|
||||
// PHASE_DECODE.
|
||||
message DecodeStep {
|
||||
// Idempotency step within the session; also orders the stream.
|
||||
uint64 idempotency_step = 1;
|
||||
@@ -402,11 +418,50 @@ message DecodeStep {
|
||||
uint64 position = 2;
|
||||
// Tokens the receiver's cache must already hold. A mismatch is a CACHE_MISS.
|
||||
uint64 expected_past_len = 3;
|
||||
// The single boundary tensor for this token, typically [1, 1, hidden].
|
||||
// Legacy compact one-tensor boundary, typically [1, 1, hidden]. New readers
|
||||
// accept it as a TensorBundle with one member; writers retain it where the
|
||||
// selected architecture genuinely requires only one tensor.
|
||||
NamedTensor tensor = 4;
|
||||
// Work id for attribution; the session/epoch/fingerprint/range are inherited.
|
||||
string work_id = 5;
|
||||
int64 deadline_unix_nanos = 6;
|
||||
// Versioned architecture boundary. This carries sidebands such as MoE router
|
||||
// state or MLA/IndexShare state; when both fields are set, bundle is
|
||||
// authoritative and tensor is retained only for older-Node forwarding.
|
||||
TensorBundle bundle = 7;
|
||||
}
|
||||
|
||||
// Exact request-level identity for a tail result. Runtime recipe identity is
|
||||
// deliberately repeated here: sampling the right logits with the wrong chat
|
||||
// template or reasoning mode is a different request, not a compatible result.
|
||||
message RequestRecipeIdentity {
|
||||
string request_id = 1;
|
||||
string runtime_recipe_digest = 2;
|
||||
string chat_template_id = 3;
|
||||
string chat_template_version = 4;
|
||||
string reasoning_mode = 5;
|
||||
ArchitectureType architecture = 6;
|
||||
}
|
||||
|
||||
// Sampling is an explicit tail-only operation. A non-tail Shard never applies
|
||||
// final norm, LM head, row pruning, or sampling to its boundary output.
|
||||
message SamplingParameters {
|
||||
float temperature = 1;
|
||||
float top_p = 2;
|
||||
uint32 top_k = 3;
|
||||
uint64 seed = 4;
|
||||
bool greedy = 5;
|
||||
}
|
||||
|
||||
// Typed tail completion. The oneof keeps token id 0 unambiguous and prevents a
|
||||
// caller from treating an activation tensor as an inferred completion.
|
||||
message TailResult {
|
||||
RequestRecipeIdentity identity = 1;
|
||||
SamplingParameters sampling = 2;
|
||||
oneof output {
|
||||
TensorBundle logits = 3;
|
||||
uint32 sampled_token_id = 4;
|
||||
}
|
||||
}
|
||||
|
||||
// Drop session state for a Route Session. Bounded memory does not depend on
|
||||
@@ -471,7 +526,8 @@ message SessionResponse {
|
||||
ActivationChunk chunk = 2;
|
||||
Ack ack = 3;
|
||||
FlowControl flow_control = 4;
|
||||
ShardStatus status = 5;
|
||||
ShardStatus status = 5;
|
||||
TailResult tail_result = 6;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
BIN
packages/node/native/testdata/decode_step_golden.binpb
vendored
Normal file
BIN
packages/node/native/testdata/decode_step_golden.binpb
vendored
Normal file
Binary file not shown.
@@ -265,7 +265,9 @@ void TestDecodeFastPathIsSmall() {
|
||||
step->set_position(1024);
|
||||
step->set_expected_past_len(1024);
|
||||
step->set_work_id("work-7f3a");
|
||||
sp::NamedTensor *tensor = step->mutable_tensor();
|
||||
sp::TensorBundle *bundle = step->mutable_bundle();
|
||||
bundle->set_bundle_version(1);
|
||||
sp::NamedTensor *tensor = bundle->add_tensors();
|
||||
tensor->set_name("hidden_states");
|
||||
tensor->add_shape(1);
|
||||
tensor->add_shape(1);
|
||||
@@ -290,7 +292,27 @@ void TestDecodeFastPathIsSmall() {
|
||||
CHECK(parsed.ParseFromString(bytes));
|
||||
CHECK(parsed.kind_case() == sp::SessionRequest::kDecode);
|
||||
CHECK_EQ(parsed.decode().position(), 1024u);
|
||||
CHECK_EQ(parsed.decode().tensor().total_bytes(), 16u);
|
||||
CHECK_EQ(parsed.decode().bundle().tensors_size(), 1);
|
||||
CHECK_EQ(parsed.decode().bundle().tensors(0).total_bytes(), 16u);
|
||||
}
|
||||
|
||||
void TestDecodeBundleVector(const std::string &bytes) {
|
||||
sp::SessionRequest request;
|
||||
CHECK(request.ParseFromString(bytes));
|
||||
CHECK(request.kind_case() == sp::SessionRequest::kDecode);
|
||||
const sp::DecodeStep &step = request.decode();
|
||||
CHECK_EQ(step.idempotency_step(), 43u);
|
||||
CHECK_EQ(step.position(), 384u);
|
||||
CHECK_EQ(step.expected_past_len(), 384u);
|
||||
CHECK_EQ(step.work_id(), std::string("decode-7f3a"));
|
||||
CHECK(step.has_bundle());
|
||||
CHECK_EQ(step.bundle().bundle_version(), 1u);
|
||||
CHECK_EQ(step.bundle().architecture(), sp::ARCHITECTURE_TYPE_MLA);
|
||||
CHECK_EQ(step.bundle().boundary_point(), std::string("pre_tail_residual"));
|
||||
CHECK_EQ(step.bundle().tensors_size(), 2);
|
||||
CHECK_EQ(step.bundle().tensors(0).name(), std::string("hidden_states"));
|
||||
CHECK_EQ(step.bundle().tensors(1).name(), std::string("index_topk"));
|
||||
CHECK_EQ(step.bundle().tensors(1).dtype(), sp::DTYPE_INT32);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@@ -308,9 +330,11 @@ int main(int argc, char **argv) {
|
||||
ReadFile(testdata / "session_request_golden.binpb");
|
||||
const std::string capability_bytes =
|
||||
ReadFile(testdata / "capability_report_golden.binpb");
|
||||
const std::string decode_bytes = ReadFile(testdata / "decode_step_golden.binpb");
|
||||
|
||||
TestSessionRequestVector(session_bytes);
|
||||
TestCapabilityReportVector(capability_bytes);
|
||||
TestDecodeBundleVector(decode_bytes);
|
||||
TestUnknownFieldsArePreserved(session_bytes);
|
||||
TestSparseMessageParses();
|
||||
TestDecodeFastPathIsSmall();
|
||||
|
||||
Reference in New Issue
Block a user