interp-engine 1.13.0__tar.gz → 1.13.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.
Files changed (222) hide show
  1. {interp_engine-1.13.0 → interp_engine-1.13.2}/PKG-INFO +1 -1
  2. {interp_engine-1.13.0 → interp_engine-1.13.2}/docs/INTERNALS.md +13 -1
  3. {interp_engine-1.13.0 → interp_engine-1.13.2}/interp_engine/vllm_capture/_demux.py +32 -2
  4. {interp_engine-1.13.0 → interp_engine-1.13.2}/interp_engine/vllm_capture/mhc.py +5 -1
  5. {interp_engine-1.13.0 → interp_engine-1.13.2}/interp_engine/vllm_capture/requests.py +28 -4
  6. {interp_engine-1.13.0 → interp_engine-1.13.2}/interp_engine/vllm_capture/static.py +58 -8
  7. interp_engine-1.13.2/interp_engine/vllm_capture/static_program.py +558 -0
  8. {interp_engine-1.13.0 → interp_engine-1.13.2}/pyproject.toml +1 -1
  9. interp_engine-1.13.2/tests/test_static_decode_writes_gpu.py +177 -0
  10. interp_engine-1.13.2/tests/test_static_program.py +207 -0
  11. interp_engine-1.13.2/tests/test_static_program_gpu.py +143 -0
  12. {interp_engine-1.13.0 → interp_engine-1.13.2}/tests/test_steer_math_parity.py +58 -0
  13. interp_engine-1.13.2/tests/test_vllm_batch_axis.py +94 -0
  14. {interp_engine-1.13.0 → interp_engine-1.13.2}/tests/test_vllm_hyper_connections.py +30 -0
  15. interp_engine-1.13.2/tests/test_vllm_transformers_backend_gpu.py +150 -0
  16. {interp_engine-1.13.0 → interp_engine-1.13.2}/.gitignore +0 -0
  17. {interp_engine-1.13.0 → interp_engine-1.13.2}/LICENSE +0 -0
  18. {interp_engine-1.13.0 → interp_engine-1.13.2}/README.md +0 -0
  19. {interp_engine-1.13.0 → interp_engine-1.13.2}/benchmarks/README.md +0 -0
  20. {interp_engine-1.13.0 → interp_engine-1.13.2}/benchmarks/__init__.py +0 -0
  21. {interp_engine-1.13.0 → interp_engine-1.13.2}/benchmarks/bench_spec.py +0 -0
  22. {interp_engine-1.13.0 → interp_engine-1.13.2}/benchmarks/cells.py +0 -0
  23. {interp_engine-1.13.0 → interp_engine-1.13.2}/benchmarks/probe.py +0 -0
  24. {interp_engine-1.13.0 → interp_engine-1.13.2}/benchmarks/probe_lens_stream.py +0 -0
  25. {interp_engine-1.13.0 → interp_engine-1.13.2}/benchmarks/publish.py +0 -0
  26. {interp_engine-1.13.0 → interp_engine-1.13.2}/benchmarks/report_bench.py +0 -0
  27. {interp_engine-1.13.0 → interp_engine-1.13.2}/benchmarks/results/deepseek-v4-flash-0731__eager.json +0 -0
  28. {interp_engine-1.13.0 → interp_engine-1.13.2}/benchmarks/results/deepseek-v4-flash-0731__vllm-cudagraph.json +0 -0
  29. {interp_engine-1.13.0 → interp_engine-1.13.2}/benchmarks/results/deepseek-v4-flash-0731__vllm-static.json +0 -0
  30. {interp_engine-1.13.0 → interp_engine-1.13.2}/benchmarks/results/deepseek-v4-flash-0731__vllm.json +0 -0
  31. {interp_engine-1.13.0 → interp_engine-1.13.2}/benchmarks/results/gemma-2-2b__eager.json +0 -0
  32. {interp_engine-1.13.0 → interp_engine-1.13.2}/benchmarks/results/gemma-2-2b__vllm-cudagraph.json +0 -0
  33. {interp_engine-1.13.0 → interp_engine-1.13.2}/benchmarks/results/gemma-2-2b__vllm-static.json +0 -0
  34. {interp_engine-1.13.0 → interp_engine-1.13.2}/benchmarks/results/gemma-2-2b__vllm.json +0 -0
  35. {interp_engine-1.13.0 → interp_engine-1.13.2}/benchmarks/results/llama-3.1-8b__eager.json +0 -0
  36. {interp_engine-1.13.0 → interp_engine-1.13.2}/benchmarks/results/llama-3.1-8b__vllm-cudagraph.json +0 -0
  37. {interp_engine-1.13.0 → interp_engine-1.13.2}/benchmarks/results/llama-3.1-8b__vllm-static.json +0 -0
  38. {interp_engine-1.13.0 → interp_engine-1.13.2}/benchmarks/results/llama-3.1-8b__vllm.json +0 -0
  39. {interp_engine-1.13.0 → interp_engine-1.13.2}/benchmarks/results/qwen3-4b__eager.json +0 -0
  40. {interp_engine-1.13.0 → interp_engine-1.13.2}/benchmarks/results/qwen3-4b__vllm-cudagraph.json +0 -0
  41. {interp_engine-1.13.0 → interp_engine-1.13.2}/benchmarks/results/qwen3-4b__vllm-static.json +0 -0
  42. {interp_engine-1.13.0 → interp_engine-1.13.2}/benchmarks/results/qwen3-4b__vllm.json +0 -0
  43. {interp_engine-1.13.0 → interp_engine-1.13.2}/benchmarks/results/qwen3.8-27b__eager.json +0 -0
  44. {interp_engine-1.13.0 → interp_engine-1.13.2}/benchmarks/results/qwen3.8-27b__vllm-cudagraph.json +0 -0
  45. {interp_engine-1.13.0 → interp_engine-1.13.2}/benchmarks/results/qwen3.8-27b__vllm-static.json +0 -0
  46. {interp_engine-1.13.0 → interp_engine-1.13.2}/benchmarks/results/qwen3.8-27b__vllm.json +0 -0
  47. {interp_engine-1.13.0 → interp_engine-1.13.2}/benchmarks/results-latest.md +0 -0
  48. {interp_engine-1.13.0 → interp_engine-1.13.2}/benchmarks/run_all.sh +0 -0
  49. {interp_engine-1.13.0 → interp_engine-1.13.2}/benchmarks/run_bench.py +0 -0
  50. {interp_engine-1.13.0 → interp_engine-1.13.2}/benchmarks/workloads.py +0 -0
  51. {interp_engine-1.13.0 → interp_engine-1.13.2}/docs/AGENT_INTEGRATION.md +0 -0
  52. {interp_engine-1.13.0 → interp_engine-1.13.2}/docs/ARCHITECTURE_QUIRKS.md +0 -0
  53. {interp_engine-1.13.0 → interp_engine-1.13.2}/docs/COMPATIBILITY.md +0 -0
  54. {interp_engine-1.13.0 → interp_engine-1.13.2}/docs/ENGINE_HOOK_MAPPINGS.md +0 -0
  55. {interp_engine-1.13.0 → interp_engine-1.13.2}/docs/GRADIENTS.md +0 -0
  56. {interp_engine-1.13.0 → interp_engine-1.13.2}/docs/PERFORMANCE.md +0 -0
  57. {interp_engine-1.13.0 → interp_engine-1.13.2}/docs/PORTING.md +0 -0
  58. {interp_engine-1.13.0 → interp_engine-1.13.2}/docs/README.md +0 -0
  59. {interp_engine-1.13.0 → interp_engine-1.13.2}/docs/SUPPORTED_POINTS.md +0 -0
  60. {interp_engine-1.13.0 → interp_engine-1.13.2}/docs/USAGE.md +0 -0
  61. {interp_engine-1.13.0 → interp_engine-1.13.2}/docs/VALIDATING_A_NEW_MODEL.md +0 -0
  62. {interp_engine-1.13.0 → interp_engine-1.13.2}/interp_engine/__init__.py +0 -0
  63. {interp_engine-1.13.0 → interp_engine-1.13.2}/interp_engine/_conformance.py +0 -0
  64. {interp_engine-1.13.0 → interp_engine-1.13.2}/interp_engine/_loop.py +0 -0
  65. {interp_engine-1.13.0 → interp_engine-1.13.2}/interp_engine/address.py +0 -0
  66. {interp_engine-1.13.0 → interp_engine-1.13.2}/interp_engine/api.py +0 -0
  67. {interp_engine-1.13.0 → interp_engine-1.13.2}/interp_engine/arch.py +0 -0
  68. {interp_engine-1.13.0 → interp_engine-1.13.2}/interp_engine/attn_config.py +0 -0
  69. {interp_engine-1.13.0 → interp_engine-1.13.2}/interp_engine/attn_scores.py +0 -0
  70. {interp_engine-1.13.0 → interp_engine-1.13.2}/interp_engine/autograd_support.py +0 -0
  71. {interp_engine-1.13.0 → interp_engine-1.13.2}/interp_engine/capture.py +0 -0
  72. {interp_engine-1.13.0 → interp_engine-1.13.2}/interp_engine/chat_compose.py +0 -0
  73. {interp_engine-1.13.0 → interp_engine-1.13.2}/interp_engine/chat_conventions.py +0 -0
  74. {interp_engine-1.13.0 → interp_engine-1.13.2}/interp_engine/chat_formatters.py +0 -0
  75. {interp_engine-1.13.0 → interp_engine-1.13.2}/interp_engine/cuda_preflight.py +0 -0
  76. {interp_engine-1.13.0 → interp_engine-1.13.2}/interp_engine/describe.py +0 -0
  77. {interp_engine-1.13.0 → interp_engine-1.13.2}/interp_engine/directions.py +0 -0
  78. {interp_engine-1.13.0 → interp_engine-1.13.2}/interp_engine/dispatch.py +0 -0
  79. {interp_engine-1.13.0 → interp_engine-1.13.2}/interp_engine/facts.py +0 -0
  80. {interp_engine-1.13.0 → interp_engine-1.13.2}/interp_engine/hooks.py +0 -0
  81. {interp_engine-1.13.0 → interp_engine-1.13.2}/interp_engine/lens.py +0 -0
  82. {interp_engine-1.13.0 → interp_engine-1.13.2}/interp_engine/lens_stream.py +0 -0
  83. {interp_engine-1.13.0 → interp_engine-1.13.2}/interp_engine/lens_topk.py +0 -0
  84. {interp_engine-1.13.0 → interp_engine-1.13.2}/interp_engine/load.py +0 -0
  85. {interp_engine-1.13.0 → interp_engine-1.13.2}/interp_engine/mappers.py +0 -0
  86. {interp_engine-1.13.0 → interp_engine-1.13.2}/interp_engine/memory.py +0 -0
  87. {interp_engine-1.13.0 → interp_engine-1.13.2}/interp_engine/model.py +0 -0
  88. {interp_engine-1.13.0 → interp_engine-1.13.2}/interp_engine/moe_routing.py +0 -0
  89. {interp_engine-1.13.0 → interp_engine-1.13.2}/interp_engine/notebook_stdout.py +0 -0
  90. {interp_engine-1.13.0 → interp_engine-1.13.2}/interp_engine/oracle.py +0 -0
  91. {interp_engine-1.13.0 → interp_engine-1.13.2}/interp_engine/points.py +0 -0
  92. {interp_engine-1.13.0 → interp_engine-1.13.2}/interp_engine/protocol.py +0 -0
  93. {interp_engine-1.13.0 → interp_engine-1.13.2}/interp_engine/residual_basis.py +0 -0
  94. {interp_engine-1.13.0 → interp_engine-1.13.2}/interp_engine/sampling.py +0 -0
  95. {interp_engine-1.13.0 → interp_engine-1.13.2}/interp_engine/select.py +0 -0
  96. {interp_engine-1.13.0 → interp_engine-1.13.2}/interp_engine/server/__init__.py +0 -0
  97. {interp_engine-1.13.0 → interp_engine-1.13.2}/interp_engine/server/__main__.py +0 -0
  98. {interp_engine-1.13.0 → interp_engine-1.13.2}/interp_engine/server/app.py +0 -0
  99. {interp_engine-1.13.0 → interp_engine-1.13.2}/interp_engine/server/config.py +0 -0
  100. {interp_engine-1.13.0 → interp_engine-1.13.2}/interp_engine/server/lens.py +0 -0
  101. {interp_engine-1.13.0 → interp_engine-1.13.2}/interp_engine/server/runner.py +0 -0
  102. {interp_engine-1.13.0 → interp_engine-1.13.2}/interp_engine/server/schemas.py +0 -0
  103. {interp_engine-1.13.0 → interp_engine-1.13.2}/interp_engine/steer.py +0 -0
  104. {interp_engine-1.13.0 → interp_engine-1.13.2}/interp_engine/steer_specs.py +0 -0
  105. {interp_engine-1.13.0 → interp_engine-1.13.2}/interp_engine/sync.py +0 -0
  106. {interp_engine-1.13.0 → interp_engine-1.13.2}/interp_engine/tokenize.py +0 -0
  107. {interp_engine-1.13.0 → interp_engine-1.13.2}/interp_engine/vllm_backend.py +0 -0
  108. {interp_engine-1.13.0 → interp_engine-1.13.2}/interp_engine/vllm_capture/__init__.py +0 -0
  109. {interp_engine-1.13.0 → interp_engine-1.13.2}/interp_engine/vllm_capture/_hooks.py +0 -0
  110. {interp_engine-1.13.0 → interp_engine-1.13.2}/interp_engine/vllm_capture/_payload.py +0 -0
  111. {interp_engine-1.13.0 → interp_engine-1.13.2}/interp_engine/vllm_capture/_tp.py +0 -0
  112. {interp_engine-1.13.0 → interp_engine-1.13.2}/interp_engine/vllm_capture/_tree.py +0 -0
  113. {interp_engine-1.13.0 → interp_engine-1.13.2}/interp_engine/vllm_capture/attn.py +0 -0
  114. {interp_engine-1.13.0 → interp_engine-1.13.2}/interp_engine/vllm_capture/capture.py +0 -0
  115. {interp_engine-1.13.0 → interp_engine-1.13.2}/interp_engine/vllm_capture/graphs.py +0 -0
  116. {interp_engine-1.13.0 → interp_engine-1.13.2}/interp_engine/vllm_capture/lens/__init__.py +0 -0
  117. {interp_engine-1.13.0 → interp_engine-1.13.2}/interp_engine/vllm_capture/lens/intervene.py +0 -0
  118. {interp_engine-1.13.0 → interp_engine-1.13.2}/interp_engine/vllm_capture/lens/readout.py +0 -0
  119. {interp_engine-1.13.0 → interp_engine-1.13.2}/interp_engine/vllm_capture/lens/unembed.py +0 -0
  120. {interp_engine-1.13.0 → interp_engine-1.13.2}/interp_engine/vllm_capture/native.py +0 -0
  121. {interp_engine-1.13.0 → interp_engine-1.13.2}/interp_engine/vllm_capture/project.py +0 -0
  122. {interp_engine-1.13.0 → interp_engine-1.13.2}/interp_engine/vllm_capture/steering.py +0 -0
  123. {interp_engine-1.13.0 → interp_engine-1.13.2}/interp_engine/vllm_plugin.py +0 -0
  124. {interp_engine-1.13.0 → interp_engine-1.13.2}/tests/conftest.py +0 -0
  125. {interp_engine-1.13.0 → interp_engine-1.13.2}/tests/harness.py +0 -0
  126. {interp_engine-1.13.0 → interp_engine-1.13.2}/tests/model_expectations.yaml +0 -0
  127. {interp_engine-1.13.0 → interp_engine-1.13.2}/tests/synthetic_families.py +0 -0
  128. {interp_engine-1.13.0 → interp_engine-1.13.2}/tests/test_address.py +0 -0
  129. {interp_engine-1.13.0 → interp_engine-1.13.2}/tests/test_api_fixtures.py +0 -0
  130. {interp_engine-1.13.0 → interp_engine-1.13.2}/tests/test_api_generated.py +0 -0
  131. {interp_engine-1.13.0 → interp_engine-1.13.2}/tests/test_attn_config_tripwire.py +0 -0
  132. {interp_engine-1.13.0 → interp_engine-1.13.2}/tests/test_attn_probs_indexing.py +0 -0
  133. {interp_engine-1.13.0 → interp_engine-1.13.2}/tests/test_attn_scores.py +0 -0
  134. {interp_engine-1.13.0 → interp_engine-1.13.2}/tests/test_attn_z_gqa.py +0 -0
  135. {interp_engine-1.13.0 → interp_engine-1.13.2}/tests/test_autograd_support.py +0 -0
  136. {interp_engine-1.13.0 → interp_engine-1.13.2}/tests/test_bench_workloads.py +0 -0
  137. {interp_engine-1.13.0 → interp_engine-1.13.2}/tests/test_capability_refusals.py +0 -0
  138. {interp_engine-1.13.0 → interp_engine-1.13.2}/tests/test_capture_addressing.py +0 -0
  139. {interp_engine-1.13.0 → interp_engine-1.13.2}/tests/test_chat_compose.py +0 -0
  140. {interp_engine-1.13.0 → interp_engine-1.13.2}/tests/test_chat_formatters.py +0 -0
  141. {interp_engine-1.13.0 → interp_engine-1.13.2}/tests/test_chat_templates.py +0 -0
  142. {interp_engine-1.13.0 → interp_engine-1.13.2}/tests/test_core.py +0 -0
  143. {interp_engine-1.13.0 → interp_engine-1.13.2}/tests/test_cuda_preflight.py +0 -0
  144. {interp_engine-1.13.0 → interp_engine-1.13.2}/tests/test_describe.py +0 -0
  145. {interp_engine-1.13.0 → interp_engine-1.13.2}/tests/test_directions.py +0 -0
  146. {interp_engine-1.13.0 → interp_engine-1.13.2}/tests/test_doc_code_fences.py +0 -0
  147. {interp_engine-1.13.0 → interp_engine-1.13.2}/tests/test_eager_autograd.py +0 -0
  148. {interp_engine-1.13.0 → interp_engine-1.13.2}/tests/test_facts.py +0 -0
  149. {interp_engine-1.13.0 → interp_engine-1.13.2}/tests/test_family_points.py +0 -0
  150. {interp_engine-1.13.0 → interp_engine-1.13.2}/tests/test_gated_attn_out.py +0 -0
  151. {interp_engine-1.13.0 → interp_engine-1.13.2}/tests/test_gpu_sizer.py +0 -0
  152. {interp_engine-1.13.0 → interp_engine-1.13.2}/tests/test_head_contributions.py +0 -0
  153. {interp_engine-1.13.0 → interp_engine-1.13.2}/tests/test_hook_call_conventions.py +0 -0
  154. {interp_engine-1.13.0 → interp_engine-1.13.2}/tests/test_injected_system_spans.py +0 -0
  155. {interp_engine-1.13.0 → interp_engine-1.13.2}/tests/test_layer_kinds.py +0 -0
  156. {interp_engine-1.13.0 → interp_engine-1.13.2}/tests/test_lens_stream.py +0 -0
  157. {interp_engine-1.13.0 → interp_engine-1.13.2}/tests/test_lens_stream_pairing.py +0 -0
  158. {interp_engine-1.13.0 → interp_engine-1.13.2}/tests/test_load.py +0 -0
  159. {interp_engine-1.13.0 → interp_engine-1.13.2}/tests/test_logit_transform.py +0 -0
  160. {interp_engine-1.13.0 → interp_engine-1.13.2}/tests/test_mappers.py +0 -0
  161. {interp_engine-1.13.0 → interp_engine-1.13.2}/tests/test_memory.py +0 -0
  162. {interp_engine-1.13.0 → interp_engine-1.13.2}/tests/test_mlp_internals.py +0 -0
  163. {interp_engine-1.13.0 → interp_engine-1.13.2}/tests/test_model_expectations.py +0 -0
  164. {interp_engine-1.13.0 → interp_engine-1.13.2}/tests/test_moe.py +0 -0
  165. {interp_engine-1.13.0 → interp_engine-1.13.2}/tests/test_multigpu.py +0 -0
  166. {interp_engine-1.13.0 → interp_engine-1.13.2}/tests/test_multimodal_arch.py +0 -0
  167. {interp_engine-1.13.0 → interp_engine-1.13.2}/tests/test_new_models_gpu.py +0 -0
  168. {interp_engine-1.13.0 → interp_engine-1.13.2}/tests/test_no_chat_template.py +0 -0
  169. {interp_engine-1.13.0 → interp_engine-1.13.2}/tests/test_normalized_hook.py +0 -0
  170. {interp_engine-1.13.0 → interp_engine-1.13.2}/tests/test_notebook_stdout.py +0 -0
  171. {interp_engine-1.13.0 → interp_engine-1.13.2}/tests/test_oracle.py +0 -0
  172. {interp_engine-1.13.0 → interp_engine-1.13.2}/tests/test_packaging.py +0 -0
  173. {interp_engine-1.13.0 → interp_engine-1.13.2}/tests/test_padded_positions.py +0 -0
  174. {interp_engine-1.13.0 → interp_engine-1.13.2}/tests/test_parity_gpt2.py +0 -0
  175. {interp_engine-1.13.0 → interp_engine-1.13.2}/tests/test_per_layer_attn_dims.py +0 -0
  176. {interp_engine-1.13.0 → interp_engine-1.13.2}/tests/test_point_capability.py +0 -0
  177. {interp_engine-1.13.0 → interp_engine-1.13.2}/tests/test_points_registry.py +0 -0
  178. {interp_engine-1.13.0 → interp_engine-1.13.2}/tests/test_protocol.py +0 -0
  179. {interp_engine-1.13.0 → interp_engine-1.13.2}/tests/test_published_benchmarks.py +0 -0
  180. {interp_engine-1.13.0 → interp_engine-1.13.2}/tests/test_qk_norm.py +0 -0
  181. {interp_engine-1.13.0 → interp_engine-1.13.2}/tests/test_qkv_layout.py +0 -0
  182. {interp_engine-1.13.0 → interp_engine-1.13.2}/tests/test_reasoning_spans.py +0 -0
  183. {interp_engine-1.13.0 → interp_engine-1.13.2}/tests/test_release.py +0 -0
  184. {interp_engine-1.13.0 → interp_engine-1.13.2}/tests/test_resid_mid.py +0 -0
  185. {interp_engine-1.13.0 → interp_engine-1.13.2}/tests/test_residual_basis.py +0 -0
  186. {interp_engine-1.13.0 → interp_engine-1.13.2}/tests/test_sample_from_embeds.py +0 -0
  187. {interp_engine-1.13.0 → interp_engine-1.13.2}/tests/test_sampling.py +0 -0
  188. {interp_engine-1.13.0 → interp_engine-1.13.2}/tests/test_sandwich_norms.py +0 -0
  189. {interp_engine-1.13.0 → interp_engine-1.13.2}/tests/test_select.py +0 -0
  190. {interp_engine-1.13.0 → interp_engine-1.13.2}/tests/test_server.py +0 -0
  191. {interp_engine-1.13.0 → interp_engine-1.13.2}/tests/test_sliding_window_attn.py +0 -0
  192. {interp_engine-1.13.0 → interp_engine-1.13.2}/tests/test_small_models_gpu.py +0 -0
  193. {interp_engine-1.13.0 → interp_engine-1.13.2}/tests/test_static_dsv4_gpu.py +0 -0
  194. {interp_engine-1.13.0 → interp_engine-1.13.2}/tests/test_static_parity_gpu.py +0 -0
  195. {interp_engine-1.13.0 → interp_engine-1.13.2}/tests/test_static_set.py +0 -0
  196. {interp_engine-1.13.0 → interp_engine-1.13.2}/tests/test_static_warmup.py +0 -0
  197. {interp_engine-1.13.0 → interp_engine-1.13.2}/tests/test_steer_context.py +0 -0
  198. {interp_engine-1.13.0 → interp_engine-1.13.2}/tests/test_steer_ops_per_site_gpu.py +0 -0
  199. {interp_engine-1.13.0 → interp_engine-1.13.2}/tests/test_steer_reaches_generate.py +0 -0
  200. {interp_engine-1.13.0 → interp_engine-1.13.2}/tests/test_steer_scope.py +0 -0
  201. {interp_engine-1.13.0 → interp_engine-1.13.2}/tests/test_steer_specs.py +0 -0
  202. {interp_engine-1.13.0 → interp_engine-1.13.2}/tests/test_steer_stream_stack.py +0 -0
  203. {interp_engine-1.13.0 → interp_engine-1.13.2}/tests/test_sync_loop.py +0 -0
  204. {interp_engine-1.13.0 → interp_engine-1.13.2}/tests/test_sync_parity.py +0 -0
  205. {interp_engine-1.13.0 → interp_engine-1.13.2}/tests/test_unified_free_functions.py +0 -0
  206. {interp_engine-1.13.0 → interp_engine-1.13.2}/tests/test_unresolved_families.py +0 -0
  207. {interp_engine-1.13.0 → interp_engine-1.13.2}/tests/test_userless_prefix_spans.py +0 -0
  208. {interp_engine-1.13.0 → interp_engine-1.13.2}/tests/test_vllm_capture_gpu.py +0 -0
  209. {interp_engine-1.13.0 → interp_engine-1.13.2}/tests/test_vllm_capture_scales.py +0 -0
  210. {interp_engine-1.13.0 → interp_engine-1.13.2}/tests/test_vllm_engine_loop.py +0 -0
  211. {interp_engine-1.13.0 → interp_engine-1.13.2}/tests/test_vllm_graph_path.py +0 -0
  212. {interp_engine-1.13.0 → interp_engine-1.13.2}/tests/test_vllm_graphs_on_gpu.py +0 -0
  213. {interp_engine-1.13.0 → interp_engine-1.13.2}/tests/test_vllm_hook_availability.py +0 -0
  214. {interp_engine-1.13.0 → interp_engine-1.13.2}/tests/test_vllm_kv_isolation.py +0 -0
  215. {interp_engine-1.13.0 → interp_engine-1.13.2}/tests/test_vllm_new_points.py +0 -0
  216. {interp_engine-1.13.0 → interp_engine-1.13.2}/tests/test_vllm_only_families.py +0 -0
  217. {interp_engine-1.13.0 → interp_engine-1.13.2}/tests/test_vllm_padded_gpu.py +0 -0
  218. {interp_engine-1.13.0 → interp_engine-1.13.2}/tests/test_vllm_plugin.py +0 -0
  219. {interp_engine-1.13.0 → interp_engine-1.13.2}/tests/test_vllm_wire_grammar.py +0 -0
  220. {interp_engine-1.13.0 → interp_engine-1.13.2}/tests/test_vocabulary_boundary.py +0 -0
  221. {interp_engine-1.13.0 → interp_engine-1.13.2}/tests/test_worker_lens_capture_readout.py +0 -0
  222. {interp_engine-1.13.0 → interp_engine-1.13.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.13.0
3
+ Version: 1.13.2
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
@@ -119,7 +119,9 @@ index names the layer HF's does, which nothing checked before and which would fa
119
119
  than raise. It needs `interp-engine[vllm]`, so it self-skips elsewhere; note that running it via
120
120
  `.venv-vllm/bin/python` needs that directory on `PATH` too, because vLLM shells out to `ninja` to
121
121
  build a sampler kernel at startup. `tests/test_vllm_wire_grammar.py` covers the same process
122
- boundary on CPU, over a synthetic demux. `tests/test_multigpu.py` (`-m multigpu`, two CUDA cards)
122
+ boundary on CPU, over a synthetic demux. vLLM's Transformers backend hands each hook `[1, tokens, d]`;
123
+ `tests/test_vllm_batch_axis.py` pins how the demux reads that on CPU, and
124
+ `tests/test_vllm_transformers_backend_gpu.py` runs OLMo-2 on that backend against eager. `tests/test_multigpu.py` (`-m multigpu`, two CUDA cards)
123
125
  repeats the comparison at `num_gpus=2`: eager under accelerate's layer placement and vLLM under
124
126
  tensor parallelism, where `vllm_capture/_tp.py` gathers the head- and neuron-sharded points across
125
127
  ranks, against the one-card eager reference — captures, attention, steering, decode and the lens.
@@ -129,6 +131,16 @@ while the others' replies stay queued, and the next RPC consumes those — so a
129
131
  took every point of a TP=8 cell with it. `resolvable_points` and `resolvable_attn` are the ask-first
130
132
  calls; `tests/test_vllm_new_points.py` pins that they install nothing.
131
133
 
134
+ **Static writes inside a recorded graph.** vLLM's V2 runner records FULL decode graphs with plain
135
+ `torch.cuda.graph`, so a static write that Python decides at record time is replayed as "no write"
136
+ on every decode step. `vllm_capture/static_program.py` makes the write data instead: one Triton
137
+ kernel per write site reads which writer owns each row, and that writer's ops, from fixed-address
138
+ tables that the host refills between steps. `tests/test_static_program.py` checks the tables on CPU,
139
+ `tests/test_steer_math_parity.py` holds the kernel's single op form to the worker modifiers,
140
+ `tests/test_static_program_gpu.py` replays a graph recorded with nothing registered, and
141
+ `tests/test_static_decode_writes_gpu.py` requires static decode rows to match hooked vLLM for every
142
+ method, with and without `generated`.
143
+
132
144
  **The docs are parsed, not maintained.** The point table in
133
145
  [SUPPORTED_POINTS.md](SUPPORTED_POINTS.md) and the footnote markers in
134
146
  [ENGINE_HOOK_MAPPINGS.md](ENGINE_HOOK_MAPPINGS.md) are read back and compared against `points.py` by
@@ -9,6 +9,7 @@ refcounting -- state manipulation only, no hook bodies.
9
9
 
10
10
  from __future__ import annotations
11
11
 
12
+ from collections.abc import Callable
12
13
  from typing import Any, cast
13
14
 
14
15
  import torch
@@ -64,6 +65,10 @@ class _Demux:
64
65
  self.runner_api: str | None = None
65
66
  # Snapshot of the in-flight forward's row layout: (req_ids, seq_lens) or None.
66
67
  self.current_meta: tuple[list[str], list[int]] | None = None
68
+ # Per request in ``current_meta`` order: the absolute position of its first row, or None.
69
+ self.current_starts: list[int] | None = None
70
+ # Called after each snapshot, before the forward. Static writes fill their row map here.
71
+ self.on_meta: list[Callable[[], None]] = []
67
72
  # Diagnostics (see worker_demux_debug).
68
73
  self.dbg_exec_calls = 0
69
74
  self.dbg_last_meta: tuple[list[str], list[int]] | None = None
@@ -89,6 +94,9 @@ class _Demux:
89
94
  # Installed hooks: hook site -> [handle, refcount]. Keyed by site and not by address, so
90
95
  # two requests reading different streams of one point share a handle -- see `hook_site`.
91
96
  self.hooks: dict[Address, list] = {}
97
+ # Hook site -> whether its tensor arrives with a leading batch axis of one, as seen on the
98
+ # last forward of more than one token (see requests._batch_axis).
99
+ self.batch_axis: dict[Address, bool] = {}
92
100
  self.dev: Any = None
93
101
  self.dt: Any = None
94
102
 
@@ -183,6 +191,22 @@ def _meta_from_v1_inputs(input_batch: object, num_scheduled_tokens: object) -> t
183
191
  return _pair_meta(getattr(input_batch, "req_ids", None), num_scheduled_tokens)
184
192
 
185
193
 
194
+ def _starts(computed: object, meta: tuple[list[str], list[int]] | None) -> list[int] | None:
195
+ """Each request's first absolute position this step, from the runner's per-request computed-token count."""
196
+ if meta is None or computed is None:
197
+ return None
198
+ starts = [int(x) for x in cast(Any, computed)[: len(meta[0])]]
199
+ return starts if len(starts) == len(meta[0]) else None
200
+
201
+
202
+ def _run_on_meta(demux: _Demux) -> None:
203
+ for callback in demux.on_meta:
204
+ try:
205
+ callback()
206
+ except Exception as exc: # noqa: BLE001 - a callback must fail closed, not stop the forward
207
+ demux.dbg_last_error = f"on_meta {type(exc).__name__}: {exc}"
208
+
209
+
186
210
  def _ensure_patched(worker: object, demux: _Demux) -> None:
187
211
  """Patch the model runner once to snapshot each forward's row layout.
188
212
 
@@ -212,10 +236,12 @@ def _ensure_patched(worker: object, demux: _Demux) -> None:
212
236
  demux.dbg_exec_calls += 1
213
237
  try:
214
238
  demux.current_meta = _meta_from_input_batch(input_batch)
239
+ demux.current_starts = _starts(getattr(input_batch, "num_computed_tokens_np", None), demux.current_meta)
215
240
  demux.dbg_last_meta = demux.current_meta
216
241
  except Exception as exc: # noqa: BLE001 - never break the forward on metadata errors
217
- demux.current_meta = None
242
+ demux.current_meta = demux.current_starts = None
218
243
  demux.dbg_last_error = f"{type(exc).__name__}: {exc}"
244
+ _run_on_meta(demux)
219
245
  return input_batch
220
246
 
221
247
  model_runner.prepare_inputs = _patched
@@ -229,10 +255,14 @@ def _ensure_patched(worker: object, demux: _Demux) -> None:
229
255
  try:
230
256
  nst = args[0] if args else kwargs.get("num_scheduled_tokens")
231
257
  demux.current_meta = _meta_from_v1_inputs(model_runner.input_batch, nst)
258
+ demux.current_starts = _starts(
259
+ getattr(model_runner.input_batch, "num_computed_tokens_cpu", None), demux.current_meta
260
+ )
232
261
  demux.dbg_last_meta = demux.current_meta
233
262
  except Exception as exc: # noqa: BLE001 - never break the forward on metadata errors
234
- demux.current_meta = None
263
+ demux.current_meta = demux.current_starts = None
235
264
  demux.dbg_last_error = f"{type(exc).__name__}: {exc}"
265
+ _run_on_meta(demux)
236
266
  return out
237
267
 
238
268
  model_runner._prepare_inputs = _patched
@@ -648,9 +648,13 @@ class _MhcTaps:
648
648
  detectably different from no steer at all. So the rows the recorder actually changed take the
649
649
  re-run and every other row keeps the kernel's own output, which is also why a zero delta needs
650
650
  no re-run: no row changed.
651
+
652
+ Inside a graph capture the host cannot read ``changed``, so the re-run is always recorded and
653
+ the row mask alone decides at replay.
651
654
  """
652
655
  changed = (edited != stack).flatten(start_dim=1).any(dim=1)
653
- if not bool(changed.any()):
656
+ capturing = stack.is_cuda and torch.cuda.is_current_stream_capturing()
657
+ if not capturing and not bool(changed.any()):
654
658
  return fused
655
659
  bound = signature.bind(*args, **kwargs)
656
660
  bound.apply_defaults()
@@ -86,6 +86,32 @@ def _process_point(demux: _Demux, site: Address, full: torch.Tensor) -> torch.Te
86
86
  if meta is None:
87
87
  return full
88
88
  req_ids, seq_lens = meta
89
+ if _batch_axis(demux, site, full, sum(seq_lens)):
90
+ rows = full[0]
91
+ new = _process_rows(demux, site, rows, req_ids, seq_lens)
92
+ return full if new is rows else new.unsqueeze(0)
93
+ return _process_rows(demux, site, full, req_ids, seq_lens)
94
+
95
+
96
+ def _batch_axis(demux: _Demux, site: Address, full: torch.Tensor, tokens: int) -> bool:
97
+ """Whether ``full`` is ``[1, tokens, ...]`` rather than tokens first.
98
+
99
+ vLLM's Transformers backend calls the HF model with a batch axis, and so do some native families
100
+ at attention. For a forward of more than one token the shape decides it. For one token it does
101
+ not: ``[1, 1, d_model]`` with a batch axis has the shape of ``[1, 1, head_dim]`` on a model with one
102
+ KV head, or of one token on a hyper-connection trunk. So the site keeps the answer from its last
103
+ forward of more than one token, and a request's prefill always comes before its decode.
104
+ """
105
+ if full.dim() < 3 or full.shape[0] != 1:
106
+ return False
107
+ if tokens > 1:
108
+ demux.batch_axis[site] = full.shape[1] >= tokens
109
+ return demux.batch_axis.get(site, False)
110
+
111
+
112
+ def _process_rows(
113
+ demux: _Demux, site: Address, full: torch.Tensor, req_ids: list[str], seq_lens: list[int]
114
+ ) -> torch.Tensor:
89
115
  if full.dim() not in (2, 3) or full.shape[0] < sum(seq_lens):
90
116
  # Fewer rows than the layout expects (unexpected shape); skip rather than corrupt.
91
117
  # More rows is fine: trailing padding after sum(seq_lens) is ignored by the loop.
@@ -266,10 +292,8 @@ def _mk_value_hook(demux: _Demux, site: Address):
266
292
  (``_hooks.flat_value``), because a norm over ``head_dim`` is handed the per-head view.
267
293
 
268
294
  The narrowed slice goes through ``flat_value`` too, and not only for symmetry: where vLLM leaves a
269
- batch axis on the hidden state it hands the projection, the slice is ``[1, tokens, width]``, whose
270
- first axis is one rather than the token count. :func:`_process_point` reads that as a trunk with
271
- one row and returns the tensor untouched rather than steering rows it cannot line up -- so on those
272
- families this point was quietly serving nothing at all.
295
+ batch axis on the hidden state it hands the projection, the slice is ``[1, tokens, width]``, and
296
+ the packed projection's geometry is what tells that apart from a per-head view.
273
297
 
274
298
  Either way the adjustment happens *before* steering and is undone after it, so a steer on this
275
299
  point writes the value in the shape a capture of it reports, leaves the queries and keys of the same
@@ -50,6 +50,7 @@ from interp_engine.vllm_capture._tree import (
50
50
  resolve_capture_module,
51
51
  scale_capture,
52
52
  )
53
+ from interp_engine.vllm_capture.static_program import StaticWriteProgram
53
54
 
54
55
  #: Post-RoPE q/k/v at ``self_attn.attn``. Not a public capture point; ``capture_attention``
55
56
  #: harvests the three roles and recomputes scores/probs.
@@ -749,6 +750,9 @@ class StaticState:
749
750
  #: read-out or collect raises this rather than return a sequence with a hole in it.
750
751
  failed: dict[str, str] = field(default_factory=dict)
751
752
  patched_execute: bool = False
753
+ #: The write sites' device tables on a CUDA worker. When set, every write goes through it, so a
754
+ #: FULL graph replays each request's own write. None on CPU, where the Python path serves.
755
+ program: StaticWriteProgram | None = None
752
756
 
753
757
 
754
758
  def _state(worker: object) -> StaticState | None:
@@ -1156,7 +1160,11 @@ def worker_install_static(worker: object) -> None:
1156
1160
  if mhc_sites:
1157
1161
  _install_mhc_static(worker, mhc_sites)
1158
1162
 
1159
- _ensure_patched(worker, _get_demux(worker))
1163
+ demux = _get_demux(worker)
1164
+ _ensure_patched(worker, demux)
1165
+ if state.writes and device.type == "cuda":
1166
+ state.program = StaticWriteProgram(sites=list(state.writes.values()), device=device, max_n=max_n)
1167
+ demux.on_meta.append(lambda: _fill_program_rows(worker))
1160
1168
  _patch_execute_model(worker)
1161
1169
  logger.info(
1162
1170
  "interp-engine static: %d read site(s), %d write site(s), max_n=%d",
@@ -1166,6 +1174,25 @@ def worker_install_static(worker: object) -> None:
1166
1174
  )
1167
1175
 
1168
1176
 
1177
+ def _fill_program_rows(worker: object) -> None:
1178
+ """Point the next forward's rows at their writers. On any failure, no row is written."""
1179
+ static = _state(worker)
1180
+ program = static.program if static is not None else None
1181
+ if program is None:
1182
+ return
1183
+ demux = _get_demux(worker)
1184
+ meta = demux.current_meta
1185
+ try:
1186
+ if meta is None:
1187
+ program.fill_rows([], [], None, lambda rid: rid)
1188
+ else:
1189
+ program.fill_rows(meta[0], meta[1], demux.current_starts, lambda rid: _resolve_rid(demux, rid))
1190
+ except Exception:
1191
+ logger.exception("static writes: could not map this step's rows to their writers; writing none")
1192
+ program.fill_rows([], [], None, lambda rid: rid)
1193
+ raise
1194
+
1195
+
1169
1196
  def _install_mhc_static(worker: object, mhc_sites: Sequence[_Site]) -> None:
1170
1197
  """Wrap the mHC kernels for every stacked static site, refusing one this model cannot serve.
1171
1198
 
@@ -1198,9 +1225,14 @@ def _static_mhc_recorder(worker: object, site: _Site):
1198
1225
  if n == 0:
1199
1226
  return tensor
1200
1227
  static = _state(worker)
1201
- reqs = _write_reqs_for(static, site) if static is not None else []
1228
+ program = static.program if static is not None else None
1202
1229
  live = tensor
1203
- if reqs or site.modify is not None or site.delta_set:
1230
+ if program is not None and program.has_site(site):
1231
+ # A recorded graph must hold the edit whether or not anything is registered now.
1232
+ if torch.cuda.is_current_stream_capturing() or not program.idle(site):
1233
+ live = tensor.clone()
1234
+ program.launch(site, live, None, n)
1235
+ elif (static is not None and _write_reqs_for(static, site)) or site.modify is not None or site.delta_set:
1204
1236
  live = tensor.clone()
1205
1237
  _apply_write(live, None, site, n, fused=False, worker=worker)
1206
1238
  if site.buf is not None:
@@ -1637,6 +1669,12 @@ def _apply_write(
1637
1669
  if n <= 0:
1638
1670
  return
1639
1671
  static = _state(worker) if worker is not None else None
1672
+ program = static.program if static is not None else None
1673
+ if program is not None and program.has_site(site):
1674
+ # A recorded graph launches the kernel on every replay; an eager forward may skip an idle site.
1675
+ if torch.cuda.is_current_stream_capturing() or not program.idle(site):
1676
+ program.launch(site, hidden, residual if fused else None, n)
1677
+ return
1640
1678
  reqs = _write_reqs_for(static, site) if static is not None else []
1641
1679
  if reqs:
1642
1680
  _apply_demuxed_writes(hidden, residual, n, fused, worker, reqs)
@@ -1868,9 +1906,11 @@ def worker_set_static_delta(
1868
1906
  ) -> None:
1869
1907
  """Install static writes from worker specs. Zeros every write site first.
1870
1908
 
1871
- Additive ``op="additive"`` without a scope fills the static ``delta`` buffer. Every other op attaches
1872
- a live ``modify`` that reads the residual each forward (breakable ``add_eager``). ``lens_scope``
1873
- is the write's position scope: ``skip_positions``, ``prompt_len`` and ``steer_generated``.
1909
+ On a CUDA worker the ops go to :class:`~interp_engine.vllm_capture.static_program.StaticWriteProgram`
1910
+ as its global write. Without one, additive ``op="additive"`` without a scope fills the static
1911
+ ``delta`` buffer, and every other op attaches a live ``modify`` that reads the residual each
1912
+ forward. ``lens_scope`` is the write's position scope: ``skip_positions``, ``prompt_len`` and
1913
+ ``steer_generated``.
1874
1914
 
1875
1915
  A ``stream`` disqualifies the static buffer too. The buffer is added whole to a ``[tokens,
1876
1916
  streams, width]`` activation, so it has no way to say "this stream and not the others"; taking
@@ -1885,7 +1925,10 @@ def worker_set_static_delta(
1885
1925
  worker_clear_static_delta(worker)
1886
1926
  from interp_engine.vllm_capture.steering import _make_steer_modifiers
1887
1927
 
1888
- for site, group in _group_by_site(static, specs).items():
1928
+ groups = _group_by_site(static, specs)
1929
+ if static.program is not None:
1930
+ static.program.set_global(groups, lens_scope)
1931
+ for site, group in groups.items():
1889
1932
  assert site.delta is not None
1890
1933
  constant = _constant_delta(group, site.delta.device, site.delta.dtype) if lens_scope is None else None
1891
1934
  if constant is not None:
@@ -1969,8 +2012,11 @@ def worker_register_static_write(
1969
2012
  skip = tuple(int(i) for i in (lens_scope.get("skip_positions") or skip))
1970
2013
  length = int(lens_scope.get("prompt_len") or length)
1971
2014
  generated = bool(lens_scope.get("steer_generated", False))
2015
+ groups = _group_by_site(static, specs)
2016
+ if static.program is not None:
2017
+ static.program.register(req_id, groups, skip=skip, prompt_len=length, generated=generated)
1972
2018
  by_site: dict[str, _WriteReq] = {}
1973
- for site, group in _group_by_site(static, specs).items():
2019
+ for site, group in groups.items():
1974
2020
  by_site[format_address(site.address)] = _compile_write_req(
1975
2021
  group, site, skip_positions=skip, prompt_len=length, steer_generated=generated
1976
2022
  )
@@ -1984,6 +2030,8 @@ def worker_unregister_static_write(worker: object, req_id: str) -> None:
1984
2030
  if static is None:
1985
2031
  return
1986
2032
  static.write_reqs.pop(req_id, None)
2033
+ if static.program is not None:
2034
+ static.program.unregister(req_id)
1987
2035
  if req_id not in static.cap_points:
1988
2036
  static.registered.discard(req_id)
1989
2037
 
@@ -1992,6 +2040,8 @@ def worker_clear_static_delta(worker: object) -> None:
1992
2040
  static = _state(worker)
1993
2041
  if static is None:
1994
2042
  return
2043
+ if static.program is not None:
2044
+ static.program.clear_global()
1995
2045
  for site in static.writes.values():
1996
2046
  site.modify = None
1997
2047
  site.lens_scope = None