interp-engine 1.6.0__tar.gz → 1.7.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {interp_engine-1.6.0 → interp_engine-1.7.1}/PKG-INFO +2 -1
- {interp_engine-1.6.0 → interp_engine-1.7.1}/README.md +1 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/interp_engine/facts.py +4 -1
- {interp_engine-1.6.0 → interp_engine-1.7.1}/interp_engine/vllm_backend.py +35 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/interp_engine/vllm_capture/static.py +48 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/pyproject.toml +1 -1
- {interp_engine-1.6.0 → interp_engine-1.7.1}/tests/synthetic_families.py +38 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/tests/test_mlp_internals.py +7 -5
- {interp_engine-1.6.0 → interp_engine-1.7.1}/tests/test_static_set.py +105 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/.gitignore +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/LICENSE +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/benchmarks/README.md +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/benchmarks/__init__.py +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/benchmarks/bench_spec.py +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/benchmarks/cells.py +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/benchmarks/probe.py +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/benchmarks/probe_lens_stream.py +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/benchmarks/publish.py +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/benchmarks/report_bench.py +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/benchmarks/results/deepseek-v4-flash-0731__eager.json +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/benchmarks/results/deepseek-v4-flash-0731__vllm-cudagraph.json +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/benchmarks/results/deepseek-v4-flash-0731__vllm-static.json +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/benchmarks/results/deepseek-v4-flash-0731__vllm.json +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/benchmarks/results/gemma-2-2b__eager.json +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/benchmarks/results/gemma-2-2b__vllm-cudagraph.json +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/benchmarks/results/gemma-2-2b__vllm-static.json +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/benchmarks/results/gemma-2-2b__vllm.json +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/benchmarks/results/llama-3.1-8b__eager.json +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/benchmarks/results/llama-3.1-8b__vllm-cudagraph.json +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/benchmarks/results/llama-3.1-8b__vllm-static.json +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/benchmarks/results/llama-3.1-8b__vllm.json +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/benchmarks/results/qwen3-4b__eager.json +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/benchmarks/results/qwen3-4b__vllm-cudagraph.json +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/benchmarks/results/qwen3-4b__vllm-static.json +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/benchmarks/results/qwen3-4b__vllm.json +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/benchmarks/results/qwen3.8-27b__eager.json +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/benchmarks/results/qwen3.8-27b__vllm-cudagraph.json +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/benchmarks/results/qwen3.8-27b__vllm-static.json +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/benchmarks/results/qwen3.8-27b__vllm.json +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/benchmarks/results-latest.md +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/benchmarks/run_all.sh +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/benchmarks/run_bench.py +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/benchmarks/workloads.py +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/docs/AGENT_INTEGRATION.md +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/docs/ARCHITECTURE_QUIRKS.md +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/docs/COMPATIBILITY.md +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/docs/ENGINE_HOOK_MAPPINGS.md +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/docs/GRADIENTS.md +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/docs/INTERNALS.md +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/docs/PERFORMANCE.md +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/docs/PORTING.md +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/docs/README.md +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/docs/SUPPORTED_POINTS.md +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/docs/USAGE.md +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/interp_engine/__init__.py +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/interp_engine/_loop.py +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/interp_engine/address.py +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/interp_engine/arch.py +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/interp_engine/attn_config.py +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/interp_engine/attn_scores.py +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/interp_engine/autograd_support.py +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/interp_engine/capture.py +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/interp_engine/chat_compose.py +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/interp_engine/chat_conventions.py +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/interp_engine/chat_formatters.py +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/interp_engine/cuda_preflight.py +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/interp_engine/dispatch.py +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/interp_engine/hooks.py +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/interp_engine/lens.py +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/interp_engine/load.py +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/interp_engine/mappers.py +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/interp_engine/memory.py +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/interp_engine/model.py +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/interp_engine/moe_routing.py +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/interp_engine/notebook_stdout.py +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/interp_engine/points.py +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/interp_engine/protocol.py +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/interp_engine/residual_basis.py +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/interp_engine/select.py +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/interp_engine/steer.py +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/interp_engine/steer_specs.py +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/interp_engine/sync.py +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/interp_engine/tokenize.py +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/interp_engine/vllm_capture/__init__.py +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/interp_engine/vllm_capture/_demux.py +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/interp_engine/vllm_capture/_hooks.py +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/interp_engine/vllm_capture/_payload.py +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/interp_engine/vllm_capture/_tree.py +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/interp_engine/vllm_capture/attn.py +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/interp_engine/vllm_capture/capture.py +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/interp_engine/vllm_capture/graphs.py +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/interp_engine/vllm_capture/lens/__init__.py +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/interp_engine/vllm_capture/lens/intervene.py +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/interp_engine/vllm_capture/lens/readout.py +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/interp_engine/vllm_capture/lens/unembed.py +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/interp_engine/vllm_capture/mhc.py +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/interp_engine/vllm_capture/native.py +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/interp_engine/vllm_capture/requests.py +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/interp_engine/vllm_capture/steering.py +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/interp_engine/vllm_plugin.py +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/tests/conftest.py +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/tests/harness.py +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/tests/model_expectations.yaml +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/tests/test_address.py +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/tests/test_attn_config_tripwire.py +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/tests/test_attn_probs_indexing.py +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/tests/test_attn_scores.py +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/tests/test_attn_z_gqa.py +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/tests/test_autograd_support.py +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/tests/test_bench_workloads.py +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/tests/test_capability_refusals.py +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/tests/test_capture_addressing.py +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/tests/test_chat_compose.py +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/tests/test_chat_formatters.py +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/tests/test_chat_templates.py +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/tests/test_core.py +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/tests/test_cuda_preflight.py +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/tests/test_doc_code_fences.py +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/tests/test_eager_autograd.py +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/tests/test_facts.py +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/tests/test_family_points.py +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/tests/test_gated_attn_out.py +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/tests/test_gpu_sizer.py +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/tests/test_head_contributions.py +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/tests/test_hook_call_conventions.py +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/tests/test_layer_kinds.py +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/tests/test_load.py +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/tests/test_logit_transform.py +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/tests/test_mappers.py +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/tests/test_memory.py +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/tests/test_model_expectations.py +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/tests/test_moe.py +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/tests/test_multimodal_arch.py +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/tests/test_new_models_gpu.py +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/tests/test_no_chat_template.py +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/tests/test_normalized_hook.py +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/tests/test_notebook_stdout.py +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/tests/test_packaging.py +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/tests/test_parity_gpt2.py +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/tests/test_per_layer_attn_dims.py +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/tests/test_points_registry.py +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/tests/test_protocol.py +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/tests/test_published_benchmarks.py +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/tests/test_qk_norm.py +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/tests/test_qkv_layout.py +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/tests/test_reasoning_spans.py +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/tests/test_release.py +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/tests/test_resid_mid.py +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/tests/test_residual_basis.py +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/tests/test_sandwich_norms.py +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/tests/test_select.py +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/tests/test_sliding_window_attn.py +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/tests/test_small_models_gpu.py +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/tests/test_static_dsv4_gpu.py +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/tests/test_static_parity_gpu.py +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/tests/test_static_warmup.py +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/tests/test_steer_context.py +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/tests/test_steer_math_parity.py +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/tests/test_sync_loop.py +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/tests/test_sync_parity.py +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/tests/test_unified_free_functions.py +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/tests/test_unresolved_families.py +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/tests/test_vllm_capture_gpu.py +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/tests/test_vllm_capture_scales.py +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/tests/test_vllm_engine_loop.py +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/tests/test_vllm_graph_path.py +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/tests/test_vllm_graphs_on_gpu.py +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/tests/test_vllm_hook_availability.py +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/tests/test_vllm_hyper_connections.py +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/tests/test_vllm_kv_isolation.py +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/tests/test_vllm_new_points.py +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/tests/test_vllm_only_families.py +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/tests/test_vllm_plugin.py +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/tests/test_vllm_wire_grammar.py +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/tests/test_vocabulary_boundary.py +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/tests/test_worker_lens_capture_readout.py +0 -0
- {interp_engine-1.6.0 → interp_engine-1.7.1}/tests/test_worker_lens_readout.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: interp-engine
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.7.1
|
|
4
4
|
Summary: A fast, standardized, and easy to use interpretability engine.
|
|
5
5
|
Project-URL: Homepage, https://github.com/decoderesearch/interp-engine
|
|
6
6
|
Project-URL: Repository, https://github.com/decoderesearch/interp-engine
|
|
@@ -162,6 +162,7 @@ Never OOM again - `interp-engine` includes gpu-sizer, an intuitive UI which tell
|
|
|
162
162
|
- **DeepSeek-V2 on transformers older than 5.15.0** captures a wrong attention temperature — the engine warns at load, and upgrading is the fix ([COMPATIBILITY.md](docs/COMPATIBILITY.md)).
|
|
163
163
|
- **DeepSeek-V4 on the eager backend needs two flags outside Hopper.** Its FP8 paths reach for `kernels-community/deep-gemm`, which declares a `9.0a` build, and with `kernels` >= 0.16.1 the arch refusal escapes transformers' Triton fallback and kills the first forward. Load with `TRANSFORMERS_DISABLE_DEEPGEMM_LINEAR=1` and `experts_implementation="grouped_mm"`, which transformers [already recommends on B200](https://github.com/huggingface/transformers/blob/93c8b7b485963a10800c91f55304db6be211c2bd/src/transformers/integrations/finegrained_fp8.py#L252-L254) for an unrelated DeepGEMM accuracy problem. The engine raises `HubKernelUnsupported` naming both; vLLM is unaffected.
|
|
164
164
|
- **MXFP4 checkpoints (gpt-oss) need `interp-engine[quant]`**, which the `[vllm]` extra does not include; without it transformers dequantizes them to bf16 at roughly 3x the weights, which can turn a model that fits into one that does not.
|
|
165
|
+
- **`gemma-4-26B-A4B-it` on the static vLLM backend needs `VLLM_BATCH_INVARIANT=1` on B200/B300.** On compute capability 10.x, vLLM's own greedy output moves by up to 6.8 nats under the CUDA-graph replay a static set requires, so the taps would report a forward pass vLLM itself gets wrong ([vllm#55238](https://github.com/vllm-project/vllm/issues/55238)). The engine refuses this combination at load and names the flag; capability 9.x and 12.x are bit-identical and unaffected.
|
|
165
166
|
|
|
166
167
|
Per-architecture structural quirks — which points a family serves and why — are not caveats but facts about the architecture, and live in [ARCHITECTURE_QUIRKS.md](docs/ARCHITECTURE_QUIRKS.md).
|
|
167
168
|
|
|
@@ -131,6 +131,7 @@ Never OOM again - `interp-engine` includes gpu-sizer, an intuitive UI which tell
|
|
|
131
131
|
- **DeepSeek-V2 on transformers older than 5.15.0** captures a wrong attention temperature — the engine warns at load, and upgrading is the fix ([COMPATIBILITY.md](docs/COMPATIBILITY.md)).
|
|
132
132
|
- **DeepSeek-V4 on the eager backend needs two flags outside Hopper.** Its FP8 paths reach for `kernels-community/deep-gemm`, which declares a `9.0a` build, and with `kernels` >= 0.16.1 the arch refusal escapes transformers' Triton fallback and kills the first forward. Load with `TRANSFORMERS_DISABLE_DEEPGEMM_LINEAR=1` and `experts_implementation="grouped_mm"`, which transformers [already recommends on B200](https://github.com/huggingface/transformers/blob/93c8b7b485963a10800c91f55304db6be211c2bd/src/transformers/integrations/finegrained_fp8.py#L252-L254) for an unrelated DeepGEMM accuracy problem. The engine raises `HubKernelUnsupported` naming both; vLLM is unaffected.
|
|
133
133
|
- **MXFP4 checkpoints (gpt-oss) need `interp-engine[quant]`**, which the `[vllm]` extra does not include; without it transformers dequantizes them to bf16 at roughly 3x the weights, which can turn a model that fits into one that does not.
|
|
134
|
+
- **`gemma-4-26B-A4B-it` on the static vLLM backend needs `VLLM_BATCH_INVARIANT=1` on B200/B300.** On compute capability 10.x, vLLM's own greedy output moves by up to 6.8 nats under the CUDA-graph replay a static set requires, so the taps would report a forward pass vLLM itself gets wrong ([vllm#55238](https://github.com/vllm-project/vllm/issues/55238)). The engine refuses this combination at load and names the flag; capability 9.x and 12.x are bit-identical and unaffected.
|
|
134
135
|
|
|
135
136
|
Per-architecture structural quirks — which points a family serves and why — are not caveats but facts about the architecture, and live in [ARCHITECTURE_QUIRKS.md](docs/ARCHITECTURE_QUIRKS.md).
|
|
136
137
|
|
|
@@ -478,6 +478,7 @@ class GateUpLayout(StrEnum):
|
|
|
478
478
|
|
|
479
479
|
#: ``[all_gate | all_up]``, two contiguous halves, gate first. Phi-3's ``gate_up_proj``, whose
|
|
480
480
|
#: forward is ``gate, up = self.gate_up_proj(x).chunk(2, dim=-1); down(up * act(gate))``.
|
|
481
|
+
#: GLM-4's ``Glm4MLP`` performs the identical chunk.
|
|
481
482
|
GATE_FIRST = "gate_first"
|
|
482
483
|
#: ``[gate_0 up_0 | gate_1 up_1 | ...]``, the two branches interleaved per neuron. gpt-oss's
|
|
483
484
|
#: MXFP4 expert weights, where the kernel reads ``[..., ::2]`` and ``[..., 1::2]``.
|
|
@@ -488,8 +489,10 @@ class GateUpLayout(StrEnum):
|
|
|
488
489
|
#: packed. Absent means the refusal in ``ArchSpec.mlp_projection`` stands: a family may fuse and pack
|
|
489
490
|
#: either way, and "probably gate first" is exactly the guess that produces silent garbage.
|
|
490
491
|
#: Verified by the identity the branches exist to satisfy -- ``act(mlp_pre) * mlp_pre_linear`` is the
|
|
491
|
-
#: down projection's input, which is captured independently as ``mlp_act``
|
|
492
|
+
#: down projection's input, which is captured independently as ``mlp_act``
|
|
493
|
+
#: (tests/test_mlp_internals.py).
|
|
492
494
|
FUSED_GATE_UP_LAYOUTS: dict[str, GateUpLayout] = {
|
|
495
|
+
"Glm4ForCausalLM": GateUpLayout.GATE_FIRST,
|
|
493
496
|
"Phi3ForCausalLM": GateUpLayout.GATE_FIRST,
|
|
494
497
|
}
|
|
495
498
|
|
|
@@ -60,6 +60,7 @@ from interp_engine.vllm_capture.static import (
|
|
|
60
60
|
kv_cache_width,
|
|
61
61
|
resid_stream_aliases,
|
|
62
62
|
resolve_static_points,
|
|
63
|
+
sm100_cudagraph_refusal_reason,
|
|
63
64
|
static_read_width,
|
|
64
65
|
static_unsupported_reason,
|
|
65
66
|
)
|
|
@@ -67,6 +68,22 @@ from interp_engine.vllm_plugin import WORKER_EXTENSION_CLS
|
|
|
67
68
|
|
|
68
69
|
logger = logging.getLogger(__name__)
|
|
69
70
|
|
|
71
|
+
|
|
72
|
+
def _device_capability() -> tuple[int, int] | None:
|
|
73
|
+
"""This GPU's compute capability, or None when there is no CUDA device to ask.
|
|
74
|
+
|
|
75
|
+
None rather than a default, because callers gate refusals on it: a guess would either refuse a
|
|
76
|
+
machine nobody measured or wave through the one that is known wrong.
|
|
77
|
+
"""
|
|
78
|
+
try:
|
|
79
|
+
if not torch.cuda.is_available():
|
|
80
|
+
return None
|
|
81
|
+
major, minor = torch.cuda.get_device_capability(0)
|
|
82
|
+
return int(major), int(minor)
|
|
83
|
+
except Exception: # noqa: BLE001 - no driver, or a torch without the call
|
|
84
|
+
return None
|
|
85
|
+
|
|
86
|
+
|
|
70
87
|
# Residual-width sites the warmup sentinel can prove. ``mlp_act`` / ``z`` need a different
|
|
71
88
|
# vector length than ``d_model``, which this process does not know until a worker wrap exists.
|
|
72
89
|
_STATIC_SENTINEL_WRITE_POINTS = frozenset(
|
|
@@ -1099,6 +1116,7 @@ class VLLMModel:
|
|
|
1099
1116
|
# Same condition as `apply_breakable_env`: only a non-empty static set turns torch.compile
|
|
1100
1117
|
# off, and it is that combination a linear-attention trunk cannot survive.
|
|
1101
1118
|
if reads or writes:
|
|
1119
|
+
self._refuse_static_where_vllm_reads_wrong()
|
|
1102
1120
|
self._pin_decode_only_graphs_on_hybrid_trunk()
|
|
1103
1121
|
# Reads only. A write allocates a `[1, width]` delta (see `static._alloc_site`), so it does
|
|
1104
1122
|
# not scale with `max_num_batched_tokens` and has no business in a budget whose whole job is
|
|
@@ -1144,6 +1162,23 @@ class VLLMModel:
|
|
|
1144
1162
|
logger.warning("lowering max_num_batched_tokens %s -> %s so static buffers fit", max_n, fitted)
|
|
1145
1163
|
self._engine_kwargs["max_num_batched_tokens"] = fitted
|
|
1146
1164
|
|
|
1165
|
+
def _refuse_static_where_vllm_reads_wrong(self) -> None:
|
|
1166
|
+
"""Refuse a static set that would report a forward pass vLLM itself gets wrong.
|
|
1167
|
+
|
|
1168
|
+
Raised here rather than after the engine exists, because it is decided by the checkpoint and
|
|
1169
|
+
the device: building 26B of weights first only delays the same answer. See
|
|
1170
|
+
:func:`~interp_engine.vllm_capture.static.sm100_cudagraph_refusal_reason`.
|
|
1171
|
+
"""
|
|
1172
|
+
compilation = self._engine_kwargs.get("compilation_config")
|
|
1173
|
+
reason = sm100_cudagraph_refusal_reason(
|
|
1174
|
+
getattr(self, "hf_model_id", None),
|
|
1175
|
+
_device_capability(),
|
|
1176
|
+
cudagraph_mode=str(compilation.get("cudagraph_mode") or "") if isinstance(compilation, dict) else "",
|
|
1177
|
+
batch_invariant=os.environ.get("VLLM_BATCH_INVARIANT") == "1",
|
|
1178
|
+
)
|
|
1179
|
+
if reason:
|
|
1180
|
+
raise ValueError(reason)
|
|
1181
|
+
|
|
1147
1182
|
def _pin_decode_only_graphs_on_hybrid_trunk(self) -> None:
|
|
1148
1183
|
"""Capture graphs for decode only when the trunk is linear attention.
|
|
1149
1184
|
|
|
@@ -142,6 +142,54 @@ def decode_only_graphs_reason(layer_types: Sequence[str] | None, n_layers: int)
|
|
|
142
142
|
)
|
|
143
143
|
|
|
144
144
|
|
|
145
|
+
#: The upstream issue behind :func:`sm100_cudagraph_refusal_reason`.
|
|
146
|
+
SM100_CUDAGRAPH_ISSUE = "https://github.com/vllm-project/vllm/issues/55238"
|
|
147
|
+
|
|
148
|
+
#: The one setting measured to make that capture correct again.
|
|
149
|
+
SM100_CUDAGRAPH_WORKAROUND = "VLLM_BATCH_INVARIANT=1"
|
|
150
|
+
|
|
151
|
+
#: Checkpoints measured wrong under a static set on compute capability 10.x. Exact ids rather than a
|
|
152
|
+
#: glob over MoE Gemma 4: this is the one that was measured, and refusing a checkpoint nobody has
|
|
153
|
+
#: tested trades a silent wrong answer for a wrong refusal.
|
|
154
|
+
SM100_CUDAGRAPH_MODELS = ("google/gemma-4-26B-A4B-it",)
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
def sm100_cudagraph_refusal_reason(
|
|
158
|
+
hf_id: str | None,
|
|
159
|
+
capability: tuple[int, int] | None,
|
|
160
|
+
*,
|
|
161
|
+
cudagraph_mode: str = "",
|
|
162
|
+
batch_invariant: bool = False,
|
|
163
|
+
) -> str | None:
|
|
164
|
+
"""Why this checkpoint cannot be captured under a static set on this GPU, or None.
|
|
165
|
+
|
|
166
|
+
A static set needs graph replay with torch.compile off, and on compute capability 10.x (B200,
|
|
167
|
+
B300) vLLM's own greedy output moves under exactly that: up to 6.8 nats of logprob and a different
|
|
168
|
+
token, deterministic, and bit-identical on 9.x and 12.x. It follows CUDA-graph padding rather than
|
|
169
|
+
the prompt, so no choice of prompt avoids it, and the taps sit downstream -- they would report a
|
|
170
|
+
forward pass vLLM itself gets wrong, at every point of the affected layers.
|
|
171
|
+
|
|
172
|
+
Refused rather than worked around quietly. ``VLLM_BATCH_INVARIANT=1`` fixes it but changes kernel
|
|
173
|
+
selection for the whole run, which is a decision about what the captured numbers mean, and that
|
|
174
|
+
belongs to the caller. See :data:`SM100_CUDAGRAPH_ISSUE` and validator/comparison/engine_bugs.py.
|
|
175
|
+
"""
|
|
176
|
+
if not hf_id or hf_id not in SM100_CUDAGRAPH_MODELS:
|
|
177
|
+
return None
|
|
178
|
+
if capability is None or capability[0] != 10:
|
|
179
|
+
return None
|
|
180
|
+
# Either of these removes the defect: the flag was measured bit-identical at every prompt length,
|
|
181
|
+
# and without replay there is no padding to be wrong about.
|
|
182
|
+
if batch_invariant or str(cudagraph_mode).upper() == "NONE":
|
|
183
|
+
return None
|
|
184
|
+
return (
|
|
185
|
+
f"{hf_id} on compute capability {capability[0]}.{capability[1]}: vLLM's own output moves under "
|
|
186
|
+
"CUDA-graph replay with torch.compile off, which is what a static set requires -- up to 6.8 nats "
|
|
187
|
+
"of logprob and a different greedy token -- so these taps would read a forward pass vLLM itself "
|
|
188
|
+
f"gets wrong ({SM100_CUDAGRAPH_ISSUE}). Set {SM100_CUDAGRAPH_WORKAROUND} to capture anyway, or "
|
|
189
|
+
"use the hooked `vllm` backend, or capture on capability 9.x/12.x."
|
|
190
|
+
)
|
|
191
|
+
|
|
192
|
+
|
|
145
193
|
def multi_stream_refusal_reason(name: str, n_streams: int) -> str | None:
|
|
146
194
|
"""Why ``name`` cannot be declared as a static tap on a trunk carrying ``n_streams`` residual streams, or None.
|
|
147
195
|
|
|
@@ -437,6 +437,44 @@ def shrunk_phi3(*, seed: int = 0) -> Any:
|
|
|
437
437
|
return EagerModel("Phi3ForCausalLM", hf_model=model, tokenizer=NoTokenizer(), device=None, dtype="float32")
|
|
438
438
|
|
|
439
439
|
|
|
440
|
+
#: Dims for :func:`shrunk_glm4`. Same subject as :data:`_PHI3_SHRUNK` -- the MLP's single
|
|
441
|
+
#: ``gate_up_proj`` -- so the only constraint is an even ``intermediate_size``. GLM-4 rotates half
|
|
442
|
+
#: its head dimension, so ``head_dim`` stays a multiple of four.
|
|
443
|
+
_GLM4_SHRUNK: dict[str, Any] = {
|
|
444
|
+
"hidden_size": 32,
|
|
445
|
+
"intermediate_size": 64,
|
|
446
|
+
"num_hidden_layers": 2,
|
|
447
|
+
"num_attention_heads": 4,
|
|
448
|
+
"num_key_value_heads": 2,
|
|
449
|
+
"head_dim": 8,
|
|
450
|
+
"vocab_size": 128,
|
|
451
|
+
"max_position_embeddings": 64,
|
|
452
|
+
"pad_token_id": 0,
|
|
453
|
+
"eos_token_id": 1,
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
|
|
457
|
+
def shrunk_glm4(*, seed: int = 0) -> Any:
|
|
458
|
+
"""A tiny real GLM-4 with float32 weights on CPU, as an ``EagerModel``.
|
|
459
|
+
|
|
460
|
+
The second family to fuse its two pre-activation projections into one ``gate_up_proj``, and the
|
|
461
|
+
reason the packing is a table rather than a default: ``Glm4MLP`` chunks gate-first exactly as
|
|
462
|
+
Phi-3 does, but that is a fact about the family, established by running it, not one the shared
|
|
463
|
+
attribute name implies.
|
|
464
|
+
"""
|
|
465
|
+
from transformers import Glm4Config, Glm4ForCausalLM
|
|
466
|
+
|
|
467
|
+
from interp_engine import EagerModel
|
|
468
|
+
|
|
469
|
+
config = Glm4Config(**_GLM4_SHRUNK)
|
|
470
|
+
config.architectures = ["Glm4ForCausalLM"]
|
|
471
|
+
torch.manual_seed(seed)
|
|
472
|
+
with warnings.catch_warnings():
|
|
473
|
+
warnings.simplefilter("ignore")
|
|
474
|
+
model = Glm4ForCausalLM(config).to(torch.float32).eval()
|
|
475
|
+
return EagerModel("Glm4ForCausalLM", hf_model=model, tokenizer=NoTokenizer(), device=None, dtype="float32")
|
|
476
|
+
|
|
477
|
+
|
|
440
478
|
def shrunk_lfm2_moe(*, seed: int = 0) -> Any:
|
|
441
479
|
"""A tiny LFM2-MoE with real float32 weights on CPU, as an ``EagerModel``.
|
|
442
480
|
|
|
@@ -29,7 +29,7 @@ from types import SimpleNamespace
|
|
|
29
29
|
import pytest
|
|
30
30
|
import torch
|
|
31
31
|
from harness import GEMMA_IT, GPT2, QWEN_THINKING, ModelSpec, load_model, require_hf_token
|
|
32
|
-
from synthetic_families import shrunk_opt, shrunk_phi3
|
|
32
|
+
from synthetic_families import shrunk_glm4, shrunk_opt, shrunk_phi3
|
|
33
33
|
|
|
34
34
|
from interp_engine import run_with_cache
|
|
35
35
|
from interp_engine.capture import AddressLike
|
|
@@ -176,8 +176,9 @@ def test_a_plain_mlp_refuses_the_multiplied_branch():
|
|
|
176
176
|
model.resolve_point("mlp_pre_linear", 0)
|
|
177
177
|
|
|
178
178
|
|
|
179
|
-
|
|
180
|
-
|
|
179
|
+
@pytest.mark.parametrize("build", [shrunk_phi3, shrunk_glm4], ids=["phi3", "glm4"])
|
|
180
|
+
def test_a_fused_gate_up_is_sliced_into_its_two_branches(build):
|
|
181
|
+
"""Phi-3's and GLM-4's shape: one projection holding both branches, so neither is a module output.
|
|
181
182
|
|
|
182
183
|
Served rather than refused, because a dense MLP's neuron basis exists whether or not the
|
|
183
184
|
checkpoint stores the two matrices concatenated -- and this is a read plus a last-axis slice, the
|
|
@@ -186,9 +187,10 @@ def test_a_fused_gate_up_is_sliced_into_its_two_branches():
|
|
|
186
187
|
Checked by the identity the branches exist to satisfy: `act(mlp_pre) * mlp_pre_linear` is the
|
|
187
188
|
down projection's input, which arrives independently as `mlp_act`. That is what says the halves
|
|
188
189
|
were cut the right way round, and it is exact -- swapping them changes the answer on any
|
|
189
|
-
non-symmetric activation, which SiLU is.
|
|
190
|
+
non-symmetric activation, which SiLU is. Run per family rather than once, because the packing is
|
|
191
|
+
a property of the family: both of these chunk gate-first, and nothing but running them says so.
|
|
190
192
|
"""
|
|
191
|
-
model =
|
|
193
|
+
model = build()
|
|
192
194
|
ids = torch.arange(7).unsqueeze(0) % 128
|
|
193
195
|
cache = run_with_cache(model, ids, [("mlp_pre", 1), ("mlp_pre_linear", 1), ("mlp_act", 1)])
|
|
194
196
|
pre, linear, act = (cache.get(name, 1) for name in ("mlp_pre", "mlp_pre_linear", "mlp_act"))
|
|
@@ -11,6 +11,8 @@ from interp_engine.address import Address
|
|
|
11
11
|
from interp_engine.points import steer_refusal_reason
|
|
12
12
|
from interp_engine.vllm_capture.static import (
|
|
13
13
|
BREAKABLE_ENV,
|
|
14
|
+
SM100_CUDAGRAPH_ISSUE,
|
|
15
|
+
SM100_CUDAGRAPH_WORKAROUND,
|
|
14
16
|
STATIC_SKIP_ABSENT_ENV,
|
|
15
17
|
StaticState,
|
|
16
18
|
_activation_width,
|
|
@@ -41,6 +43,7 @@ from interp_engine.vllm_capture.static import (
|
|
|
41
43
|
multi_stream_refusal_reason,
|
|
42
44
|
resid_stream_aliases,
|
|
43
45
|
resolve_static_points,
|
|
46
|
+
sm100_cudagraph_refusal_reason,
|
|
44
47
|
static_buffer_bytes,
|
|
45
48
|
static_unsupported_reason,
|
|
46
49
|
steer_write_for_sae_point,
|
|
@@ -603,6 +606,38 @@ def test_unknown_layer_types_are_not_a_hybrid_claim():
|
|
|
603
606
|
assert decode_only_graphs_reason([], 12) is None
|
|
604
607
|
|
|
605
608
|
|
|
609
|
+
def test_the_checkpoint_vllm_reads_wrong_on_sm100_is_refused_rather_than_captured():
|
|
610
|
+
"""A static set on this pair would report vLLM's own wrong forward as an interp result.
|
|
611
|
+
|
|
612
|
+
The refusal names the workaround and the issue, because a caller who hits this needs to decide
|
|
613
|
+
between `VLLM_BATCH_INVARIANT=1` and different hardware, and cannot do that from "unsupported".
|
|
614
|
+
"""
|
|
615
|
+
reason = sm100_cudagraph_refusal_reason("google/gemma-4-26B-A4B-it", (10, 0))
|
|
616
|
+
assert reason is not None
|
|
617
|
+
assert SM100_CUDAGRAPH_WORKAROUND in reason
|
|
618
|
+
assert SM100_CUDAGRAPH_ISSUE in reason
|
|
619
|
+
assert sm100_cudagraph_refusal_reason("google/gemma-4-26B-A4B-it", (10, 3)) is not None
|
|
620
|
+
|
|
621
|
+
|
|
622
|
+
def test_the_sm100_refusal_lifts_for_each_setting_measured_to_remove_the_defect():
|
|
623
|
+
"""`VLLM_BATCH_INVARIANT=1` was bit-identical at every prompt length, and without replay there is
|
|
624
|
+
no padding to be wrong about. Both are the caller's call to make, so neither is refused."""
|
|
625
|
+
args = ("google/gemma-4-26B-A4B-it", (10, 0))
|
|
626
|
+
assert sm100_cudagraph_refusal_reason(*args, batch_invariant=True) is None
|
|
627
|
+
assert sm100_cudagraph_refusal_reason(*args, cudagraph_mode="NONE") is None
|
|
628
|
+
assert sm100_cudagraph_refusal_reason(*args, cudagraph_mode="none") is None
|
|
629
|
+
|
|
630
|
+
|
|
631
|
+
def test_the_sm100_refusal_does_not_reach_hardware_or_checkpoints_it_was_not_measured_on():
|
|
632
|
+
"""Capability 9.0 and 12.0 were measured bit-identical across all 60 points, and no other
|
|
633
|
+
checkpoint was measured at all -- refusing those would trade a wrong answer for a wrong refusal."""
|
|
634
|
+
assert sm100_cudagraph_refusal_reason("google/gemma-4-26B-A4B-it", (9, 0)) is None
|
|
635
|
+
assert sm100_cudagraph_refusal_reason("google/gemma-4-26B-A4B-it", (12, 0)) is None
|
|
636
|
+
assert sm100_cudagraph_refusal_reason("google/gemma-4-26B-A4B-it", None) is None
|
|
637
|
+
assert sm100_cudagraph_refusal_reason("google/gemma-4-12B-it", (10, 0)) is None
|
|
638
|
+
assert sm100_cudagraph_refusal_reason(None, (10, 0)) is None
|
|
639
|
+
|
|
640
|
+
|
|
606
641
|
def test_every_recurrent_kind_upstream_leaves_unbroken_is_pinned():
|
|
607
642
|
"""The shared recurrent layers carry no `eager_break_during_capture`, so each of these families is
|
|
608
643
|
exposed the same way Qwen3.5 was: mamba_mixer (Jamba), mamba_mixer2 (Nemotron-H, Bamba,
|
|
@@ -1239,6 +1274,76 @@ def test_capturing_attn_wrap_weak_refs_qkv_before_add_eager(monkeypatch):
|
|
|
1239
1274
|
assert torch.equal(v_site.buf[:3], v)
|
|
1240
1275
|
|
|
1241
1276
|
|
|
1277
|
+
class _ReplayableBreakableCapture(_FakeBreakableCapture):
|
|
1278
|
+
""":class:`_FakeBreakableCapture`, plus the replays a real graph runs after recording.
|
|
1279
|
+
|
|
1280
|
+
The base class stops at capture time. That is what the weak-ref tests above need -- and it is
|
|
1281
|
+
also why they cannot see this: a recorded eager segment runs again on *every* replay, reading
|
|
1282
|
+
whatever the weak-reffed addresses hold then, and :func:`_harvest` reads the site buffer after
|
|
1283
|
+
the replay rather than after the recording.
|
|
1284
|
+
"""
|
|
1285
|
+
|
|
1286
|
+
def replay(self) -> None:
|
|
1287
|
+
for fn in self.fns:
|
|
1288
|
+
fn()
|
|
1289
|
+
|
|
1290
|
+
|
|
1291
|
+
class _KwargAttn(torch.nn.Module):
|
|
1292
|
+
"""An attention module called the way vLLM calls it on a Gemma-shaped block."""
|
|
1293
|
+
|
|
1294
|
+
def forward(self, positions=None, hidden_states=None): # noqa: ANN001
|
|
1295
|
+
return hidden_states * 0.5
|
|
1296
|
+
|
|
1297
|
+
|
|
1298
|
+
def _tap_attn_in(monkeypatch) -> tuple[_ReplayableBreakableCapture, _Site, torch.Tensor]:
|
|
1299
|
+
"""An ``attn_in`` read tapped on a recording graph, with the trunk tensor it was called with."""
|
|
1300
|
+
import interp_engine.vllm_capture.static as static_mod
|
|
1301
|
+
|
|
1302
|
+
cap = _ReplayableBreakableCapture()
|
|
1303
|
+
monkeypatch.setattr(static_mod, "_breakable_capture", lambda: cap)
|
|
1304
|
+
attn = _KwargAttn()
|
|
1305
|
+
site = _Site(address=Address("attn_in", 22), buf=torch.zeros(4, 2), module=attn)
|
|
1306
|
+
_wrap_module(attn, [(site, "read")])
|
|
1307
|
+
hidden = torch.full((3, 2), 22.0)
|
|
1308
|
+
attn(positions=torch.arange(3), hidden_states=hidden)
|
|
1309
|
+
return cap, site, hidden
|
|
1310
|
+
|
|
1311
|
+
|
|
1312
|
+
def test_replay_recopies_the_same_rows_when_nothing_overwrites_the_tapped_buffer(monkeypatch):
|
|
1313
|
+
"""Replaying the segment is harmless by itself -- it recopies what it copied at capture.
|
|
1314
|
+
|
|
1315
|
+
Read with the test below: deferral alone is safe, and only a reused block moves the rows. That
|
|
1316
|
+
separates the two candidate explanations whenever a static column disagrees with a hooked one.
|
|
1317
|
+
"""
|
|
1318
|
+
cap, site, _ = _tap_attn_in(monkeypatch)
|
|
1319
|
+
assert torch.equal(site.buf[:3], torch.full((3, 2), 22.0))
|
|
1320
|
+
cap.replay()
|
|
1321
|
+
assert torch.equal(site.buf[:3], torch.full((3, 2), 22.0))
|
|
1322
|
+
|
|
1323
|
+
|
|
1324
|
+
def test_a_replayed_read_follows_the_address_so_vllms_callback_order_is_load_bearing(monkeypatch):
|
|
1325
|
+
"""The recorded read holds an address, so its correctness rests on vLLM's callback order.
|
|
1326
|
+
|
|
1327
|
+
The weak ref handed to ``add_eager`` is deliberate: a strong Python ref pins the capture-time
|
|
1328
|
+
cudagraph-pool slot, and replay then recopies the profile run into the site buffer instead of the
|
|
1329
|
+
live request (:func:`_cuda_weak_ref`). Materialising a copy at registration would instead cost a
|
|
1330
|
+
copy and its VRAM per site per step, on every model. So the tap reads an address and depends on
|
|
1331
|
+
vLLM running the callback where it was registered, ahead of anything that writes that block.
|
|
1332
|
+
|
|
1333
|
+
Overwrite the block first and the read follows the address. That is the shape to watch for if a
|
|
1334
|
+
future vLLM stops ordering eager segments this way, and this test is here to catch that.
|
|
1335
|
+
|
|
1336
|
+
It is not the explanation for ``attn_in.22`` on capability 10.x. Plain vLLM moves by 6.8 nats on
|
|
1337
|
+
that hardware with no tap in the process, and a fresh engine and a reversed run order return the
|
|
1338
|
+
same wrong numbers, which a reused block would not
|
|
1339
|
+
(validator/docs/VLLM_SM100_CUDAGRAPH.md).
|
|
1340
|
+
"""
|
|
1341
|
+
cap, site, hidden = _tap_attn_in(monkeypatch)
|
|
1342
|
+
hidden.copy_(torch.full((3, 2), 23.0)) # a later layer reusing the block
|
|
1343
|
+
cap.replay()
|
|
1344
|
+
assert torch.equal(site.buf[:3], torch.full((3, 2), 23.0))
|
|
1345
|
+
|
|
1346
|
+
|
|
1242
1347
|
class _FusedQKV(torch.nn.Module):
|
|
1243
1348
|
"""The three numbers :func:`value_span` reads off vLLM's ``QKVParallelLinear``."""
|
|
1244
1349
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{interp_engine-1.6.0 → interp_engine-1.7.1}/benchmarks/results/deepseek-v4-flash-0731__eager.json
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{interp_engine-1.6.0 → interp_engine-1.7.1}/benchmarks/results/deepseek-v4-flash-0731__vllm.json
RENAMED
|
File without changes
|
|
File without changes
|
{interp_engine-1.6.0 → interp_engine-1.7.1}/benchmarks/results/gemma-2-2b__vllm-cudagraph.json
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{interp_engine-1.6.0 → interp_engine-1.7.1}/benchmarks/results/llama-3.1-8b__vllm-cudagraph.json
RENAMED
|
File without changes
|
{interp_engine-1.6.0 → interp_engine-1.7.1}/benchmarks/results/llama-3.1-8b__vllm-static.json
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{interp_engine-1.6.0 → interp_engine-1.7.1}/benchmarks/results/qwen3-4b__vllm-cudagraph.json
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{interp_engine-1.6.0 → interp_engine-1.7.1}/benchmarks/results/qwen3.8-27b__vllm-cudagraph.json
RENAMED
|
File without changes
|
{interp_engine-1.6.0 → interp_engine-1.7.1}/benchmarks/results/qwen3.8-27b__vllm-static.json
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|