Repository navigation
Design spec: benchmark-first CPU text refinement (llama.cpp tier conditional) - #385
tonythethompson wants to merge 1 commit into
Conversation
How to use the Graphite Merge QueueAdd either label to this PR to merge it via the merge queue:
You must have a Graphite account in order to use the merge queue. Sign up using this link. An organization admin has enabled the Graphite Merge Queue in this repository. Please do not merge from GitHub as this will restart CI on PRs being processed by the merge queue. |
|
Important Draft PR not reviewedDraft PRs are not automatically reviewed by default.
To automatically review draft PRs, update your CodeRabbit configuration: reviews:
auto_review:
drafts: true
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. Comment |
There was a problem hiding this comment.
This design specification is well-structured and ready for product sign-off. The proposal to add llama.cpp/GGUF text refinement support for CPU-only and low-VRAM users addresses a clear coverage gap with a sound technical approach.
Key strengths:
- Problem statement clearly identifies the underserved user segment (CPU-only/low-VRAM)
- Technical design follows existing architectural patterns (managed sidecar, hardware-tier routing, model manifest integration)
- Comprehensive quality gates (QwenRefinementOutputGuard parity checks, DubBench metrics)
- Risk mitigation strategies address lifecycle concerns, quality degradation, and hardware detection edge cases
- Execution plan provides clear phased implementation with measurable acceptance criteria
The specification is complete and no blocking issues were identified. The open questions appropriately defer packaging and threshold decisions to implementation review.
You can now have the agent implement changes and create commits directly on your pull request's source branch. Simply comment with /q followed by your request in natural language to ask the agent to make changes.
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
The specification misstates current runtime behavior and needs planner-aligned routing, model-governance requirements, and a valid semantic quality gate.
Review effort: Balanced
Findings: 6
Open (6)
Correct runtime baseline before proposing a new hardware tier · New Reframe model performance gap and require benchmark evidence · New Add semantic quality metrics beyond output safety checks · New Include model governance metadata and pinned source revision · New Require provider availability and successful runtime smoke tests · New Use runtime planner output instead of hardcoded hardware probes · New
What changed in this PR
Proposes a llama.cpp/GGUF text-refinement tier for CPU-only and low-VRAM users.
Changes:
- Defines sidecar architecture, routing, readiness, and manifest integration.
- Proposes quality and performance validation.
- Documents rollout risks and open distribution questions.
| File | Description |
|---|---|
docs/specs/design-llamacpp-cpu-text-tier.md |
Adds the llama.cpp CPU text-tier design specification. |
💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
|
|
||
| | Engine | Path | Hardware floor | | ||
| |---|---|---| | ||
| | `QwenTextRefinementEngine` | ONNX Runtime GenAI; accelerated via TensorRT-RTX / NPU EPs (`tools/olive/Export-QwenTrtRtxGenAi.ps1`, VitisAI/QNN/OpenVINO recipes) | Mid-range RTX GPU or supported NPU | |
| | `QwenTextRefinementEngine` | ONNX Runtime GenAI; accelerated via TensorRT-RTX / NPU EPs (`tools/olive/Export-QwenTrtRtxGenAi.ps1`, VitisAI/QNN/OpenVINO recipes) | Mid-range RTX GPU or supported NPU | | ||
| | `GeminiCloudTextRefinementEngine` | Cloud API | Network + egress consent | | ||
|
|
||
| `RoutedTextRefinementEngine` (`src/Trackdub.Inference.Onnx/QwenTextRefinement/RoutedTextRefinementEngine.cs`) picks the Qwen local engine by default and the Gemini engine only on explicit alias. **There is no local option for a CPU-only or low-VRAM machine.** On that hardware, ORT GenAI CPU execution of the current 7–9B class export is slow enough that users silently fall back to cloud — or abandon local refinement entirely. GPU/NPU-less users are an explicit target segment, so this is a coverage gap, not an edge case. |
| - A third `ITextRefinementEngine` (`LlamaCppTextRefinementEngine`) registered behind `RoutedTextRefinementEngine`, real readiness semantics per the *never-fake-readiness* invariant. | ||
| - Hardware-tier-aware **default** routing: CPU-only → llama.cpp; capable GPU/NPU → existing ONNX/TRT-RTX path; explicit alias always wins. | ||
| - Minimal new maintenance surface: one sidecar binary per platform, curated GGUF manifest — not a general "bring any model" feature. | ||
| - Measured quality: quantized GGUF output must pass `QwenRefinementOutputGuard` checks at parity with the current INT4 export on a fixed prompt suite. |
|
|
||
| In-process C API binding was considered and rejected for v1: it would require shipping/`dlopen`-ing platform binaries into the app process and harder lifecycle control. The HTTP loopback interface is stable and sufficient. | ||
|
|
||
| **Model manifest.** Extend the existing model catalog/readiness flow (`EnsureTextRefinementModelAvailableAsync` in the stage coordinator) with GGUF entries: alias, GGUF URL + size + SHA-256, recommended quant per hardware tier, prompt-template metadata. A curated set of one model family (same Qwen family as the current engine, for output-guard comparability) in Q4_K_M and Q5_K_M is enough for v1. |
| | Tier | Decision | | ||
| |---|---| | ||
| | Capable GPU (≥ threshold VRAM, TRT-RTX/CUDA available) | ONNX/TRT-RTX default (unchanged) | | ||
| | NPU supported (QNN/OpenVINO/VitisAI recipe present) | ONNX default (unchanged) | |
|
|
||
| ## 4. Execution plan | ||
|
|
||
| 1. **Spike (this spec's acceptance slice):** `LlamaCppTextRefinementEngine` skeleton + sidecar process manager (start/health/stop, loopback-only port binding) + one manifest entry; route by hardcoded hardware-tier probe behind a feature flag. |
tonythethompson
left a comment
There was a problem hiding this comment.
Docs-only spec, so no build concerns. I checked its claims against the code at this head. The main issue is the problem statement: the current text-refinement model is a 1.5B model that is already allowed to run on CPU, so the spec needs measured numbers before it can justify a second runtime. Details are inline, one per thread.
| | `QwenTextRefinementEngine` | ONNX Runtime GenAI; accelerated via TensorRT-RTX / NPU EPs (`tools/olive/Export-QwenTrtRtxGenAi.ps1`, VitisAI/QNN/OpenVINO recipes) | Mid-range RTX GPU or supported NPU | | ||
| | `GeminiCloudTextRefinementEngine` | Cloud API | Network + egress consent | | ||
|
|
||
| `RoutedTextRefinementEngine` (`src/Trackdub.Inference.Onnx/QwenTextRefinement/RoutedTextRefinementEngine.cs`) picks the Qwen local engine by default and the Gemini engine only on explicit alias. **There is no local option for a CPU-only or low-VRAM machine.** On that hardware, ORT GenAI CPU execution of the current 7–9B class export is slow enough that users silently fall back to cloud — or abandon local refinement entirely. GPU/NPU-less users are an explicit target segment, so this is a coverage gap, not an edge case. |
There was a problem hiding this comment.
The premise doesn't match the code. The shipped text-refinement model is tonythethompson/Qwen2.5-1.5B-Instruct (in bundled-models.manifest.json, engine family qwen-instruct), not a 7–9B class export. CPU is also already an allowed provider for RuntimeStage.TextRefinement: StageRuntimeRequirements.cs (around lines 177–191) uses DefaultOnnxStageAllowedProviders, which ends with ExecutionProviderKind.Cpu. The manifest's Olive block also lists cpu and int4. So a local CPU option exists today, and the claim that there is no local option is not accurate.
That changes what the spec has to prove. Before adding a second native runtime, please add measured ORT GenAI CPU numbers for the shipped 1.5B model (and an int4 CPU variant) on a target laptop: time to first token, tokens per second, and wall time per segment batch. Show that it's actually too slow. The low-VRAM partial-offload case gets weaker too, because a ~1.5B model fits in 4 GB without -ngl tricks.
|
|
||
| | Engine | Path | Hardware floor | | ||
| |---|---|---| | ||
| | `QwenTextRefinementEngine` | ONNX Runtime GenAI; accelerated via TensorRT-RTX / NPU EPs (`tools/olive/Export-QwenTrtRtxGenAi.ps1`, VitisAI/QNN/OpenVINO recipes) | Mid-range RTX GPU or supported NPU | |
There was a problem hiding this comment.
TRT-RTX isn't on the text-refinement path. StageRuntimeRequirements.cs deliberately strips TensorRT and TRT-RTX from qwen-instruct with WithoutTensorRtFamilies(...), because ORT GenAI on NvTensorRtRtx crashes the process with a native stack overflow. On NVIDIA hardware, Qwen refinement plans to CUDA or DirectML, not TRT-RTX. The same mistake shows up in the tier table (line 62, "ONNX/TRT-RTX default") and in the non-goals. Please fix the engine row and the tier table so reviewers are comparing llama.cpp against the routing that actually ships.
|
|
||
| **Model manifest.** Extend the existing model catalog/readiness flow (`EnsureTextRefinementModelAvailableAsync` in the stage coordinator) with GGUF entries: alias, GGUF URL + size + SHA-256, recommended quant per hardware tier, prompt-template metadata. A curated set of one model family (same Qwen family as the current engine, for output-guard comparability) in Q4_K_M and Q5_K_M is enough for v1. | ||
|
|
||
| **Hardware tier detection** reuses existing capability probing (`Trackdub.Inference.Onnx/Runtime` EP probes, `DnnlEngineCacheProbe`-style pattern) to classify: |
There was a problem hiding this comment.
There's no DnnlEngineCacheProbe in the repo (the closest are EngineCacheProbe and Dnnl/DnnlReadinessProbe). Here's a pointer to the hardware-profiling types that do exist:
| **Hardware tier detection** reuses existing capability probing (`Trackdub.Inference.Onnx/Runtime` EP probes, `DnnlEngineCacheProbe`-style pattern) to classify: | |
| **Hardware tier detection** reuses existing capability probing (`Trackdub.Inference.Onnx/Runtime` device enumerators and `WindowsVramMonitor`, `Runtime/Planning/MachineHardwareProfileProvider`, and the cached EP readiness probes in `Runtime/Planning/CachingReadinessProbes.cs`) to classify: |
| ## 4. Execution plan | ||
|
|
||
| 1. **Spike (this spec's acceptance slice):** `LlamaCppTextRefinementEngine` skeleton + sidecar process manager (start/health/stop, loopback-only port binding) + one manifest entry; route by hardcoded hardware-tier probe behind a feature flag. | ||
| 2. **Routing:** extend `RoutedTextRefinementEngine` tier-aware default selection; keep alias override semantics exactly as today. |
There was a problem hiding this comment.
Tier selection belongs in the runtime planner, not RoutedTextRefinementEngine. Today the router only matches by alias and engine family. The hardware and EP decision happens in IStageRuntimePlanner, which QwenTextRefinementEngine calls through StageRuntimePlanningRequestFactory.ApplyPreferredModelTierAsync, then PlanAsync, then EnsurePlanReady. If the router adds its own hardware-tier probe, there are two sources of truth. The readiness report and the plan would say qwen-instruct on CPU or DML while the router quietly runs llama.cpp, and LastExecutionSummary and the G5 readiness panel would disagree with what actually ran.
Please make llama.cpp visible to the planner for RuntimeStage.TextRefinement, for example as its own engine family or runtime in the manifest and requirements catalog. Then one plan drives selection, readiness, and the execution summary, and the router stays alias-only.
| - A third `ITextRefinementEngine` (`LlamaCppTextRefinementEngine`) registered behind `RoutedTextRefinementEngine`, real readiness semantics per the *never-fake-readiness* invariant. | ||
| - Hardware-tier-aware **default** routing: CPU-only → llama.cpp; capable GPU/NPU → existing ONNX/TRT-RTX path; explicit alias always wins. | ||
| - Minimal new maintenance surface: one sidecar binary per platform, curated GGUF manifest — not a general "bring any model" feature. | ||
| - Measured quality: quantized GGUF output must pass `QwenRefinementOutputGuard` checks at parity with the current INT4 export on a fixed prompt suite. |
There was a problem hiding this comment.
The quality baseline names a variant that doesn't ship. The manifest's default qwen-instruct variant is the GenAI bundle produced by tools/olive/Export-QwenTrtRtxGenAi.ps1, and that script exports -p fp16. int4 is only a supported Olive precision, not the shipped default. Please name the exact baseline (manifest revision f56ee652… plus variant alias). Otherwise the parity check in step 3 compares against an artifact users aren't running.
|
|
||
| 1. **Spike (this spec's acceptance slice):** `LlamaCppTextRefinementEngine` skeleton + sidecar process manager (start/health/stop, loopback-only port binding) + one manifest entry; route by hardcoded hardware-tier probe behind a feature flag. | ||
| 2. **Routing:** extend `RoutedTextRefinementEngine` tier-aware default selection; keep alias override semantics exactly as today. | ||
| 3. **Quality gate:** fixed refinement prompt suite through `QwenRefinementOutputGuard` at Q4_K_M and Q5_K_M vs current INT4 export; adopt Q4_K_M only at parity, else default to Q5_K_M on CPU. |
There was a problem hiding this comment.
QwenRefinementOutputGuard parity can't show quality parity. The guard is a per-segment reject filter: explanation text, format, multi-segment leakage, length, edit distance, and name/number checks, each falling back to the original text. It also marks unchanged output as not accepted. Two models can match on acceptance rate while one makes worse edits, and a model that is lazier about editing can look "safer." Please keep the guard as a floor (no higher rejection rate than baseline), and add an actual quality signal for the adopt-Q4_K_M decision: for example, WER or CER against reference transcripts on the fixed suite, or a blind A/B on the accepted outputs.
…routing Co-authored-by: tonythethompson <tonythethompson@users.noreply.github.com>
9d83de5 to
667e6f5
Compare
OpenVINO EP vs CPU EP smoke results (Intel CPU sandbox)Environment: same constrained-CPU sandbox as the TTS benchmark — Intel Xeon 6985P-C @ 2.30 GHz, no GPU/NPU. Stack: 1. EP discovery & session creation
2. whisper-base encoder (ASR-class, 80×3000 features ≈ 30 s audio), warm, best-of-5
3. Kokoro-82M (all variants: fp32, fp16, q8f16) — OpenVINO EP cannot load the graphAll three exports fail at session creation: The Kokoro graph's STFT output has dynamic rank, which the OpenVINO CPU plugin rejects outright (ORT CPU EP runs the same file fine). So for the current Kokoro TTS artifact, the OpenVINO EP is not a drop-in alternative without a graph fix (pin the STFT output rank/shape in the export). 4. Read-through to the CPU-tier spec (#385)
Raw results JSON and harness available in the benchmark working notes; can be adapted into |

Summary
bundled-models.manifest.json), CPU is an allowed provider (StageRuntimeRequirements.cs), and the router selects Qwen by default when registered; CPU users already get local refinement, so the premise is now "measure, don't assume"QwenRefinementOutputGuardis treated as a safety filter, not a semantic quality measureStageRuntimeRequirements/RuntimePlanner), not a router bypass; per-artifact license verification for community GGUFs per manifest schemaVerification