Skip to content

refactor(zmq): dedup engine scaffolding and move connect mechanics to the client layer - #2073

Merged
slin1237 merged 5 commits into
mainfrom
zmq/phase2-layering
Aug 7, 2026
Merged

refactor(zmq): dedup engine scaffolding and move connect mechanics to the client layer#2073
slin1237 merged 5 commits into
mainfrom
zmq/phase2-layering

Conversation

@slin1237

@slin1237 slin1237 commented Aug 7, 2026

Copy link
Copy Markdown
Member

Motivation

Phase 2 of the ZMQ architecture cleanup (follows #2065/#2068): remove the duplication blocks and the one layering inversion the audit identified. No behavior changes.

Modifications (one commit each)

  1. Shared n>1 fan-out scaffoldingfan_out_requests/fan_out_tokenspeed_requests were ~90% identical; fan_out_n owns the clone/suffix/collapse dance, each engine keeps only its sampling-tweak closure.
  2. Shared per-tick stream emission — the twin ~50-line Chunk/Complete stanzas (finish tick carries tokens → emit Chunk, park Complete) collapse into StreamState::emit_tick; mappers contribute only finish reason, matched stop, and token ids.
  3. Harmony request building: one dispatch per (backend × kind) — the ~330-line 7-arm match becomes HarmonyBody resolution + build_harmony_proto, with one shared error path. vLLM/TokenSpeed build via static translators, so one arm each now covers both gRPC and direct-ZMQ. The 5-arm stop-id injection becomes ProtoGenerateRequest::extend_stop_token_ids. Net −164 lines.
  4. ZMQ connect mechanics move to zmq_client — handshake-port derivation (FNV pinned against serve.py, vectors moved with it), socket layout, ipc-dir prep, EOS resolution, and the handshake now live in zmq_client::connect_for_worker; worker.rs keeps a thin wrapper and stops reaching into the router layer for its transport. Also fixes the misleading EOS warning (connect-time miss falls back to tokenizer EOS at request time — it never meant 'runs to max_tokens').

Checklist

  • cargo fmt clean; clippy --all-targets clean on changed files
  • cargo test -p smg --lib: 1435 passed (single failure is the pre-existing middleware::metrics interner flake, identical on clean main)

@github-actions github-actions Bot added grpc gRPC client and router changes model-gateway Model gateway crate changes labels Aug 7, 2026
@coderabbitai

coderabbitai Bot commented Aug 7, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: c0358d1b-ae3b-49cf-b457-d05a31ce93e9

📥 Commits

Reviewing files that changed from the base of the PR and between f7c1ec8 and baea129.

📒 Files selected for processing (1)
  • model_gateway/src/routers/grpc/proto_wrapper.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • model_gateway/src/routers/grpc/proto_wrapper.rs

📝 Walkthrough

Summary by CodeRabbit

  • New Features
    • Improved handling of stop sequences across supported model backends.
    • Added configurable connection handshake settings.
  • Bug Fixes
    • Generated content now appears before completion notifications in streaming responses.
    • Engine failures are reported more clearly as request errors.
    • Improved end-of-sequence handling with metadata or fallback detection.
  • Reliability
    • Strengthened connection setup and socket-directory security.
    • Improved multi-sample response handling across supported backends.
    • Improved consistency when connecting to model-serving workers.

Walkthrough

Harmony request construction now uses shared backend dispatch and stop-token handling. ZMQ connection setup moved into the client module. vLLM and TokenSpeed share stream and fan-out handling. Worker connection logic delegates to the client module.

Changes

Harmony and ZMQ backend integration

Layer / File(s) Summary
Shared Harmony request construction
model_gateway/src/routers/grpc/harmony/stages/request_building.rs, model_gateway/src/routers/grpc/proto_wrapper.rs
Chat and Responses requests use HarmonyBody and build_harmony_proto. Stop-token IDs are applied through extend_stop_token_ids, with coverage for supported and no-sampling paths.
Centralized ZMQ connection setup
model_gateway/src/routers/grpc/zmq_client.rs
The client module handles handshake ports, socket addresses, TCP overrides, socket-directory checks, EOS resolution, and worker connections.
Shared ZMQ streaming and fan-out
model_gateway/src/routers/grpc/zmq_client.rs
vLLM and TokenSpeed use shared tick emission and fan-out scaffolding for chunks, completions, finish metadata, errors, and subrequests.
Worker delegation and validation updates
model_gateway/src/worker/worker.rs
The worker delegates ZMQ connection setup to connect_for_worker, maps failures to WorkerError::ConnectionFailed, and updates related tests and imports.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Worker
  participant ZmqClient
  participant Backend
  participant StreamMapper
  Worker->>ZmqClient: connect_for_worker(...)
  ZmqClient->>Backend: establish ZMQ connection
  Backend-->>ZmqClient: connected client
  Worker->>Backend: submit generation request
  Backend-->>StreamMapper: stream output ticks
  StreamMapper-->>Worker: chunks and completion
Loading

Possibly related PRs

Suggested reviewers: catherinesue, key4ng

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main refactoring: shared engine scaffolding and moving ZMQ connection mechanics into the client layer.
Description check ✅ Passed The description directly explains the refactoring objectives, implementation changes, testing, and reported behavior-preservation intent.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch zmq/phase2-layering

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@claude

claude Bot commented Aug 7, 2026

Copy link
Copy Markdown

👋 The PR description doesn't fully follow
PULL_REQUEST_TEMPLATE.md:

  • Missing header: ## Description
  • Missing header: ### Problem
  • Missing header: ### Solution
  • Missing header: ## Changes
  • Missing header: ## Test Plan

Please update the PR description so reviewers have the context they need.

Comment on lines +168 to 176
req.include_stop_token_in_output = true;
}
debug!(
stop_token_count = harmony_stop_ids.len(),
"Injected Harmony stop tokens"
);
}

// The client resolves string `stop`s its engine can't match and

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Nit: The catch-all _ arm here returns a String error that the caller wraps as error::bad_request (HTTP 400). The old code used error::internal_error (HTTP 500) for the equivalent BackendClient::Zmq(zmq_client) arm with an unsupported runtime, which is semantically more accurate — this is a wiring bug, not a malformed client request.

Practically unreachable since connect() only admits vLLM/TokenSpeed runtimes, but if it's ever hit, a 400 would mislead the caller into thinking their request was wrong rather than pointing at an internal misconfiguration.

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Clean refactoring. The four changes (shared fan-out scaffolding, shared emit_tick, consolidated Harmony request builder, ZMQ connect mechanics moved to client layer) are all behavior-preserving and well-tested. One nit filed about the catch-all error type change (internal_error → bad_request) in the Harmony builder — practically unreachable but semantically off.

0 🔴 Important · 1 🟡 Nit · 0 🟣 Pre-existing

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
model_gateway/src/routers/grpc/proto_wrapper.rs (1)

1095-1128: 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

🔴 Important — The new method was inserted between the #[expect] attribute and as_sglang.

Lines 1095-1098 hold #[expect(clippy::panic, ...)] and line 1094 holds the doc comment /// Get SGLang variant (panics if not SGLang). Both now attach to extend_stop_token_ids. Two consequences follow:

  • extend_stop_token_ids contains no panic!, so the expectation is unfulfilled. Clippy reports unfulfilled_lint_expectation.
  • as_sglang at line 1128 still calls panic! but has lost its allowance and its doc comment. If clippy::panic is denied for this crate, the lint gate fails there.

Place extend_stop_token_ids after as_sglang so the attribute and doc comment return to the accessor.

🔧 Proposed fix: restore the accessor attribute and move the new method
 impl ProtoGenerateRequest {
-    /// Get SGLang variant (panics if not SGLang)
-    #[expect(
-        clippy::panic,
-        reason = "typed accessor: caller guarantees variant via is_sglang() check"
-    )]
     /// Append stop token ids to the request's sampling params (TRT-LLM keeps
     /// them on the request itself). Requests without sampling params are left
     /// unchanged, matching the per-engine injection this replaces.
     pub fn extend_stop_token_ids(&mut self, ids: &[u32]) {
@@
             Self::Trtllm(req) => req.stop_token_ids.extend_from_slice(ids),
         }
     }
 
+    /// Get SGLang variant (panics if not SGLang)
+    #[expect(
+        clippy::panic,
+        reason = "typed accessor: caller guarantees variant via is_sglang() check"
+    )]
     pub fn as_sglang(&self) -> &sglang::GenerateRequest {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@model_gateway/src/routers/grpc/proto_wrapper.rs` around lines 1095 - 1128,
Move extend_stop_token_ids so it appears after as_sglang and its full
implementation, restoring the existing doc comment and #[expect(clippy::panic,
...)] attribute directly above as_sglang. Ensure the attribute applies to the
panic-containing accessor and the new method no longer inherits either
annotation.
🧹 Nitpick comments (5)
model_gateway/src/routers/grpc/harmony/stages/request_building.rs (2)

130-141: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

🟡 Nit — Replace the wildcard arm with explicit variants to keep exhaustiveness.

The request-id match at lines 88-116 already returns an error for Generate, Completion, Embedding, Classify, and Messages. Both the Embedding arm and the _ arm here are unreachable. The _ arm also disables exhaustiveness checking. If a new RequestType variant is added later, this match silently accepts it and returns a generic error instead of failing to compile.

List the remaining variants in one reject arm.

♻️ Proposed refactor
             RequestType::Responses(request) => HarmonyBody::Responses(request.as_ref()),
-            RequestType::Embedding(_) => {
-                return Err(error::bad_request(
-                    "harmony_embedding_not_supported",
-                    "Embedding requests are not supported with Harmony models".to_string(),
-                ));
-            }
-            _ => {
+            RequestType::Generate(_)
+            | RequestType::Completion(_)
+            | RequestType::Embedding(_)
+            | RequestType::Classify(_)
+            | RequestType::Messages(_) => {
                 return Err(error::bad_request(
                     "unsupported_request_type",
                     "Unsupported request type for Harmony models".to_string(),
                 ));
             }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@model_gateway/src/routers/grpc/harmony/stages/request_building.rs` around
lines 130 - 141, Update the RequestType match in the request-building flow to
replace the wildcard arm with one explicit reject arm listing every remaining
RequestType variant, while retaining the existing Embedding-specific error and
behavior for already handled variants. Preserve exhaustiveness checking so
adding a new variant requires updating this match.

152-158: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

🟡 Nit — The single error path maps wiring bugs to HTTP 400.

build_harmony_proto returns two distinct classes of error. Builder failures come from client input. The fallback arm at lines 342-346 returns "unsupported backend runtime ...", which the comment describes as a wiring bug. Both now become bad_request("invalid_request_parameters", ...). A server misconfiguration is then reported to the caller as a 400 and counted as a client error.

Distinguish the two classes. One option is a small error enum from build_harmony_proto; a simpler option is to keep the unsupported-runtime case as an internal_error.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@model_gateway/src/routers/grpc/harmony/stages/request_building.rs` around
lines 152 - 158, Update the error handling around
HarmonyRequestBuildingStage::execute and build_harmony_proto to distinguish
client validation failures from unsupported backend runtime failures. Preserve
bad_request("invalid_request_parameters", ...) for invalid input, but map the
unsupported-runtime fallback to internal_error so wiring or server configuration
issues are reported as server errors.
model_gateway/src/routers/grpc/zmq_client.rs (3)

242-242: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

🟡 Nit — fail is an identity closure.

let fail = |reason: String| reason; returns its argument unchanged. Every call site wraps a format! that is already the final error value. Remove the closure and pass the formatted string directly.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@model_gateway/src/routers/grpc/zmq_client.rs` at line 242, Remove the
identity closure fail and update each of its call sites to pass the existing
format! result directly as the error value, preserving the current messages and
behavior.

1409-1416: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

🟡 Nit — Add a test for the symlinked-parent rejection.

The doc comment at Line 243 states that symlink_metadata is used so "a symlinked parent must not redirect the checks (or the sockets) into a directory we did not verify." That property is the security reason for choosing symlink_metadata over metadata, and no test pins it. A future change back to metadata would pass the whole suite.

💚 Proposed test
#[cfg(unix)]
#[tokio::test]
async fn ensure_ipc_socket_dir_rejects_a_symlinked_parent() {
    let base = tempfile::tempdir().unwrap();
    let real = base.path().join("real");
    std::fs::create_dir(&real).unwrap();
    let link = base.path().join("link");
    std::os::unix::fs::symlink(&real, &link).unwrap();
    let url = format!("ipc://{}/x.ipc", link.display());
    assert!(ensure_ipc_socket_dir(&url).await.is_err());
}

As per coding guidelines: "Run the pr-test-analyzer agent to verify that tests adequately cover new or changed functionality."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@model_gateway/src/routers/grpc/zmq_client.rs` around lines 1409 - 1416, Add a
Unix-only async test alongside
ensure_ipc_socket_dir_rejects_a_non_directory_parent that creates a real
directory, symlinks a parent path to it, and verifies ensure_ipc_socket_dir
returns an error for an IPC URL using the symlinked parent. Keep the test
focused on rejecting symlinked parents and use the existing tempfile and tokio
test patterns.

Source: Coding guidelines


304-314: 🚀 Performance & Scalability | 🔵 Trivial | 💤 Low value

🟡 Nit — Blocking filesystem calls run on the runtime thread here.

model_dir.is_dir() and EosTokenIds::from_model_dir perform synchronous filesystem I/O inside an async fn. The adjacent ensure_ipc_socket_dir was deliberately made async for exactly this reason (see its doc comment at Line 228). The cost is small — one stat plus two small JSON reads per connect — but the two paths now follow different policies. Consider wrapping the EOS resolution in tokio::task::spawn_blocking or converting it to tokio::fs.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@model_gateway/src/routers/grpc/zmq_client.rs` around lines 304 - 314, Move
the synchronous EOS resolution in the connect flow into a blocking-safe async
path: wrap both model_dir.is_dir() and EosTokenIds::from_model_dir in
tokio::task::spawn_blocking, or replace their filesystem access with tokio::fs
while preserving the existing warning and default behavior for non-directory
model IDs. Handle the async task/result errors consistently with the surrounding
connection logic.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@model_gateway/src/routers/grpc/harmony/stages/request_building.rs`:
- Around line 285-347: Add unit tests for build_harmony_proto covering ZMQ vLLM
and TokenSpeed clients with both Chat and Responses requests, verifying each
dispatches to the correct ProtoGenerateRequest variant. Also test the
unsupported-runtime fallback returns the expected error, using existing
request/client test helpers and keeping the builder behavior unchanged.

In `@model_gateway/src/routers/grpc/zmq_client.rs`:
- Around line 922-933: Update TokenSpeedGenerateStream::map_output to detect the
wire-level "error" finish reason before normalizing it or calling
state.emit_tick, and return the corresponding Result::Err instead of emitting a
normal completion. Match the existing vLLM error mapping behavior.

---

Outside diff comments:
In `@model_gateway/src/routers/grpc/proto_wrapper.rs`:
- Around line 1095-1128: Move extend_stop_token_ids so it appears after
as_sglang and its full implementation, restoring the existing doc comment and
#[expect(clippy::panic, ...)] attribute directly above as_sglang. Ensure the
attribute applies to the panic-containing accessor and the new method no longer
inherits either annotation.

---

Nitpick comments:
In `@model_gateway/src/routers/grpc/harmony/stages/request_building.rs`:
- Around line 130-141: Update the RequestType match in the request-building flow
to replace the wildcard arm with one explicit reject arm listing every remaining
RequestType variant, while retaining the existing Embedding-specific error and
behavior for already handled variants. Preserve exhaustiveness checking so
adding a new variant requires updating this match.
- Around line 152-158: Update the error handling around
HarmonyRequestBuildingStage::execute and build_harmony_proto to distinguish
client validation failures from unsupported backend runtime failures. Preserve
bad_request("invalid_request_parameters", ...) for invalid input, but map the
unsupported-runtime fallback to internal_error so wiring or server configuration
issues are reported as server errors.

In `@model_gateway/src/routers/grpc/zmq_client.rs`:
- Line 242: Remove the identity closure fail and update each of its call sites
to pass the existing format! result directly as the error value, preserving the
current messages and behavior.
- Around line 1409-1416: Add a Unix-only async test alongside
ensure_ipc_socket_dir_rejects_a_non_directory_parent that creates a real
directory, symlinks a parent path to it, and verifies ensure_ipc_socket_dir
returns an error for an IPC URL using the symlinked parent. Keep the test
focused on rejecting symlinked parents and use the existing tempfile and tokio
test patterns.
- Around line 304-314: Move the synchronous EOS resolution in the connect flow
into a blocking-safe async path: wrap both model_dir.is_dir() and
EosTokenIds::from_model_dir in tokio::task::spawn_blocking, or replace their
filesystem access with tokio::fs while preserving the existing warning and
default behavior for non-directory model IDs. Handle the async task/result
errors consistently with the surrounding connection logic.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 7405db94-b302-41ca-8581-b2a82df27bef

📥 Commits

Reviewing files that changed from the base of the PR and between f5a02a4 and c1f35d4.

📒 Files selected for processing (4)
  • model_gateway/src/routers/grpc/harmony/stages/request_building.rs
  • model_gateway/src/routers/grpc/proto_wrapper.rs
  • model_gateway/src/routers/grpc/zmq_client.rs
  • model_gateway/src/worker/worker.rs

Comment thread model_gateway/src/routers/grpc/harmony/stages/request_building.rs
Comment thread model_gateway/src/routers/grpc/zmq_client.rs Outdated
fan_out_requests and fan_out_tokenspeed_requests were structurally
identical (clone n subs, suffix rids, collapse n to 1) differing only in
vLLM's per-sub seed derivation. Extract fan_out_n; each engine keeps
just its sampling tweak closure.

Signed-off-by: Simo Lin <25425177+slin1237@users.noreply.github.com>
…streams

VllmGenerateStream and TokenSpeedGenerateStream carried near-identical
~50-line emission tails: build the Complete, and when the finish tick
also carried tokens, emit a Chunk first and park the Complete for the
next poll. Move that dance into StreamState::emit_tick; each mapper now
contributes only its engine-specific inputs (finish-reason string,
matched_stop, token ids). vLLM's engine-error surfacing stays in its
mapper; TokenSpeed documents its absent matched_stop.

Signed-off-by: Simo Lin <25425177+slin1237@users.noreply.github.com>
…(backend, kind)

The builder was a ~330-line 7-arm backend match, each arm repeating the
same 4-way request-type match, per-arm error mapping, and reject arms —
and the two ZMQ arms re-duplicated the gRPC vLLM/TokenSpeed arms
verbatim.

- Resolve the request kind once (HarmonyBody: Chat with the modified
  body applied, or Responses), with the rejects stated a single time.
- build_harmony_proto is one compact (backend x kind) match where every
  arm is just the engine's builder call, sharing a single error path at
  the call site. vLLM and TokenSpeed build through static translators,
  so one arm each now covers both gRPC and direct-ZMQ — the transport
  duplication is gone, and an impossible runtime remains a hard error.
- The 5-arm Harmony stop-id injection block becomes
  ProtoGenerateRequest::extend_stop_token_ids, with TRT-LLM's
  include_stop_token_in_output kept as the one engine-specific line.

Net -164 lines with no behavior change.

Signed-off-by: Simo Lin <25425177+slin1237@users.noreply.github.com>
worker/worker.rs owned the entire ZMQ connect path — handshake-port
derivation (FNV-1a, pinned against serve.py), socket-address layout,
ipc-dir preparation, EOS resolution, and the engine handshake — while
reverse-importing ZmqEngineClient from routers::grpc to do it. The
worker layer was reaching into the router layer for its transport.

Move it all behind zmq_client::connect_for_worker: one connect path,
plain-reason errors, owned by the module that owns every other piece of
the ZMQ client (EOS, translate, streams). The worker keeps a thin
wrapper that maps into its client slot and WorkerError, and its reverse
import shrinks to the BackendClient/GrpcClient types it stores. The
pinned FNV vectors and address/ipc-dir tests move with the code. Also
corrects the stale EOS warning: connect-time ids being unavailable
falls back to the tokenizer EOS set at request time, it does not mean
generation runs to max_tokens.

Signed-off-by: Simo Lin <25425177+slin1237@users.noreply.github.com>
@slin1237
slin1237 force-pushed the zmq/phase2-layering branch from c1f35d4 to 52e319b Compare August 7, 2026 20:54
let Some(parent) = Path::new(path).parent() else {
return Ok(());
};
let fail = |reason: String| reason;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Nit: fail is now an identity closure (|reason: String| reason) — a leftover from the move out of worker.rs where it wrapped into WorkerError::ConnectionFailed. Every call site is just fail(format!(...)) which is format!(...). You can drop the binding and pass the format!() strings directly to Err / .map_err().

Suggested change
let fail = |reason: String| reason;

@slin1237

slin1237 commented Aug 7, 2026

Copy link
Copy Markdown
Member Author

Review comments addressed in f7c1ec8:

  • proto_wrapper attribute hijack (CodeRabbit, Important) — fixed. Confirmed real and it was also the cause of this PR's CI lint failure: extend_stop_token_ids had been inserted between as_sglang's doc + #[expect(clippy::panic)] and the fn, so the expect went unfulfilled and as_sglang's panic went unshielded. Attachment restored; added a unit test for the new method (per-variant params, TRT-LLM field, missing-params no-op).
  • TokenSpeed error finish reason (CodeRabbit, Major) — fixed with the suggested pre-emit_tick guard: engine-side failures now surface as a tonic error, mirroring the vLLM stream, instead of a normal Complete.
  • 400 vs 500 for the impossible-runtime arm (Claude nit) — fixed by typing the build error (Request → 400, Wiring → 500), restoring the pre-refactor semantics.
  • Identity fail closure (Claude nit) — removed.
  • Dispatcher unit tests (CodeRabbit, Minor) — deferred with reason: build_harmony_proto's arms take connected engine clients (gRPC channels / completed ZMQ handshakes), so unit coverage needs the mock-engine harness per backend — meaningful but not a quick win. The dispatch paths are exercised live by the e2e chat lanes (including the ZMQ lane landing in the tests PR). Tracked for the test-coverage pass alongside the deferred streaming-harness items from feat(zmq): forward EOS and resolve string stops for the direct backend (4/7) #2057/feat(zmq): synthesize Harmony stop strings for the direct backend (5/7) #2058.

… error classes

- extend_stop_token_ids was inserted between as_sglang's doc +
  #[expect(clippy::panic)] and the fn itself, hijacking both (and
  leaving as_sglang's panic unshielded — the CI lint failure). Restore
  the attachment and give the new method its own placement, plus a test
  covering the sampling-params/TRT-LLM/missing-params variants.
- TokenSpeed streams now surface an engine 'error' finish reason as a
  tonic error before emit_tick, mirroring the vLLM stream's guard,
  instead of emitting a normal Complete for a failed request.
- Harmony build failures are typed: builder rejections stay 400, the
  impossible backend/runtime pairing is a wiring bug and returns 500
  (restoring the pre-refactor semantics the collapse had lost).
- Drop the identity 'fail' closure left over from the worker.rs move.

Signed-off-by: Simo Lin <25425177+slin1237@users.noreply.github.com>
@slin1237
slin1237 force-pushed the zmq/phase2-layering branch from f7c1ec8 to baea129 Compare August 7, 2026 22:35
@slin1237
slin1237 merged commit 4f1010d into main Aug 7, 2026
35 of 40 checks passed
@slin1237
slin1237 deleted the zmq/phase2-layering branch August 7, 2026 23:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

grpc gRPC client and router changes model-gateway Model gateway crate changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant