interp-engine 1.3.0__tar.gz → 1.3.2__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.3.0 → interp_engine-1.3.2}/PKG-INFO +3 -6
- {interp_engine-1.3.0 → interp_engine-1.3.2}/README.md +2 -5
- {interp_engine-1.3.0 → interp_engine-1.3.2}/interp_engine/chat_formatters.py +27 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/pyproject.toml +1 -1
- {interp_engine-1.3.0 → interp_engine-1.3.2}/tests/test_chat_formatters.py +87 -18
- {interp_engine-1.3.0 → interp_engine-1.3.2}/tests/test_doc_code_fences.py +12 -1
- {interp_engine-1.3.0 → interp_engine-1.3.2}/tests/test_points_registry.py +28 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/tests/test_release.py +50 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/.gitignore +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/LICENSE +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/benchmarks/README.md +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/benchmarks/__init__.py +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/benchmarks/bench_spec.py +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/benchmarks/cells.py +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/benchmarks/probe.py +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/benchmarks/probe_lens_stream.py +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/benchmarks/publish.py +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/benchmarks/report_bench.py +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/benchmarks/results/deepseek-v4-flash-0731__eager.json +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/benchmarks/results/deepseek-v4-flash-0731__vllm-cudagraph.json +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/benchmarks/results/deepseek-v4-flash-0731__vllm-dspark-cudagraph.json +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/benchmarks/results/deepseek-v4-flash-0731__vllm-dspark.json +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/benchmarks/results/deepseek-v4-flash-0731__vllm-static.json +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/benchmarks/results/deepseek-v4-flash-0731__vllm.json +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/benchmarks/results/gemma-2-2b__eager.json +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/benchmarks/results/gemma-2-2b__vllm-cudagraph.json +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/benchmarks/results/gemma-2-2b__vllm-static.json +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/benchmarks/results/gemma-2-2b__vllm.json +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/benchmarks/results/llama-3.1-8b__eager.json +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/benchmarks/results/llama-3.1-8b__vllm-cudagraph.json +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/benchmarks/results/llama-3.1-8b__vllm-static.json +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/benchmarks/results/llama-3.1-8b__vllm.json +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/benchmarks/results/qwen3-4b__eager.json +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/benchmarks/results/qwen3-4b__vllm-cudagraph.json +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/benchmarks/results/qwen3-4b__vllm-static.json +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/benchmarks/results/qwen3-4b__vllm.json +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/benchmarks/results/qwen3.8-27b__eager.json +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/benchmarks/results/qwen3.8-27b__vllm-cudagraph.json +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/benchmarks/results/qwen3.8-27b__vllm-static.json +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/benchmarks/results/qwen3.8-27b__vllm.json +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/benchmarks/results-latest.md +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/benchmarks/run_all.sh +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/benchmarks/run_bench.py +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/benchmarks/workloads.py +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/docs/AGENT_INTEGRATION.md +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/docs/ARCHITECTURE_QUIRKS.md +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/docs/COMPATIBILITY.md +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/docs/ENGINE_HOOK_MAPPINGS.md +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/docs/GRADIENTS.md +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/docs/INTERNALS.md +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/docs/PERFORMANCE.md +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/docs/PORTING.md +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/docs/README.md +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/docs/SUPPORTED_POINTS.md +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/docs/USAGE.md +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/interp_engine/__init__.py +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/interp_engine/_loop.py +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/interp_engine/address.py +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/interp_engine/arch.py +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/interp_engine/attn_config.py +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/interp_engine/attn_scores.py +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/interp_engine/autograd_support.py +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/interp_engine/capture.py +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/interp_engine/chat_compose.py +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/interp_engine/chat_conventions.py +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/interp_engine/cuda_preflight.py +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/interp_engine/dispatch.py +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/interp_engine/facts.py +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/interp_engine/hooks.py +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/interp_engine/lens.py +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/interp_engine/load.py +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/interp_engine/mappers.py +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/interp_engine/model.py +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/interp_engine/moe_routing.py +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/interp_engine/points.py +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/interp_engine/protocol.py +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/interp_engine/residual_basis.py +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/interp_engine/select.py +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/interp_engine/steer.py +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/interp_engine/steer_specs.py +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/interp_engine/sync.py +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/interp_engine/tokenize.py +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/interp_engine/vllm_backend.py +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/interp_engine/vllm_capture/__init__.py +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/interp_engine/vllm_capture/_demux.py +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/interp_engine/vllm_capture/_hooks.py +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/interp_engine/vllm_capture/_payload.py +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/interp_engine/vllm_capture/_tree.py +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/interp_engine/vllm_capture/attn.py +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/interp_engine/vllm_capture/capture.py +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/interp_engine/vllm_capture/graphs.py +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/interp_engine/vllm_capture/lens/__init__.py +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/interp_engine/vllm_capture/lens/intervene.py +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/interp_engine/vllm_capture/lens/readout.py +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/interp_engine/vllm_capture/lens/unembed.py +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/interp_engine/vllm_capture/mhc.py +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/interp_engine/vllm_capture/native.py +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/interp_engine/vllm_capture/requests.py +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/interp_engine/vllm_capture/static.py +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/interp_engine/vllm_capture/steering.py +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/interp_engine/vllm_plugin.py +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/tests/conftest.py +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/tests/harness.py +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/tests/model_expectations.yaml +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/tests/synthetic_families.py +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/tests/test_address.py +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/tests/test_attn_config_tripwire.py +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/tests/test_attn_probs_indexing.py +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/tests/test_attn_scores.py +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/tests/test_attn_z_gqa.py +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/tests/test_autograd_support.py +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/tests/test_bench_workloads.py +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/tests/test_capability_refusals.py +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/tests/test_capture_addressing.py +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/tests/test_chat_compose.py +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/tests/test_chat_templates.py +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/tests/test_core.py +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/tests/test_cuda_preflight.py +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/tests/test_eager_autograd.py +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/tests/test_facts.py +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/tests/test_family_points.py +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/tests/test_gated_attn_out.py +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/tests/test_head_contributions.py +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/tests/test_hook_call_conventions.py +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/tests/test_layer_kinds.py +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/tests/test_load.py +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/tests/test_logit_transform.py +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/tests/test_mappers.py +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/tests/test_mlp_internals.py +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/tests/test_model_expectations.py +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/tests/test_moe.py +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/tests/test_multimodal_arch.py +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/tests/test_new_models_gpu.py +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/tests/test_no_chat_template.py +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/tests/test_normalized_hook.py +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/tests/test_packaging.py +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/tests/test_parity_gpt2.py +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/tests/test_per_layer_attn_dims.py +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/tests/test_protocol.py +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/tests/test_published_benchmarks.py +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/tests/test_qk_norm.py +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/tests/test_qkv_layout.py +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/tests/test_reasoning_spans.py +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/tests/test_resid_mid.py +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/tests/test_residual_basis.py +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/tests/test_sandwich_norms.py +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/tests/test_select.py +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/tests/test_sliding_window_attn.py +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/tests/test_small_models_gpu.py +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/tests/test_static_dsv4_gpu.py +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/tests/test_static_parity_gpu.py +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/tests/test_static_set.py +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/tests/test_static_warmup.py +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/tests/test_steer_context.py +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/tests/test_steer_math_parity.py +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/tests/test_sync_loop.py +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/tests/test_sync_parity.py +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/tests/test_unified_free_functions.py +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/tests/test_unresolved_families.py +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/tests/test_vllm_capture_gpu.py +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/tests/test_vllm_capture_scales.py +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/tests/test_vllm_engine_loop.py +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/tests/test_vllm_graph_path.py +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/tests/test_vllm_graphs_on_gpu.py +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/tests/test_vllm_hook_availability.py +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/tests/test_vllm_hyper_connections.py +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/tests/test_vllm_kv_isolation.py +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/tests/test_vllm_new_points.py +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/tests/test_vllm_only_families.py +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/tests/test_vllm_plugin.py +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/tests/test_vllm_wire_grammar.py +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/tests/test_vocabulary_boundary.py +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/tests/test_worker_lens_capture_readout.py +0 -0
- {interp_engine-1.3.0 → interp_engine-1.3.2}/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.
|
|
3
|
+
Version: 1.3.2
|
|
4
4
|
Summary: A fast, standardized interpretability engine that supports most modern models and architectures. Powers Neuronpedia.
|
|
5
5
|
Project-URL: Homepage, https://github.com/decoderesearch/interp-engine
|
|
6
6
|
Project-URL: Repository, https://github.com/decoderesearch/interp-engine
|
|
@@ -44,7 +44,6 @@ Description-Content-Type: text/markdown
|
|
|
44
44
|
<a href="https://join.slack.com/t/opensourcemechanistic/shared_invite/zt-3z9o0hxjl-MDX9pbATO2qESOazNDLpdQ"><img src="https://img.shields.io/badge/Slack-Open%20Source%20Mechanistic%20Interpretability-4A154B?logo=slack&logoColor=white" alt="Join the Slack"></a>
|
|
45
45
|
</p>
|
|
46
46
|
|
|
47
|
-
|
|
48
47
|
`interp-engine` is an interpretability engine that is fast (>40x tok/s vs HF eager), standardized (34 'points'/addresses across architectures), and easy to use and debug. It powers all of [Neuronpedia](https://neuronpedia.org)'s inference and is checked for accuracy against HF Transformers and other engines.
|
|
49
48
|
|
|
50
49
|
<p align="center">
|
|
@@ -98,7 +97,7 @@ Add "use interp-engine" to your prompt and let your agent figure it out - everyt
|
|
|
98
97
|
|
|
99
98
|
## Performance / Speed
|
|
100
99
|
|
|
101
|
-
vLLM gives `interp-engine` high throughput via concurrency, and `backend="vllm-static"`
|
|
100
|
+
vLLM gives `interp-engine` high throughput via concurrency, and `backend="vllm-static"` gives even higher throughput at the cost of higher VRAM usage. Every column below is capture-capable.
|
|
102
101
|
|
|
103
102
|
<!-- THROUGHPUT:START -->
|
|
104
103
|
|
|
@@ -137,9 +136,7 @@ We verify correctness in two main ways:
|
|
|
137
136
|
1. A test suite that checks results across several models - what each check is designed to catch is in [INTERNALS.md](docs/INTERNALS.md#correctness).
|
|
138
137
|
2. A full `validator` comparison engine that checks most hook points across 50+ models, at early, middle and late layers - fully reproducible, with detailed results saved in the git repo at [`validator/`](validator/).
|
|
139
138
|
|
|
140
|
-
## Why use an Interpretability Engine
|
|
141
|
-
|
|
142
|
-
Software is easy to make in 2026, here's why an engine makes sense:
|
|
139
|
+
## Why use an Interpretability Engine instead of building from scratch?
|
|
143
140
|
|
|
144
141
|
1. **Speed**: Get performance without sacrificing correctness.
|
|
145
142
|
2. **Standardization + Verification**: Eliminate ambiguity when referring to points, plus a full test suite included.
|
|
@@ -13,7 +13,6 @@
|
|
|
13
13
|
<a href="https://join.slack.com/t/opensourcemechanistic/shared_invite/zt-3z9o0hxjl-MDX9pbATO2qESOazNDLpdQ"><img src="https://img.shields.io/badge/Slack-Open%20Source%20Mechanistic%20Interpretability-4A154B?logo=slack&logoColor=white" alt="Join the Slack"></a>
|
|
14
14
|
</p>
|
|
15
15
|
|
|
16
|
-
|
|
17
16
|
`interp-engine` is an interpretability engine that is fast (>40x tok/s vs HF eager), standardized (34 'points'/addresses across architectures), and easy to use and debug. It powers all of [Neuronpedia](https://neuronpedia.org)'s inference and is checked for accuracy against HF Transformers and other engines.
|
|
18
17
|
|
|
19
18
|
<p align="center">
|
|
@@ -67,7 +66,7 @@ Add "use interp-engine" to your prompt and let your agent figure it out - everyt
|
|
|
67
66
|
|
|
68
67
|
## Performance / Speed
|
|
69
68
|
|
|
70
|
-
vLLM gives `interp-engine` high throughput via concurrency, and `backend="vllm-static"`
|
|
69
|
+
vLLM gives `interp-engine` high throughput via concurrency, and `backend="vllm-static"` gives even higher throughput at the cost of higher VRAM usage. Every column below is capture-capable.
|
|
71
70
|
|
|
72
71
|
<!-- THROUGHPUT:START -->
|
|
73
72
|
|
|
@@ -106,9 +105,7 @@ We verify correctness in two main ways:
|
|
|
106
105
|
1. A test suite that checks results across several models - what each check is designed to catch is in [INTERNALS.md](docs/INTERNALS.md#correctness).
|
|
107
106
|
2. A full `validator` comparison engine that checks most hook points across 50+ models, at early, middle and late layers - fully reproducible, with detailed results saved in the git repo at [`validator/`](validator/).
|
|
108
107
|
|
|
109
|
-
## Why use an Interpretability Engine
|
|
110
|
-
|
|
111
|
-
Software is easy to make in 2026, here's why an engine makes sense:
|
|
108
|
+
## Why use an Interpretability Engine instead of building from scratch?
|
|
112
109
|
|
|
113
110
|
1. **Speed**: Get performance without sacrificing correctness.
|
|
114
111
|
2. **Standardization + Verification**: Eliminate ambiguity when referring to points, plus a full test suite included.
|
|
@@ -188,6 +188,7 @@ class DeepseekV4Formatter:
|
|
|
188
188
|
prefix = self._bos + "".join(blocks[: len(lead)])
|
|
189
189
|
blocks = blocks[len(lead) :]
|
|
190
190
|
|
|
191
|
+
self._reassign_interior_openers(blocks, turns)
|
|
191
192
|
scaffold = self._split_scaffold(blocks, turns)
|
|
192
193
|
return RenderedChat(prefix=prefix, blocks=tuple(blocks), suffix=scaffold if add_generation_prompt else "")
|
|
193
194
|
|
|
@@ -270,6 +271,32 @@ class DeepseekV4Formatter:
|
|
|
270
271
|
turn.setdefault(self._reasoning_key, value)
|
|
271
272
|
return turn
|
|
272
273
|
|
|
274
|
+
def _reassign_interior_openers(self, blocks: list[str], turns: Sequence[Message]) -> None:
|
|
275
|
+
"""Move each assistant opener onto the assistant turn it opens, in place.
|
|
276
|
+
|
|
277
|
+
The encoder writes ``<|Assistant|>`` plus a thinking delimiter at the *tail* of every
|
|
278
|
+
user/developer turn -- including interior ones that a completed assistant answer follows,
|
|
279
|
+
not just the last turn whose opener is the generation scaffold. Because ``_tails``
|
|
280
|
+
decomposes the render by common suffix, that opener lands at the end of the *user* block,
|
|
281
|
+
so span metadata attributes it to the user turn: the opener renders inside the user's
|
|
282
|
+
bubble and the assistant's bubble starts on bare content. This peels it off the user turn
|
|
283
|
+
and prepends it to the assistant turn it belongs to, leaving ``RenderedChat.text``
|
|
284
|
+
unchanged (a substring only moves across the block boundary). The final turn's opener has
|
|
285
|
+
no assistant turn after it and is handled by :meth:`_split_scaffold` instead.
|
|
286
|
+
"""
|
|
287
|
+
for k in range(len(blocks) - 1):
|
|
288
|
+
turn = turns[k]
|
|
289
|
+
if turn.get("role") not in ("user", "developer") or turn.get("task") is not None:
|
|
290
|
+
continue
|
|
291
|
+
if turns[k + 1].get("role") != "assistant":
|
|
292
|
+
continue
|
|
293
|
+
for delimiter in (self._think_open, self._think_close):
|
|
294
|
+
opener = self._assistant_token + delimiter
|
|
295
|
+
if blocks[k].endswith(opener):
|
|
296
|
+
blocks[k] = blocks[k][: -len(opener)]
|
|
297
|
+
blocks[k + 1] = opener + blocks[k + 1]
|
|
298
|
+
break
|
|
299
|
+
|
|
273
300
|
def _split_scaffold(self, blocks: list[str], turns: Sequence[Message]) -> str:
|
|
274
301
|
"""Remove and return the trailing generation scaffold from ``blocks``, or ``""``.
|
|
275
302
|
|
|
@@ -66,9 +66,12 @@ def _stub_encoder(reasoning_key: str = "reasoning_content") -> ModuleType:
|
|
|
66
66
|
"""A module with the surface :class:`DeepseekV4Formatter` reads, and the same semantics.
|
|
67
67
|
|
|
68
68
|
Not a second implementation of the format -- it renders a deliberately simplified one. What
|
|
69
|
-
it reproduces faithfully is the
|
|
70
|
-
trailing ``<|Assistant|>`` scaffold appears whether or not you asked for it
|
|
71
|
-
|
|
69
|
+
it reproduces faithfully is the four behaviours the formatter has to work around: the
|
|
70
|
+
trailing ``<|Assistant|>`` scaffold appears whether or not you asked for it (and rides on
|
|
71
|
+
the user turn it follows, not the assistant turn it opens), ``wo_eos`` is how a turn is held
|
|
72
|
+
open, ``context`` decomposes the render per message, and ``drop_thinking`` strips a completed
|
|
73
|
+
answer's reasoning once a later user turn demotes it to history -- the non-monotonic rewrite
|
|
74
|
+
this whole module exists for.
|
|
72
75
|
"""
|
|
73
76
|
module = ModuleType("stub_dsv4_encoder")
|
|
74
77
|
module.bos_token = BOS # type: ignore[attr-defined]
|
|
@@ -78,39 +81,53 @@ def _stub_encoder(reasoning_key: str = "reasoning_content") -> ModuleType:
|
|
|
78
81
|
module.thinking_end_token = THINK_CLOSE # type: ignore[attr-defined]
|
|
79
82
|
module.thinking_template = "{" + reasoning_key + "}" # type: ignore[attr-defined]
|
|
80
83
|
|
|
81
|
-
def
|
|
84
|
+
def last_user_index(messages: Sequence[dict]) -> int:
|
|
85
|
+
for idx in range(len(messages) - 1, -1, -1):
|
|
86
|
+
if messages[idx].get("role") in ("user", "developer"):
|
|
87
|
+
return idx
|
|
88
|
+
return -1
|
|
89
|
+
|
|
90
|
+
def render_one(index: int, messages: Sequence[dict], thinking_mode: str, drop_thinking: bool) -> str:
|
|
82
91
|
message = messages[index]
|
|
83
92
|
role = message.get("role")
|
|
93
|
+
last_user = last_user_index(messages)
|
|
84
94
|
text = ""
|
|
85
95
|
if role == "user":
|
|
86
96
|
text += USER + (message.get("content") or "")
|
|
87
97
|
elif role == "assistant":
|
|
88
|
-
|
|
98
|
+
# Reasoning survives only for the turn being generated (after the last user turn) or
|
|
99
|
+
# when dropping is off -- the encoder's `drop_thinking`, which makes the render
|
|
100
|
+
# non-monotonic because a completed answer's block shrinks once a later user turn
|
|
101
|
+
# demotes it to history.
|
|
102
|
+
if thinking_mode == "thinking" and (not drop_thinking or index > last_user):
|
|
89
103
|
text += (message.get(reasoning_key) or "") + THINK_CLOSE
|
|
90
104
|
text += message.get("content") or ""
|
|
91
105
|
if not message.get("wo_eos", False):
|
|
92
106
|
text += EOS
|
|
93
107
|
else:
|
|
94
108
|
text += message.get("content") or ""
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
109
|
+
# The assistant opener rides on the user/developer turn it follows (or on the last turn,
|
|
110
|
+
# as the generation scaffold), never on the assistant turn itself.
|
|
111
|
+
is_last = index == len(messages) - 1
|
|
112
|
+
next_is_assistant = not is_last and messages[index + 1].get("role") == "assistant"
|
|
113
|
+
if role in ("user", "developer") and (is_last or next_is_assistant):
|
|
114
|
+
opens_thinking = thinking_mode == "thinking" and (not drop_thinking or index >= last_user)
|
|
115
|
+
text += ASSISTANT + (THINK_OPEN if opens_thinking else THINK_CLOSE)
|
|
99
116
|
return text
|
|
100
117
|
|
|
101
118
|
def encode_messages(
|
|
102
119
|
messages: Sequence[dict],
|
|
103
120
|
thinking_mode: str,
|
|
104
121
|
context: Sequence[dict] | None = None,
|
|
105
|
-
drop_thinking: bool = True,
|
|
122
|
+
drop_thinking: bool = True,
|
|
106
123
|
add_default_bos_token: bool = True,
|
|
107
|
-
reasoning_effort: str | None = None, # noqa: ARG001 -
|
|
124
|
+
reasoning_effort: str | None = None, # noqa: ARG001 - part of the signature under test
|
|
108
125
|
) -> str:
|
|
109
126
|
context = list(context or [])
|
|
110
127
|
full = context + list(messages)
|
|
111
128
|
prompt = BOS if add_default_bos_token and not context else ""
|
|
112
129
|
for offset in range(len(messages)):
|
|
113
|
-
prompt += render_one(len(context) + offset, full, thinking_mode)
|
|
130
|
+
prompt += render_one(len(context) + offset, full, thinking_mode, drop_thinking)
|
|
114
131
|
return prompt
|
|
115
132
|
|
|
116
133
|
module.encode_messages = encode_messages # type: ignore[attr-defined]
|
|
@@ -255,6 +272,30 @@ def test_continue_final_message_refuses_a_non_assistant_turn(formatter: Deepseek
|
|
|
255
272
|
formatter.render(CHAT, continue_final_message=True)
|
|
256
273
|
|
|
257
274
|
|
|
275
|
+
def test_interior_assistant_opener_rides_on_its_own_block(formatter: DeepseekV4Formatter):
|
|
276
|
+
"""A completed answer's ``<|Assistant|>`` opener belongs to the assistant, not the user.
|
|
277
|
+
|
|
278
|
+
The encoder writes the opener at the *tail* of the user turn it follows, so the raw block
|
|
279
|
+
split leaves it on the user turn. Left there it renders inside the previous user's bubble,
|
|
280
|
+
and a follow-up message makes the whole assistant scaffold appear to jump backwards a turn.
|
|
281
|
+
The blocks must instead read as one clean turn each.
|
|
282
|
+
"""
|
|
283
|
+
conversation = [
|
|
284
|
+
{"role": "user", "content": "hi"},
|
|
285
|
+
{"role": "assistant", "content": "Hi there!"},
|
|
286
|
+
{"role": "user", "content": "hi 2"},
|
|
287
|
+
]
|
|
288
|
+
rendered = formatter.render(conversation, add_generation_prompt=True, enable_thinking=False)
|
|
289
|
+
assert rendered.blocks == (
|
|
290
|
+
USER + "hi",
|
|
291
|
+
ASSISTANT + THINK_CLOSE + "Hi there!" + EOS,
|
|
292
|
+
USER + "hi 2",
|
|
293
|
+
)
|
|
294
|
+
assert rendered.suffix == ASSISTANT + THINK_CLOSE
|
|
295
|
+
# The move only relocates a substring across a boundary; the prompt itself is unchanged.
|
|
296
|
+
assert rendered.text == formatter.render(conversation, add_generation_prompt=True, enable_thinking=False).text
|
|
297
|
+
|
|
298
|
+
|
|
258
299
|
def test_closed_assistant_turn_gets_no_invented_scaffold(formatter: DeepseekV4Formatter):
|
|
259
300
|
"""A Jinja template would open a fresh turn here; this format does not, and we follow it.
|
|
260
301
|
|
|
@@ -385,6 +426,26 @@ def test_generation_scaffold_opens_the_assistant_turn(tok: Tokenize):
|
|
|
385
426
|
assert all(s.role == "assistant" and s.section == "header" for s in trailing)
|
|
386
427
|
|
|
387
428
|
|
|
429
|
+
def test_completed_assistant_opener_is_the_assistants_header_not_the_users_footer(tok: Tokenize):
|
|
430
|
+
"""The span-level regression behind the reported bug.
|
|
431
|
+
|
|
432
|
+
On a follow-up turn the previous assistant's ``<|Assistant|></think>`` scaffold was landing
|
|
433
|
+
in the earlier user bubble (span metadata attributed it to the user's message), so the user
|
|
434
|
+
turn grew an assistant footer and the assistant turn lost its header. Each opener must be the
|
|
435
|
+
header of the assistant turn it introduces.
|
|
436
|
+
"""
|
|
437
|
+
conversation = [
|
|
438
|
+
{"role": "user", "content": "hi"},
|
|
439
|
+
{"role": "assistant", "content": "Hi there!"},
|
|
440
|
+
{"role": "user", "content": "hi 2"},
|
|
441
|
+
]
|
|
442
|
+
spans = tok.message_spans(conversation, add_generation_prompt=True, enable_thinking=False)
|
|
443
|
+
user0 = "".join(s.token_str for s in spans if s.message_index == 0)
|
|
444
|
+
assistant1_header = "".join(s.token_str for s in spans if s.message_index == 1 and s.section == "header")
|
|
445
|
+
assert ASSISTANT not in user0, "the assistant opener leaked into the user's bubble"
|
|
446
|
+
assert assistant1_header == ASSISTANT + THINK_CLOSE
|
|
447
|
+
|
|
448
|
+
|
|
388
449
|
def test_prefill_spans_end_on_content(tok: Tokenize):
|
|
389
450
|
"""A held-open turn has no footer to find: generation continues from its last token."""
|
|
390
451
|
prefill = [*CHAT, {"role": "assistant", "content": "It is "}]
|
|
@@ -398,7 +459,9 @@ def test_prefill_spans_end_on_content(tok: Tokenize):
|
|
|
398
459
|
# --------------------------------------------------------------------------- #
|
|
399
460
|
|
|
400
461
|
|
|
401
|
-
def _legacy_partition(
|
|
462
|
+
def _legacy_partition(
|
|
463
|
+
tok: Tokenize, messages: list[dict[str, str]], **template_kwargs: Any
|
|
464
|
+
) -> tuple[list[int], list[tuple[int, int]]]:
|
|
402
465
|
"""The prefix-delta arithmetic that callers ran inline before ``message_partition``.
|
|
403
466
|
|
|
404
467
|
Kept here verbatim as the reference implementation. A template-rendered model has to
|
|
@@ -409,7 +472,9 @@ def _legacy_partition(tok: Tokenize, messages: list[dict[str, str]]) -> tuple[li
|
|
|
409
472
|
previous = 0
|
|
410
473
|
full_ids: list[int] = []
|
|
411
474
|
for index in range(len(messages)):
|
|
412
|
-
current = tok.apply_chat_template(
|
|
475
|
+
current = tok.apply_chat_template(
|
|
476
|
+
messages[: index + 1], tokenize=True, add_generation_prompt=False, **template_kwargs
|
|
477
|
+
)
|
|
413
478
|
assert isinstance(current, list)
|
|
414
479
|
spans.append((previous, len(current)))
|
|
415
480
|
previous = len(current)
|
|
@@ -507,11 +572,15 @@ def test_the_legacy_arithmetic_would_misplace_a_code_rendered_conversation(tok:
|
|
|
507
572
|
"""Why this method exists rather than the caller keeping its inline loop.
|
|
508
573
|
|
|
509
574
|
The prefix delta assumes appending a message only appends tokens. DeepSeek-V4 rewrites
|
|
510
|
-
earlier turns once a later user turn exists (
|
|
511
|
-
|
|
512
|
-
a partition.
|
|
575
|
+
earlier turns once a later user turn exists (``drop_thinking`` strips a completed answer's
|
|
576
|
+
reasoning once it stops being the current turn), so the deltas land in the wrong places — and
|
|
577
|
+
the failure is silent, since they still look like a partition. This only bites in thinking
|
|
578
|
+
mode, where there is reasoning to drop; in chat mode the render is monotonic and the two
|
|
579
|
+
agree.
|
|
513
580
|
"""
|
|
514
|
-
assert tok.message_partition(MULTI_TURN) != _legacy_partition(
|
|
581
|
+
assert tok.message_partition(MULTI_TURN, enable_thinking=True) != _legacy_partition(
|
|
582
|
+
tok, MULTI_TURN, enable_thinking=True
|
|
583
|
+
)
|
|
515
584
|
|
|
516
585
|
|
|
517
586
|
# --------------------------------------------------------------------------- #
|
|
@@ -48,6 +48,12 @@ DOC_FILES = (
|
|
|
48
48
|
"benchmarks/README.md",
|
|
49
49
|
)
|
|
50
50
|
|
|
51
|
+
#: The samples site, which is nothing *but* fences a reader pastes -- so it is the doc that rot
|
|
52
|
+
#: hurts most. Globbed rather than listed: a page there is one file with one job, and adding one
|
|
53
|
+
#: should not need an edit here. Its own build would not catch a rename, since it never imports
|
|
54
|
+
#: the package.
|
|
55
|
+
SAMPLE_DOCS = tuple(sorted(str(p.relative_to(ROOT)) for p in (ROOT / "visualizer-web/docs-site/docs").glob("*.md")))
|
|
56
|
+
|
|
51
57
|
#: Submodules the docs may import from directly. The package's own ``__init__`` says these two are
|
|
52
58
|
#: reachable-but-not-re-exported on purpose (the worker RPC names, and the payload codecs), so a doc
|
|
53
59
|
#: that shows tier-1 integration has to name them.
|
|
@@ -85,7 +91,7 @@ def _engine_imports(tree: ast.Module) -> list[tuple[str, str]]:
|
|
|
85
91
|
|
|
86
92
|
DOC_FENCES = [
|
|
87
93
|
pytest.param(path, line, source, id=f"{path}:{line}")
|
|
88
|
-
for path in DOC_FILES
|
|
94
|
+
for path in DOC_FILES + SAMPLE_DOCS
|
|
89
95
|
for line, source in _fences((ROOT / path).read_text())
|
|
90
96
|
]
|
|
91
97
|
|
|
@@ -98,6 +104,11 @@ def test_the_doc_list_still_matches_the_docs_on_disk():
|
|
|
98
104
|
assert (ROOT / path).exists(), f"{path} is listed here but does not exist"
|
|
99
105
|
|
|
100
106
|
|
|
107
|
+
def test_the_samples_site_was_found():
|
|
108
|
+
"""The glob above is silent when the directory moves, which would drop every sample page."""
|
|
109
|
+
assert len(SAMPLE_DOCS) > 5, f"only {len(SAMPLE_DOCS)} sample pages found; did docs-site move?"
|
|
110
|
+
|
|
111
|
+
|
|
101
112
|
def test_there_are_fences_to_check():
|
|
102
113
|
"""Guards against the regex silently matching nothing after a format change."""
|
|
103
114
|
assert len(DOC_FENCES) > 15, f"only {len(DOC_FENCES)} python fences found; did the fence format change?"
|
|
@@ -32,6 +32,11 @@ from interp_engine.vllm_capture.requests import _DEMUX_MHC_HOOKS, _DEMUX_OUT_HOO
|
|
|
32
32
|
ROOT = pathlib.Path(__file__).resolve().parent.parent
|
|
33
33
|
DOCS = ROOT / "docs"
|
|
34
34
|
|
|
35
|
+
#: The samples site's vocabulary page. Outside ``docs/``, so nothing else in this file reaches it,
|
|
36
|
+
#: and it tables every point by name -- the one list a reader consults to find out what may be asked
|
|
37
|
+
#: for at all.
|
|
38
|
+
ADDRESSES_PAGE = ROOT / "visualizer-web/docs-site/docs/addresses.md"
|
|
39
|
+
|
|
35
40
|
#: How the support table in ``docs/SUPPORTED_POINTS.md`` spells each :class:`points.VllmSupport`.
|
|
36
41
|
_SUPPORT_MARKS = {
|
|
37
42
|
"✅": points.VllmSupport.HOOKS,
|
|
@@ -50,6 +55,9 @@ _PROSE_COUNTS = (
|
|
|
50
55
|
("docs/SUPPORTED_POINTS.md", r"The (\d+) canonical points", "declared"),
|
|
51
56
|
("docs/SUPPORTED_POINTS.md", r"vLLM backend serves (\d+) of them", "served"),
|
|
52
57
|
("docs/SUPPORTED_POINTS.md", r"(\d+) of those by recompute", "recomputed"),
|
|
58
|
+
("visualizer-web/docs-site/docs/addresses.md", r"The (\d+) points", "declared"),
|
|
59
|
+
("visualizer-web/docs-site/docs/addresses.md", r"(\d+) on every model", "global"),
|
|
60
|
+
("visualizer-web/docs-site/docs/addresses.md", r"(\d+) more that need a hyper-connection trunk", "conditional"),
|
|
53
61
|
)
|
|
54
62
|
|
|
55
63
|
|
|
@@ -313,12 +321,32 @@ def test_the_support_table_doc_says_what_the_point_table_declares():
|
|
|
313
321
|
)
|
|
314
322
|
|
|
315
323
|
|
|
324
|
+
def test_the_samples_page_names_every_point_and_no_others():
|
|
325
|
+
"""``addresses.md``'s two tables, checked against the registry they transcribe.
|
|
326
|
+
|
|
327
|
+
It carries no support column -- that claim is SUPPORTED_POINTS.md's, and checked above -- so
|
|
328
|
+
this is the narrower assertion that the names are the point set. A point added without a row
|
|
329
|
+
here is one the samples site tells a reader does not exist, which is the same silent failure
|
|
330
|
+
as a point missing from any other consumer.
|
|
331
|
+
"""
|
|
332
|
+
rows = re.findall(r"^\|([^|]*`\w+`[^|]*)\|", ADDRESSES_PAGE.read_text(), re.MULTILINE)
|
|
333
|
+
assert len(rows) > 20, f"the table parse found only {len(rows)} rows; did the page's format change?"
|
|
334
|
+
|
|
335
|
+
listed = {name for row in rows for name in re.findall(r"`(\w+)`", row)}
|
|
336
|
+
every = {p.name for p in (*points.POINTS, *points.HYPER_CONNECTION_POINTS)}
|
|
337
|
+
assert listed == every, (
|
|
338
|
+
f"missing from addresses.md: {sorted(every - listed)}; no longer a point: {sorted(listed - every)}"
|
|
339
|
+
)
|
|
340
|
+
|
|
341
|
+
|
|
316
342
|
@pytest.mark.parametrize(("doc", "pattern", "which"), _PROSE_COUNTS)
|
|
317
343
|
def test_a_point_count_stated_in_prose_matches_the_registry(doc: str, pattern: str, which: str):
|
|
318
344
|
"""A sentence saying "34 standardized points" is a claim the table beside it does not keep honest."""
|
|
319
345
|
every = (*points.POINTS, *points.HYPER_CONNECTION_POINTS)
|
|
320
346
|
expected = {
|
|
321
347
|
"declared": len(every),
|
|
348
|
+
"global": len(points.POINTS),
|
|
349
|
+
"conditional": len(points.HYPER_CONNECTION_POINTS),
|
|
322
350
|
"served": sum(p.vllm is not points.VllmSupport.NONE for p in every),
|
|
323
351
|
"recomputed": sum(p.vllm is points.VllmSupport.RECOMPUTE for p in every),
|
|
324
352
|
}[which]
|
|
@@ -136,6 +136,56 @@ class TestWhichVersionWins:
|
|
|
136
136
|
assert release.version_key("v1.2.3") is None
|
|
137
137
|
|
|
138
138
|
|
|
139
|
+
class TestWhetherAChangeIsWorthAVersion:
|
|
140
|
+
"""A version is what a downstream pin moves to, so only the imported engine earns one.
|
|
141
|
+
|
|
142
|
+
Two exclusions with different reasons: `visualizer-web/` and the validator never reach a user,
|
|
143
|
+
while `tests/`, `docs/`, `benchmarks/` and the README do reach one and still cannot change what
|
|
144
|
+
`import interp_engine` gives them.
|
|
145
|
+
"""
|
|
146
|
+
|
|
147
|
+
PREFIXES = ["interp_engine", "tests", "docs", "benchmarks", "README.md", "pyproject.toml"]
|
|
148
|
+
|
|
149
|
+
def test_the_engine_and_its_dependency_set_are_worth_a_version(self):
|
|
150
|
+
assert release.changes_the_package("interp_engine/points.py", self.PREFIXES)
|
|
151
|
+
# The dependency table is part of what an install resolves, so pyproject counts.
|
|
152
|
+
assert release.changes_the_package("pyproject.toml", self.PREFIXES)
|
|
153
|
+
|
|
154
|
+
def test_the_sidecar_projects_are_not(self):
|
|
155
|
+
assert not release.changes_the_package("visualizer-web/data/points.ts", self.PREFIXES)
|
|
156
|
+
assert not release.changes_the_package("validator/comparison/score.py", self.PREFIXES)
|
|
157
|
+
assert not release.changes_the_package("plans/deepseek-v4-b200-bringup.md", self.PREFIXES)
|
|
158
|
+
|
|
159
|
+
def test_prose_tests_and_measurements_ship_without_being_worth_a_version(self):
|
|
160
|
+
# Every one of these is inside the sdist -- `is_packaged` says yes and the answer is no.
|
|
161
|
+
assert release.is_packaged("docs/USAGE.md", self.PREFIXES)
|
|
162
|
+
assert not release.changes_the_package("docs/USAGE.md", self.PREFIXES)
|
|
163
|
+
assert not release.changes_the_package("README.md", self.PREFIXES)
|
|
164
|
+
assert not release.changes_the_package("tests/test_points.py", self.PREFIXES)
|
|
165
|
+
assert not release.changes_the_package("benchmarks/results/gpt2.json", self.PREFIXES)
|
|
166
|
+
|
|
167
|
+
def test_a_markdown_file_beside_the_engine_is_still_only_prose(self):
|
|
168
|
+
assert not release.changes_the_package("interp_engine/README.md", self.PREFIXES)
|
|
169
|
+
assert release.changes_the_package("interp_engine/vllm_capture/mhc.py", self.PREFIXES)
|
|
170
|
+
|
|
171
|
+
def test_the_first_release_needs_no_comparison(self):
|
|
172
|
+
cut, why = release.release_plan("1.1.0", None, "auto", self.PREFIXES)
|
|
173
|
+
assert cut is True
|
|
174
|
+
assert "no v* tag" in why
|
|
175
|
+
|
|
176
|
+
def test_a_hand_declared_version_releases_whatever_changed(self):
|
|
177
|
+
# The escape hatch for a docs-only release, and for repairing a version that reached PyPI
|
|
178
|
+
# untagged. It is read before anything is diffed, so it cannot be overruled.
|
|
179
|
+
cut, why = release.release_plan("2.0.0", "1.4.3", "auto", self.PREFIXES)
|
|
180
|
+
assert cut is True
|
|
181
|
+
assert "by hand" in why
|
|
182
|
+
|
|
183
|
+
def test_a_dispatched_bump_releases_whatever_changed(self):
|
|
184
|
+
cut, why = release.release_plan("1.4.3", "1.4.3", "minor", self.PREFIXES)
|
|
185
|
+
assert cut is True
|
|
186
|
+
assert "by hand" in why
|
|
187
|
+
|
|
188
|
+
|
|
139
189
|
class TestWhetherToUpload:
|
|
140
190
|
PREFIXES = ["interp_engine", "tests", "docs", "README.md", "pyproject.toml"]
|
|
141
191
|
|
|
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.3.0 → interp_engine-1.3.2}/benchmarks/results/deepseek-v4-flash-0731__eager.json
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{interp_engine-1.3.0 → interp_engine-1.3.2}/benchmarks/results/deepseek-v4-flash-0731__vllm.json
RENAMED
|
File without changes
|
|
File without changes
|
{interp_engine-1.3.0 → interp_engine-1.3.2}/benchmarks/results/gemma-2-2b__vllm-cudagraph.json
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{interp_engine-1.3.0 → interp_engine-1.3.2}/benchmarks/results/llama-3.1-8b__vllm-cudagraph.json
RENAMED
|
File without changes
|
{interp_engine-1.3.0 → interp_engine-1.3.2}/benchmarks/results/llama-3.1-8b__vllm-static.json
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{interp_engine-1.3.0 → interp_engine-1.3.2}/benchmarks/results/qwen3-4b__vllm-cudagraph.json
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{interp_engine-1.3.0 → interp_engine-1.3.2}/benchmarks/results/qwen3.8-27b__vllm-cudagraph.json
RENAMED
|
File without changes
|
{interp_engine-1.3.0 → interp_engine-1.3.2}/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
|