Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions adapters/vllm_omni/kb-calibration/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# vllm-omni kb-gate calibration set

`infermatrix-copilot kb calibrate --repo vllm-omni` runs the pinned judge over
these cases exactly as the knowledge gate does. The repository may leave
shadow mode only when every `reject` case is caught and at most 20 % of the
`pass` cases are rejected; re-run it whenever the judge model or version
changes.

- `good-*`: the eight human-approved rules of InferMatrixCopilot #202, each
replayed as an `add` against its page as it was before the merge.
- `bad-*`: mutations the gate must reject (reversed claim, optional enforcement,
restated PR description, contradiction of BENCH-1a, unjustified retirement,
an edit that changes meaning, an invented API) and the historical bot-written
MMH3-4e that contradicted MMH3-4a (folded by a human in 03b0b62a2).

Evidence is the real upstream PR (title, body, bounded diff excerpt).

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Remove persisted raw PR evidence from calibration fixtures

The calibration cases check full upstream PR bodies and diff excerpts into the adapter tree, even though the repository contract requires raw PR material to remain temporary and outside the repository. Replace these replay fixtures with compliant evaluation artifacts or externally supplied evidence rather than shipping the raw PR payloads in every installation.

AGENTS.md reference: AGENTS.md:L12-L17

Useful? React with 👍 / 👎.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

23 changes: 23 additions & 0 deletions adapters/vllm_omni/kb-calibration/cases/bad-invented-api.json

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

23 changes: 23 additions & 0 deletions adapters/vllm_omni/kb-calibration/cases/good-BENCH-1h.json

Large diffs are not rendered by default.

27 changes: 27 additions & 0 deletions adapters/vllm_omni/kb-calibration/cases/good-COSMOS-7a.json

Large diffs are not rendered by default.

23 changes: 23 additions & 0 deletions adapters/vllm_omni/kb-calibration/cases/good-DIFF-1ak.json

Large diffs are not rendered by default.

25 changes: 25 additions & 0 deletions adapters/vllm_omni/kb-calibration/cases/good-DIFF-2ag4.json

Large diffs are not rendered by default.

27 changes: 27 additions & 0 deletions adapters/vllm_omni/kb-calibration/cases/good-DIST-1m.json

Large diffs are not rendered by default.

23 changes: 23 additions & 0 deletions adapters/vllm_omni/kb-calibration/cases/good-QOMNI-1i.json

Large diffs are not rendered by default.

23 changes: 23 additions & 0 deletions adapters/vllm_omni/kb-calibration/cases/good-SCHED-1c.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"id": "good-SCHED-1c",
"expected": "pass",
"note": "human-approved rule from #202 (repos/vllm-omni/components/scheduler/rules-shared-lifecycle.md)",
"base": {
"repos/vllm-omni/components/scheduler/rules-shared-lifecycle.md": "---\ntitle: \"Scheduler shared lifecycle 规则\"\ncreated: 2026-09-22\nupdated: 2026-09-22\ntype: rule\ntags: [vllm-omni, components, scheduler]\nsources: [\"PR #5461\"]\n---\n\n# Scheduler shared lifecycle 规则\n\n## SCHED-6a2 — AR 与 generation 的共享生命周期只能有一个 canonical 实现\n\n- 触发:把 AR/generation scheduler 的 I/O、输出包装、finish 或统计逻辑移入\n `OmniSchedulerMixin`,或修改该 mixin 及任一 scheduler 的 shared hook。\n- 强制:两类 scheduler 共用 pending connector/chunk 输入处理、wait queue restore、\n `OmniSchedulerOutput` 包装、失败 KV load 的 terminal output、finished IDs、队列清理、\n stats/events 和 finish cleanup;`NewRequestData` 转成 Omni 结构时逐字段无损,并保留已经\n 是 Omni 类型的 fast path。AR 的 synthetic-abort 等差异策略必须作为显式参数留在调用点。\n- 禁止:把共享生命周期复制回两个 subclass;重建 `NewRequestData` 时只挑当前已知字段;\n 在 mixin 内按 scheduler 类型隐式猜差异策略;失败 KV load 只改状态而不发 terminal output。\n- 验收:同一组 contract 测试分别驱动 AR 与 generation 输入路径;断言 base dataclass 的\n 每个字段均被转交、现有 Omni entry 保持 identity、失败 KV load 终止输出、finished ID/\n stats/events/cleanup 一致,并单独验证显式 abort policy。 ^[PR #5461]\n"
},
"head": {
"repos/vllm-omni/components/scheduler/rules-shared-lifecycle.md": "---\ntitle: \"Scheduler shared lifecycle 规则\"\ncreated: 2026-09-22\nupdated: 2026-09-26\ntype: rule\ntags: [vllm-omni, components, scheduler]\nsources: [\"PR #5461\", \"PR #7877\"]\n---\n\n# Scheduler shared lifecycle 规则\n\n## SCHED-6a2 — AR 与 generation 的共享生命周期只能有一个 canonical 实现\n\n- 触发:把 AR/generation scheduler 的 I/O、输出包装、finish 或统计逻辑移入\n `OmniSchedulerMixin`,或修改该 mixin 及任一 scheduler 的 shared hook。\n- 强制:两类 scheduler 共用 pending connector/chunk 输入处理、wait queue restore、\n `OmniSchedulerOutput` 包装、失败 KV load 的 terminal output、finished IDs、队列清理、\n stats/events 和 finish cleanup;`NewRequestData` 转成 Omni 结构时逐字段无损,并保留已经\n 是 Omni 类型的 fast path。AR 的 synthetic-abort 等差异策略必须作为显式参数留在调用点。\n- 禁止:把共享生命周期复制回两个 subclass;重建 `NewRequestData` 时只挑当前已知字段;\n 在 mixin 内按 scheduler 类型隐式猜差异策略;失败 KV load 只改状态而不发 terminal output。\n- 验收:同一组 contract 测试分别驱动 AR 与 generation 输入路径;断言 base dataclass 的\n 每个字段均被转交、现有 Omni entry 保持 identity、失败 KV load 终止输出、finished ID/\n stats/events/cleanup 一致,并单独验证显式 abort policy。 ^[PR #5461]\n\n## SCHED-1c — 同 step 的 prefix-hit prefetch 必须在 publish 后按新 version 重规划\n\n- 触发:修改 `OmniTensorPrefixCache` 的 same-step hit prefetch、`_hit_prefetch` / `_prefetch_queue`,或 `allocate_slots` 后同一次 `schedule()` 内后到请求命中先到请求本步才写入的 block。\n- 强制:B 在 `new_step_starts` 规划的 hit 可能读到 ABSENT mm 行,或回收块上上一 tenant 的 COMMITTED 行。A 在本步 `save_outputs` / `_publish_saved_step` 登记 write 后,必须丢掉 `reserved_version` 已不再匹配的 prefetch future(旧 fetch 可跑完但须 unregister pending read),再让 `_prefetch_hit_spans` 对现已 `IN_TRANSIT` 的行重规划。COW 保住旧 tenant 时,该 hit 也不得继续用旧 future。\n- 禁止:只因 `_hit_prefetch` 已有 future 就跳过 re-plan;把提前读到的 pool 行当成 B 的 merged prefix。\n- 验收:覆盖 fresh/reused block 与 immediate/deferred mm;断言 B 物化的 prefix 等于本步 A 刚写入的 hidden/mm,而不是 prefetch 当时的旧/空行。^[PR #7877]\n\n<!-- kb:rule status=active since=v0.30.0 -->\n"
},
"evidence": [
{
"source_reference": "PR #7877",
"title": "[Hotfix][Core] PrefixCache: re-plan same-step prefix hits whose rows are written after the early prefetch",
"body": "\r\n## Purpose\r\n\r\nvLLM caches A's block hashes at `allocate_slots`, so a request B admitted in the same `schedule()` call hits blocks A only writes this step. The manager plans B's hit span at `new_step_starts`, before A's rows are registered: `_slot_ref` accepts ABSENT rows for mm keys (and for recycled blocks the previous tenant's COMMITTED rows for every key), the prefetch reads the pool, and publish never re-plans because a future already exists. B's merged prefix rows are silently wrong. Reachable with n>1, concurrent identical prompts, cold-start bursts, and chunked prefill of a shared prefix.\r\n\r\nModification:\r\n\r\nAt publish, drop prefetch futures whose `reserved_version` no longer matches after this step's writes are mapped, and let `_prefetch_hit_spans` re-plan them against the now IN_TRANSIT rows.\r\n\r\ncc @amy-why-3459 \r\n\r\n## Test Plan\r\n\r\n**vLLM Version:** v0.29.0\r\n\r\n**vLLM-Omni Commit:** main @ 470ec6084 (base), fix 7bafe243f\r\n\r\n```\r\npytest tests/core/test_prefix_cache.py -k \"same_step_hit\" -m \"core_model and cpu\"\r\n```\r\n\r\n## Test Result\r\n\r\n```\r\n$ pytest tests/core/test_prefix_cache.py -k same_step_hit --confcutdir=tests/core -q\r\n6 passed, 84 deselected in 0.76s\r\n\r\n$ pytest tests/core/test_prefix_cache.py tests/core/test_prefix_cache_runner_mixin.py \\\r\n tests/platforms/npu/test_npu_prefix_cache_wiring.py --confcutdir=tests/core -q\r\n98 passed, 1 skipped in 1.70s\r\n```\r\n\r\n**BEFORE SUBMITTING:** read [CONTRIBUTING.md](https://github.com/vllm-project/vllm-omni/blob/main/CONTRIBUTING.md) and run the [precheck-pr skill](https://github.com/vllm-project/vllm-omni/blob/main/.claude/skills/precheck-pr/SKILL.md) with the code agent for a self-check against project conventions.\r\n(anything written below this line will be removed by GitHub Actions)\r\n",
"changed_files": [
"tests/core/test_prefix_cache.py",
"vllm_omni/core/prefix_cache/manager.py"
],
"diff_excerpt": "--- tests/core/test_prefix_cache.py\n@@ -1363,6 +1363,43 @@ def test_same_step_hit_prefetch_starts_at_save(caplog):\n assert torch.equal(outs.hidden_states[\"b\"][:8], expected_rows(view.slots_for(\"b\", 0, 8)))\n \n \n+@pytest.mark.parametrize(\"reuse_blocks\", [False, True], ids=[\"fresh\", \"reused\"])\n+@pytest.mark.parametrize(\"deferred_mm\", [False, True], ids=[\"immediate\", \"deferred\"])\n+def test_same_step_hit_refreshes_prefetch_after_write(reuse_blocks, deferred_mm):\n+ policy = ModelCachePolicy(deferred_keys=frozenset({\"mm\"}) if deferred_mm else frozenset())\n+ mgr, view = make_manager(policy=policy)\n+ try:\n+ blocks = [0, 1] if reuse_blocks else [8, 9]\n+ sid = run_step(mgr, view, {\"old\": (blocks, 0, 8)}, mm={\"mm\": torch.full((8, 2), 100.0)})\n+ mgr.materialize(sid, [\"old\"])\n+ mgr.new_step_starts(FakeSchedOut(finished=[\"old\"]))\n+\n+ mgr.new_step_starts(\n+ FakeSchedOut(\n+ new_reqs=[FakeNewReq(\"a\", 0, [[0, 1]]), FakeNewReq(\"b\", 8, [[0, 1, 2]])],\n+ num_scheduled={\"a\": 8, \"b\": 4},\n+ )\n+ )\n+ old_prefetch = dict(mgr._hit_prefetch[\"b\"])\n+ # Complete the early reads before A publishes the prefix B actually hits.\n+ for future in old_prefetch.values():\n+ future.result(timeout=5)\n+\n+ view.order = [\"a\", \"b\"]\n+ view.req_blocks.update(a=[0, 1], b=[0, 1, 2])\n+ view.computed.update(a=0, b=8)\n+ hidden = torch.cat([torch.full((8, HIDDEN), 20.0), torch.full((4, HIDDEN), 30.0)])\n+ mm = torch.cat([torch.full((8, 2), 200.0), torch.full((4, 2), 300.0)])\n+ sid = mgr.save_outputs(hidden, {\"mm\": mm}, num_tokens_unpadded=12, num_tokens_padded=12)\n+ outs = mgr.materialize(sid, [\"a\", \"b\"])\n+\n+ assert torch.equal(outs.hidden_states[\"b\"][:8], hidden[:8])\n+ assert torch.equal(outs.hidden_states[\"b\"][8:], hidden[8:])\n+ assert torch.equal(outs.mm_outputs[\"mm\"][\"b\"], mm)\n+ finally:\n+ mgr.shutdown()\n+\n+\n def test_delayed_read_of_reassigned_hit_raises():\n \"\"\"A version check with no COW copy still raises for live and finished.\n The production path registers the ref first so remap preserves the rows\n--- vllm_omni/core/prefix_cache/manager.py\n@@ -789,6 +789,19 @@ def _clear_hit_infos(self) -> None:\n self._hit_spans.clear()\n self._hit_prefetch.clear()\n \n+ def _invalidate_overwritten_prefetches(self) -> None:\n+ \"\"\"Caller holds ``_state_lock``. Refresh this step's hits whose\n+ intended producer registered a write after the early prefetch.\"\"\"\n+ for future, src in self._prefetch_queue:\n+ futs = self._hit_prefetch.get(src.req_id)\n+ if futs is None or futs.get(src.key) is not future:\n+ continue\n+ versions = self._slot_status.get_slot_status(src.key).slot_version[src.slots]\n+ if src.reserved_version is None or not torch.equal(versions, src.reserved_version):\n+ # Let the old fetch finish and unregister its pending read.\n+ # COW may have preserved the prior tenant, which this hit must not use.\n+ del futs[src.key]\n+\n def _prefetch_hit_spans(self) -> None:\n \"\"\"Caller holds ``_state_lock``. Plan each hit span not yet planned\n and gather it on the prefetch thread.\n@@ -917,6 +930,7 @@ def _publish_saved_step(\n if self._hit_spans:\n # Same-step hits: their rows are registered now (IN_TRANSIT on\n # this step's tasks); start the gather before the next step.\n+ self._invalidate_overwritten_prefetches()\n self._prefetch_hit_spans()\n step_id = self._next_step_id\n self._next_step_id += 1\n"
}
]
}
Loading
Loading