Skip to content

feat(generation): remote vLLM/NIM generation backend (GPU-free)#575

Draft
binaryaaron wants to merge 16 commits into
mainfrom
agonzales/remote-generation-backend
Draft

feat(generation): remote vLLM/NIM generation backend (GPU-free)#575
binaryaaron wants to merge 16 commits into
mainfrom
agonzales/remote-generation-backend

Conversation

@binaryaaron

@binaryaaron binaryaaron commented Jun 8, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Adds a GPU-free remote generation backend for OpenAI-compatible vLLM/NIM /v1/completions endpoints, including retries, dialect-aware request fields, and structured-output handling.
  • Moves the shared record-generation loop into GeneratorBackend while keeping engine-specific calls in the local and remote backends.
  • Adds a typed ModelHost[ModelT, TokenizerT] lifecycle boundary for locally owned models; VllmBackend implements it, and feat: add option to use local classification #615 can use the same shape for its Hugging Face classifier.
  • Rebases the branch onto main and adopts the nested structured-generation configuration introduced there.

Test plan

  • mise run check
  • 123 focused generation/model-host unit tests
  • 3 CPU remote-generation smoke tests

@coderabbitai

coderabbitai Bot commented Jun 8, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: aacfae21-80fc-4241-967f-d728c062a3bc

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch agonzales/remote-generation-backend

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

@binaryaaron binaryaaron force-pushed the agonzales/remote-generation-backend branch from 135a18b to 3d14d00 Compare June 9, 2026 04:05
Base automatically changed from binaryaaron/vllm-observability-improvements to main June 9, 2026 18:08
@binaryaaron binaryaaron force-pushed the agonzales/remote-generation-backend branch from 3d14d00 to 5a6a324 Compare July 8, 2026 18:19
read_vllm_runtime_metrics,
)
from ..llm.metadata import ModelMetadata
from ..llm.model_host import ModelHost
return random.uniform(0.0, capped)


def _coerce_token_count(value: object) -> int:
binaryaaron and others added 16 commits July 10, 2026 21:41
Add RemoteBackend, a GPU-free generation backend that calls an external
vLLM OpenAI-compatible /v1/completions endpoint instead of loading a local
engine. Intended for generation-only setups where the fine-tuned LoRA
adapter is already served by a running endpoint.

- Lift the shared batch-generation loop from VllmBackend up into
  GeneratorBackend as a concrete template method. VllmBackend and
  RemoteBackend now implement only the engine-specific pieces
  (_generate_batch, _get_prompt_token_count, prepare_params, initialize,
  teardown). TimeseriesBackend keeps its own generate() override.
- Add RemoteParameters (endpoint_url, model, api_key_env, timeout_seconds,
  max_concurrency) nested under GenerateParameters; wire backend selection
  in SafeSynthesizer.generate(). Remote + time-series is rejected at config
  validation time.
- Structured generation maps to vLLM guided_regex / guided_json; the
  XGrammar structural_tag method has no OpenAI-API equivalent and is
  rejected with a clear error.
- Uses httpx (already a dependency); one request per record, issued
  concurrently up to max_concurrency, with per-completion token counts
  taken from the server's usage report.

Verified end-to-end against a live vLLM server (guided_json) plus 24 unit
tests; ruff and ty clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Aaron Gonzales <aagonzales@nvidia.com>
Testing against a second vLLM server (0.20+, serving Nemotron-3-Super)
showed it silently ignores the legacy top-level guided_json / guided_regex
request fields, producing unconstrained output. Switch RemoteBackend to the
modern `structured_outputs: {json|regex: ...}` field, which both old and new
vLLM OpenAI servers honor and which mirrors the offline StructuredOutputsParams
the local backend already builds.

Verified end-to-end against the new server with the regex method (status
complete, valid JSONL records parsed by TabularDataProcessor). Note: the
json_schema method works at the transport level but the server pretty-prints
the JSON across lines, which the JSONL processor rejects; the regex method
enforces the compact single-line shape the pipeline expects.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Aaron Gonzales <aagonzales@nvidia.com>
structural_tag was originally rejected on the assumption it had no HTTP
equivalent -- true for the legacy guided_* extensions, but stale now that
the backend uses the modern `structured_outputs` field. Verified against a
vLLM 0.20+ server that `structured_outputs: {structural_tag: <json-string>}`
is accepted and yields multi-record JSONL.

Map all three schema methods to structured_outputs (regex/json/structural_tag),
so the `auto` default (which resolves to structural_tag on xgrammar/auto
backends) now works out-of-the-box for remote generation. End-to-end run with
auto produced valid, realistic records via TabularDataProcessor.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Aaron Gonzales <aagonzales@nvidia.com>
A PEP 723 uv tool to quickly spin up a vLLM model and poke it -- the debugging
companion to VllmBackend / RemoteBackend. Three subcommands:

- serve: launch the OpenAI-compatible server from the project venv, optionally
  attaching a LoRA adapter (--adapter / --max-lora-rank). Bakes in this box's
  A100 workarounds (offline HF cache, FLASH_ATTN, --enforce-eager). --dry-run
  prints the command without touching the GPU.
- call: fire one or more chat/text completions, with thinking toggle, structured
  outputs (--json-schema / --regex / --structural-tag -> structured_outputs),
  token usage, latency, and --json output.
- models: list served models / health check.

Distilled from dev/evals/route-eval/{serve_vllm.sh,vllm_probe.py}. Validated
against a live server (models + chat + json-schema-constrained text call) and
serve via --dry-run.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Aaron Gonzales <aagonzales@nvidia.com>
Durable session note: RemoteBackend design and verified facts (the
structured_outputs vs legacy guided_* version split, structural_tag now
supported, httpx base-path join), the dev flow used, and a roadmap for
targeting NIM / Triton / Dynamo / TensorRT-LLM (incl. a structured-output
dialect seam if a target only speaks response_format).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Aaron Gonzales <aagonzales@nvidia.com>
…e arc

Horizon note capturing the multi-month direction: self-managing,
backend-like components with slowly-exposed knobs, coordinated by an async
orchestrator that treats each component (and each component treats itself) as
a state machine over a single shared state system, with config first-class
throughout. Records what the codebase already embodies, the target contracts,
engine-vs-transform archetypes, tensions to watch, and an incremental
adoption path (backends -> evaluation -> assembly -> PII -> orchestrator).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Aaron Gonzales <aagonzales@nvidia.com>
The --enforce-eager workaround existed only because the venv's FlashInfer was
broken (vLLM's torch.compile path imports flashinfer.comm unconditionally).
With FlashInfer repaired, default to CUDA graphs and make --eager an opt-in
escape hatch for broken-FlashInfer environments. Verified end-to-end: same
financial-transactions LoRA workload dropped from ~119s (eager) to ~15s
(CUDA graphs) for 10 records, with identical, valid output.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Aaron Gonzales <aagonzales@nvidia.com>
mise run format (copyright_fixer) added SPDX headers to the chronicle notes.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Aaron Gonzales <aagonzales@nvidia.com>
…servers

NIM (TensorRT-LLM) rejects several vLLM sampling extensions with a 400
(repetition_penalty, top_k, skip_special_tokens, include_stop_str_in_output),
so RemoteBackend's vLLM-shaped request fails against it out of the box.

Add RemoteParameters.dialect ("vllm" default | "openai"). prepare_params emits
the vLLM extensions only for the "vllm" dialect; "openai" sends just the
universal OpenAI fields (temperature/top_p/max_tokens/n) for strict servers
like NIM and Triton. Resolved sampling values are identical across dialects;
only the wire fields differ. Verified the dialect finding live against a NIM
serving a Safe Synthesizer LoRA.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Aaron Gonzales <aagonzales@nvidia.com>
…ronicle

Records the NIM + vLLM real-adapter runtime validation, the NIM sampling-field
dialect finding (and the shipped RemoteParameters.dialect seam), the
face-validity-only quality caveat, the one clean perf comparison (eager->CUDA
graphs ~7.8x), and the FlashInfer corrupted-install root cause + fix.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Aaron Gonzales <aagonzales@nvidia.com>
…icle

- AGENTS.md module map: note RemoteBackend and that GeneratorBackend now owns
  the concrete generate() template method (was stale after the refactor).
- chronicle: record the measured results — real Evaluator scores (SQS 8.0 /
  Data Privacy 8.8 on 250 remote-generated records) and the controlled
  single-stream vLLM-vs-NIM throughput (74.7 vs 66.3 tok/s), replacing the
  "not yet done" placeholder.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Aaron Gonzales <aagonzales@nvidia.com>
…rsing

Add RemoteParameters.max_retries (default 4) and retry transient failures
(connection drops, timeouts, HTTP 408/409/425/429/5xx) with full-jitter
exponential backoff that honors a Retry-After header; non-retryable statuses
fail fast. Split _complete_one into _post_completion / _raise_for_status /
_parse_completion, coerce the advisory usage token count defensively, and
treat only a missing choices array as fatal.

Size the prompt-length clamp from an opportunistic local tokenizer (None on
the offline-remote path) and compact pretty-printed json_schema completions to
single-line JSONL (disable_any_whitespace at the source on the vllm dialect,
client-side compaction as a portable net). Bake the constant prompt into the
request body once and submit per-request futures so each completion retries
independently. Expand unit coverage for retries, parsing, and helpers.

Signed-off-by: Aaron Gonzales <aagonzales@nvidia.com>
Validate the at-most-one constraint rule before touching the filesystem, and
read schema/tag files through helpers that exit via bad_input (code 125) on a
missing file or malformed JSON instead of raising a traceback. Guard
parse_call_response against a response with no choices so do_call reports it as
a failed CallResult rather than crashing on a KeyError/IndexError.

Signed-off-by: Aaron Gonzales <aagonzales@nvidia.com>
Add a CPU smoke test that drives the full RemoteBackend path -- real httpx
client, ThreadPoolExecutor concurrency, the shared generate() loop, the real
tabular processor, retry/backoff, and JSON compaction -- against an in-process
loopback HTTP stub (no GPU, no network, no model). Add an opt-in live smoke
test against build.nvidia.com gated by NSS_REMOTE_SMOKE_SEND=1 + NVIDIA_API_KEY
that validates the transport and skips when a model rejects /v1/completions.

Share tokenizer-free metadata and minimal-config builders via conftest and
document both layers in the smoke README.

Signed-off-by: Aaron Gonzales <aagonzales@nvidia.com>
…er KB

Move the remote-generation-backend and modular-stateful-architecture-arc
chronicle notes out of the product repo into the repo's knowledge base
(/root/kb/safe-synthesizer/raw), where the working-memory chronicle belongs.
The KB graph was refreshed to reflect the promoted content.

Signed-off-by: Aaron Gonzales <aagonzales@nvidia.com>
Signed-off-by: Aaron Gonzales <aagonzales@nvidia.com>
@binaryaaron binaryaaron force-pushed the agonzales/remote-generation-backend branch from 5a6a324 to c226c79 Compare July 10, 2026 21:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant