"""Pure no-op KV connector for measuring vLLM v1 framework overhead.

This connector implements every abstract hook of KVConnectorBase_V1 with
the cheapest possible no-op return. Loaded via:

  --kv-transfer-config '{
    "kv_connector_module_path":
        "microbench.connector_tax.tools.noop_connector:NoOpConnector",
    "kv_role": "kv_both"
  }'

It does:
  - no I/O
  - no per-step cache key walk
  - no per-layer save/load
  - no metadata serialization beyond an empty dataclass

So `tax(NoOpConnector) ≈ pure vLLM v1 framework overhead`.
"""

from typing import TYPE_CHECKING, Any

from vllm.distributed.kv_transfer.kv_connector.v1.base import (
    KVConnectorBase_V1,
    KVConnectorMetadata,
)

if TYPE_CHECKING:
    import torch
    from vllm.attention.backends.abstract import AttentionMetadata
    from vllm.forward_context import ForwardContext
    from vllm.v1.core.kv_cache_manager import KVCacheBlocks
    from vllm.v1.core.sched.output import SchedulerOutput
    from vllm.v1.request import Request


class NoOpConnector(KVConnectorBase_V1):
    """Empty connector — every hook is a no-op.

    Used as a control to isolate vLLM v1 framework dispatch cost
    (build_connector_meta walking SchedulerOutput, mixin hooks, etc.)
    from any specific connector implementation work (RDMA setup,
    per-layer save, hash table walks).
    """

    # ---- scheduler-side abstract methods ------------------------------
    def get_num_new_matched_tokens(
        self,
        request: "Request",
        num_computed_tokens: int,
    ) -> tuple[int | None, bool]:
        # Never advertises any external cache hits.
        return 0, False

    def update_state_after_alloc(
        self,
        request: "Request",
        blocks: "KVCacheBlocks",
        num_external_tokens: int,
    ) -> None:
        return None

    def build_connector_meta(
        self,
        scheduler_output: "SchedulerOutput",
    ) -> KVConnectorMetadata:
        return KVConnectorMetadata()

    # ---- worker-side abstract methods ---------------------------------
    def start_load_kv(
        self,
        forward_context: "ForwardContext",
        **kwargs: Any,
    ) -> None:
        return None

    def wait_for_layer_load(self, layer_name: str) -> None:
        return None

    def save_kv_layer(
        self,
        layer_name: str,
        kv_layer: "torch.Tensor",
        attn_metadata: "AttentionMetadata",
        **kwargs: Any,
    ) -> None:
        return None

    def wait_for_save(self) -> None:
        return None
